Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Created February 20, 2026 13:20
Show Gist options
  • Select an option

  • Save sunmeat/37cabef9b4e22d965e9f661d26aef77f to your computer and use it in GitHub Desktop.

Select an option

Save sunmeat/37cabef9b4e22d965e9f661d26aef77f to your computer and use it in GitHub Desktop.
MVT (flask)
from flask import Flask, request, redirect, url_for, render_template_string
app = Flask(__name__)
# ────────────────────────────────────────────────
# model — дані та бізнес-логіка
# ────────────────────────────────────────────────
class ShoppingModel:
# ініціалізація
def __init__(self):
self.items = []
# додавання товару
def add_item(self, name, qty, price):
self.items.append({
"name": name,
"quantity": qty,
"price": price
})
# очищення списку
def clear(self):
self.items.clear()
# отримання копії
def get_items(self):
return self.items.copy()
# підрахунок суми
def total(self):
return sum(i["quantity"] * i["price"] for i in self.items)
model = ShoppingModel()
# ────────────────────────────────────────────────
# template — html шаблон
# ────────────────────────────────────────────────
HTML_TEMPLATE = """
<!DOCTYPE html>
<html lang="uk">
<head>
<meta charset="UTF-8">
<title>список покупок</title>
<style>
* {
box-sizing: border-box;
font-family: "Segoe UI", Roboto, sans-serif;
}
body {
margin: 0;
min-height: 100vh;
background: linear-gradient(135deg, #0f1022, #1b1d40 60%, #2b2f7a);
color: #f1f2ff;
display: flex;
align-items: center;
justify-content: center;
}
.card {
width: 760px;
background: rgba(255,255,255,0.06);
backdrop-filter: blur(18px);
border-radius: 20px;
padding: 28px;
box-shadow: 0 20px 60px rgba(0,0,0,0.6);
}
h1 {
text-align: center;
margin-top: 0;
font-weight: 600;
letter-spacing: 1px;
}
.form-row {
display: grid;
grid-template-columns: 1fr 120px 140px auto;
gap: 10px;
margin-bottom: 12px;
}
input {
padding: 12px;
border-radius: 10px;
border: none;
background: rgba(0,0,0,0.35);
color: #fff;
font-size: 15px;
outline: none;
transition: 0.2s;
}
input:focus {
background: rgba(0,0,0,0.55);
box-shadow: 0 0 0 2px #7c82ff;
}
button {
border: none;
border-radius: 10px;
padding: 12px 16px;
font-weight: 600;
cursor: pointer;
transition: 0.2s;
white-space: nowrap;
}
.btn-add {
background: linear-gradient(135deg, #6b73ff, #000dff);
color: white;
}
.btn-add:hover {
transform: translateY(-1px);
box-shadow: 0 6px 18px rgba(0,0,0,0.5);
}
.btn-clear {
background: rgba(255,255,255,0.12);
color: #fff;
margin-top: 8px;
width: 100%;
}
.btn-clear:hover {
background: rgba(255,255,255,0.22);
}
.list {
margin-top: 18px;
background: rgba(0,0,0,0.35);
border-radius: 14px;
padding: 16px;
}
table {
width: 100%;
border-collapse: collapse;
}
th {
text-align: left;
font-weight: 500;
padding-bottom: 8px;
opacity: 0.8;
}
td {
padding: 8px 0;
border-bottom: 1px solid rgba(255,255,255,0.08);
}
.sum {
text-align: right;
font-variant-numeric: tabular-nums;
}
.empty {
text-align: center;
opacity: 0.7;
padding: 30px 0;
}
.total {
text-align: right;
font-size: 18px;
font-weight: 600;
margin-top: 12px;
}
</style>
</head>
<body>
<div class="card">
<h1>список покупок</h1>
<form method="post" action="/add">
<div class="form-row">
<input name="name" placeholder="назва товару" required>
<input name="qty" placeholder="кількість" required>
<input name="price" placeholder="ціна" required>
<button class="btn-add" type="submit">додати</button>
</div>
</form>
<form method="post" action="/clear">
<button class="btn-clear" type="submit">очистити список</button>
</form>
<div class="list">
{% if not items %}
<div class="empty">список порожній</div>
{% else %}
<table>
<tr>
<th>#</th>
<th>товар</th>
<th>к-сть</th>
<th>ціна</th>
<th class="sum">сума</th>
</tr>
{% for i, item in items %}
<tr>
<td>{{ i }}</td>
<td>{{ item.name }}</td>
<td>{{ item.quantity }}</td>
<td>{{ "%.2f"|format(item.price) }} ₴</td>
<td class="sum">
{{ "%.2f"|format(item.quantity * item.price) }} ₴
</td>
</tr>
{% endfor %}
</table>
<div class="total">
разом: {{ "%.2f"|format(total) }} ₴
</div>
{% endif %}
</div>
</div>
</body>
</html>
"""
# ────────────────────────────────────────────────
# view — маршрути flask
# ────────────────────────────────────────────────
# головна сторінка
@app.route("/")
def index():
items = [
(i + 1, item)
for i, item in enumerate(model.get_items())
]
return render_template_string(
HTML_TEMPLATE,
items=items,
total=model.total()
)
# додавання товару
@app.route("/add", methods=["POST"])
def add_item():
name = request.form.get("name", "").strip()
qty_str = request.form.get("qty", "").strip()
price_str = request.form.get("price", "").strip()
if not name or not qty_str or not price_str:
return redirect(url_for("index"))
try:
qty = int(qty_str)
price = float(price_str)
except ValueError:
return redirect(url_for("index"))
if qty < 1 or price <= 0:
return redirect(url_for("index"))
model.add_item(name, qty, price)
return redirect(url_for("index"))
# очищення списку
@app.route("/clear", methods=["POST"])
def clear():
model.clear()
return redirect(url_for("index"))
# ────────────────────────────────────────────────
# запуск
# ────────────────────────────────────────────────
if __name__ == "__main__":
app.run(debug=True)
# запустити в браузері: http://127.0.0.1:5000/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment