- CREATE TABLE IF NOT EXISTS дополнена колонками:
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
created_by TEXT NOT NULL DEFAULT ''
- ALTER TABLE ... ADD COLUMN IF NOT EXISTS досыпает колонки в уже существующую
таблицу — данные при этом не теряются;
- INSERT пишет created_by из env SERVICE_NAME (задаётся в json_env манифеста),
created_at ставит сама СУБД;
- список записей и UI показывают обе колонки.
Зачем: три приложения (flask / lucee / nodejs) пишут в ОДНУ таблицу — теперь
сразу видно, какое из них и когда добавило строку.
142 lines
4.9 KiB
Python
142 lines
4.9 KiB
Python
"""
|
|
app.py — Flask CRUD-интерфейс для PostgreSQL.
|
|
|
|
Переменные окружения (из json_env):
|
|
- PGHOST, PGPORT, PGDATABASE, PGUSER, PGPASSWORD — подключение к БД
|
|
- TABLE_NAME — имя таблицы (по умолчанию crud_items)
|
|
|
|
При старте: CREATE TABLE IF NOT EXISTS (БЕЗ DROP).
|
|
"""
|
|
|
|
import os
|
|
import psycopg2
|
|
import psycopg2.extras
|
|
from flask import Flask, render_template, request, redirect, url_for, g
|
|
|
|
VERSION = "1.0.0"
|
|
TABLE_NAME = os.environ.get("TABLE_NAME", "crud_items")
|
|
# SERVICE_NAME — какое приложение добавило строку (колонка created_by).
|
|
# Задаётся в json_env манифеста отдельно для каждого сервиса (flask / lucee / nodejs),
|
|
# чтобы три приложения, пишущие в ОДНУ таблицу, были различимы.
|
|
SERVICE_NAME = os.environ.get("SERVICE_NAME", "flask")
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
def get_db():
|
|
"""Ленивое подключение к PostgreSQL. Один коннект на запрос."""
|
|
if "db" not in g:
|
|
g.db = psycopg2.connect(
|
|
host=os.environ.get("PGHOST", "127.0.0.1"),
|
|
port=os.environ.get("PGPORT", "5432"),
|
|
dbname=os.environ.get("PGDATABASE", "postgres"),
|
|
user=os.environ.get("PGUSER", "postgres"),
|
|
password=os.environ.get("PGPASSWORD", ""),
|
|
)
|
|
g.db.autocommit = True
|
|
return g.db
|
|
|
|
|
|
def init_db():
|
|
"""Создать таблицу, если её нет, и досыпать новые колонки.
|
|
|
|
Таблица выставочная — кроме id/value храним только две метки:
|
|
created_at — когда строка добавлена (ставит сама СУБД);
|
|
created_by — какое приложение её добавило (flask / lucee / nodejs).
|
|
|
|
ALTER TABLE ... ADD COLUMN IF NOT EXISTS нужен, чтобы таблица, созданная
|
|
предыдущей версией приложения, получила новые колонки без потери данных.
|
|
"""
|
|
db = get_db()
|
|
cur = db.cursor()
|
|
cur.execute(
|
|
f"""
|
|
CREATE TABLE IF NOT EXISTS {TABLE_NAME} (
|
|
id SERIAL PRIMARY KEY,
|
|
value TEXT NOT NULL DEFAULT '',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
created_by TEXT NOT NULL DEFAULT ''
|
|
)
|
|
"""
|
|
)
|
|
cur.execute(
|
|
f"ALTER TABLE {TABLE_NAME} ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ NOT NULL DEFAULT now()"
|
|
)
|
|
cur.execute(
|
|
f"ALTER TABLE {TABLE_NAME} ADD COLUMN IF NOT EXISTS created_by TEXT NOT NULL DEFAULT ''"
|
|
)
|
|
cur.close()
|
|
|
|
|
|
@app.teardown_appcontext
|
|
def close_db(exc):
|
|
db = g.pop("db", None)
|
|
if db is not None:
|
|
db.close()
|
|
|
|
|
|
@app.route("/", methods=["GET", "POST"])
|
|
def index():
|
|
db = get_db()
|
|
cur = db.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
|
|
|
|
# --- Обработка POST (create / update / delete) — POST-redirect-GET ---
|
|
if request.method == "POST":
|
|
action = request.form.get("action", "")
|
|
|
|
if action == "create":
|
|
# created_at ставит СУБД (DEFAULT now()), created_by — имя этого сервиса
|
|
cur.execute(
|
|
f"INSERT INTO {TABLE_NAME} (value, created_by) VALUES (%s, %s)",
|
|
(request.form.get("value", ""), SERVICE_NAME),
|
|
)
|
|
|
|
elif action == "update":
|
|
cur.execute(
|
|
f"UPDATE {TABLE_NAME} SET value = %s WHERE id = %s",
|
|
(request.form.get("value", ""), int(request.form.get("id", 0))),
|
|
)
|
|
|
|
elif action == "delete":
|
|
cur.execute(
|
|
f"DELETE FROM {TABLE_NAME} WHERE id = %s",
|
|
(int(request.form.get("id", 0)),),
|
|
)
|
|
|
|
cur.close()
|
|
return redirect(url_for("index"))
|
|
|
|
# --- GET: список + форма редактирования ---
|
|
edit_row = None
|
|
edit_id = request.args.get("edit")
|
|
if edit_id:
|
|
cur.execute(
|
|
f"SELECT id, value, created_at, created_by FROM {TABLE_NAME} WHERE id = %s",
|
|
(int(edit_id),),
|
|
)
|
|
row = cur.fetchone()
|
|
if row:
|
|
edit_row = dict(row)
|
|
|
|
# Список всех записей — вместе с метками «когда» и «кем» добавлено
|
|
cur.execute(f"SELECT id, value, created_at, created_by FROM {TABLE_NAME} ORDER BY id")
|
|
rows = cur.fetchall()
|
|
cur.close()
|
|
|
|
return render_template(
|
|
"index.html",
|
|
table_name=TABLE_NAME,
|
|
rows=rows,
|
|
edit_row=edit_row,
|
|
version=VERSION,
|
|
)
|
|
|
|
|
|
# --- Инициализация БД при старте (до первого запроса) ---
|
|
with app.app_context():
|
|
init_db()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000)), debug=False)
|