From 3ba6099976111202b3972c88d19201069a6fb4ea Mon Sep 17 00:00:00 2001 From: Jurn Wubben Date: Mon, 19 May 2025 22:47:45 +0200 Subject: [PATCH] Implemented basic models --- .envrc | 1 + .gitignore | 1 + app/__init__.py | 7 +- app/__pycache__/__init__.cpython-312.pyc | Bin 505 -> 0 bytes app/__pycache__/configuration.cpython-312.pyc | Bin 929 -> 0 bytes app/__pycache__/models.cpython-312.pyc | Bin 1819 -> 0 bytes app/__pycache__/views.cpython-312.pyc | Bin 504 -> 0 bytes app/configuration.py | 4 +- app/forms.py | 30 +++++ app/models.py | 41 ++++--- app/templates/edit.html | 69 ++++++++++++ app/templates/new.html | 11 ++ app/templates/view.html | 10 ++ app/views.py | 105 +++++++++++++++++- flake.nix | 2 +- instance/application.db | Bin 0 -> 12288 bytes 16 files changed, 257 insertions(+), 24 deletions(-) delete mode 100644 app/__pycache__/__init__.cpython-312.pyc delete mode 100644 app/__pycache__/configuration.cpython-312.pyc delete mode 100644 app/__pycache__/models.cpython-312.pyc delete mode 100644 app/__pycache__/views.cpython-312.pyc create mode 100644 app/forms.py create mode 100644 app/templates/edit.html create mode 100644 app/templates/new.html create mode 100644 app/templates/view.html create mode 100644 instance/application.db diff --git a/.envrc b/.envrc index 3550a30..4d3e59f 100644 --- a/.envrc +++ b/.envrc @@ -1 +1,2 @@ use flake +export FLASK_APP=app \ No newline at end of file diff --git a/.gitignore b/.gitignore index 92b2793..a4b73fe 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .direnv +__pycache__ diff --git a/app/__init__.py b/app/__init__.py index 3fa7218..42a5f46 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -2,12 +2,9 @@ from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) - -# Configuration of application, see configuration.py, choose one and uncomment. -# app.config.from_object('configuration.ProductionConfig') app.config.from_object("app.configuration.DevelopmentConfig") -# app.config.from_object('configuration.TestingConfig') - db = SQLAlchemy(app) # flask-sqlalchemy from app import views, models +with app.app_context(): + db.create_all() \ No newline at end of file diff --git a/app/__pycache__/__init__.cpython-312.pyc b/app/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index e5a887e9340f52e474f868862dfaf93e047f8eb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 505 zcmX@j%ge>Uz`&sJM=N6yBLl-@5C?{tp^VQ>3=9m@8B!Qh7;_kM8KW2(L2RZRrd;MI zW<~}khE&E>meq_9)lsaetXVt|HZqyTkIaLxQV z5gP*o!%BwFAh-T<)X&JzP1VmTF4uR-Pc8+yq*xzphpuUvZfa&;N$C21b^4u12ow3}P1<#4d>2Tw$>N$i~1XFoC%_zJcjEgX{$c*&Uz`$_(w^oJ=69dCz5C?`?p^VRN3=9m@8JHN{8B!Qp7*ZHh8CEkx*?p==kbiz7ZhFEKYYKK>S0e0*+xN@)&= z%@ZGASelpvm0@*pbqaOA#UA1s9OCKceoH(!(8tlo*~8U0GTy~8#L>wy*fl;h$n%z{ zlfQpRa7d72K)jp3Ux;J4Yp}ns>n$#beevF|k+*o9gM-}SUHu%Ld|X{_$sw5(66EOY z9q;S!;_2q;>=@$d?-#t1;WH>ee!1yq zLRUz`!8Dtep|e#K7sktF58EMv>NwI!fPGWJku4$QWYGz(aK~a8IYI2EgQDS0ld1_I%equp^er|qBYEH3U zL1hsrp@0ccqAbp1U|?urxWOsZA=EF~DLKJty46Ih6((0W4H}$o@Q8Le^?P=DPRN{) zG(B@-<^tx)d6#)q8(eP)Nl#Fkt~pV2hSPlaneLZ`R2sYys*|Q?Ow72Uz`*e2w^l|U0|Ucj5C?{tp^VQ73=9m@8B!Qh7;_kM8KW3;nWC6-nWLC< zS)y2SS)*8U*`nAOLGnyF?78ew?2HUd45{o{0#N-(R0{KIMhG{BrG;TNgvk)aS;?x& z_7cR?WV^*)T9gx?mS1#>qbM~cvnVyWSq;~>$~J9m*%GCl@#lPEXme2Ez?cS%u6XK%FjwoF3~MYOw27$Ey~tU zEGW<~%S5W&g7z;KJTD8IBM6>JvRgGczy=fLUPE03tkc Aj{pDw diff --git a/app/configuration.py b/app/configuration.py index 129bb0a..092b4a8 100644 --- a/app/configuration.py +++ b/app/configuration.py @@ -2,8 +2,7 @@ class Config(object): DEBUG = False TESTING = False SQLALCHEMY_DATABASE_URI = "sqlite:///application.db" - BOOTSTRAP_FONTAWESOME = True - SECRET_KEY = "MINHACHAVESECRETA" + SECRET_KEY = "yessir" CSRF_ENABLED = True SQLALCHEMY_TRACK_MODIFICATIONS = True @@ -15,6 +14,5 @@ class Config(object): class DevelopmentConfig(Config): DEBUG = True - class TestingConfig(Config): TESTING = True diff --git a/app/forms.py b/app/forms.py new file mode 100644 index 0000000..452ad00 --- /dev/null +++ b/app/forms.py @@ -0,0 +1,30 @@ +from flask_wtf import FlaskForm +from wtforms import StringField, SubmitField, IntegerField, HiddenField +from wtforms.validators import DataRequired + +class NewWishlist(FlaskForm): + title = StringField("Title:", validators=[DataRequired()]) + description = StringField("Description:", validators=[DataRequired()]) + submit = SubmitField("Submit") + +# Each submit needs a different page fot it to work on the same page. +class DeleteWishlist(FlaskForm): + wl_del_submit = SubmitField("Delete wishlist") + +class EditWishlistInfo(FlaskForm): + title = StringField("Title:", validators=[DataRequired()]) + description = StringField("Description:", validators=[DataRequired()]) + wl_edit_submit = SubmitField("Submit") + +class ResetWishlistUrls(FlaskForm): + wl_reset_submit = SubmitField("Reset urls") + +class NewItem(FlaskForm): + title = StringField("Title:", validators=[DataRequired()]) + description = StringField("Description:", validators=[DataRequired()]) + price = IntegerField("Price:", validators=[DataRequired()]) + it_new_submit = SubmitField("Submit") + +class DeleteItem(FlaskForm): + index = HiddenField() + it_del_submit = SubmitField("Delete item") diff --git a/app/models.py b/app/models.py index 60113cf..00ecd18 100644 --- a/app/models.py +++ b/app/models.py @@ -1,20 +1,35 @@ +from typing import List from app import db - +from sqlalchemy.orm import Mapped, mapped_column, RelationshipProperty +from sqlalchemy import Uuid, String, Text +from uuid import uuid4 as uuid class Item(db.Model): - id = db.Column(db.Integer, primary_key=True) - title = db.Column(db.String(250)) - description = db.Column(db.Text) - price = db.Column(db.Float) - bought = db.Column(db.Boolean) + def __init__(self, title: str, description: str, price: float): + self.wishlist_id = None + self.title = title + self.description = description + self.price = price + self.bought = False + id: Mapped[int] = mapped_column(primary_key=True) + wishlist_id: Mapped[int | None] = mapped_column(db.ForeignKey("wishlist.id")) + title: Mapped[str] = mapped_column(db.String(250)) + description: Mapped[str] = mapped_column(db.Text) + price: Mapped[float] = mapped_column() + bought: Mapped[bool] = mapped_column() class Wishlist(db.Model): - id = db.Column(db.Integer, primary_key=True) - editId = db.Column(db.Uuid) - viewId = db.Column(db.Uuid) - title = db.Column(db.String(250)) - description = db.Column(db.Text) + def __init__(self, title: str, description: str): + self.editId = uuid() + self.viewId = uuid() + self.title = title + self.description = description - itemIds = db.Column(db.ARRAY(db.Integer)) # Store item IDs as an array - items = db.relationship("Item", primaryjoin="Wishlist.itemIds") + id: Mapped[int] = mapped_column(primary_key=True) + editId: Mapped[Uuid] = mapped_column(Uuid) + viewId: Mapped[Uuid] = mapped_column(Uuid) + title: Mapped[str] = mapped_column(String(250)) + description: Mapped[str] = mapped_column(Text) + + items: Mapped[List["Item"]] = db.relationship("Item", backref="post") \ No newline at end of file diff --git a/app/templates/edit.html b/app/templates/edit.html new file mode 100644 index 0000000..e3cd676 --- /dev/null +++ b/app/templates/edit.html @@ -0,0 +1,69 @@ +{% set cpath = url_for("edit", id=wishlist.editId) %} + +

Change name

+
+ {{ form_wl_editinfo.hidden_tag() }} + + {{ form_wl_editinfo.title.label }} + {{ form_wl_editinfo.title() }} + + {{ form_wl_editinfo.description.label }} + {{ form_wl_editinfo.description() }} + + {{ form_wl_editinfo.wl_edit_submit() }} +
+ +

Reset urls

+
    +
  • + View: {{ wishlist.viewId }} +
  • +
  • + Edit: {{ wishlist.editId }} +
  • +
+ + +
+ {{ form_wl_reseturls.hidden_tag() }} + {{ form_wl_reseturls.wl_reset_submit() }} +
+ +
+

Delete wishlist

+
+ {{ form_wl_delete.hidden_tag() }} + {{ form_wl_delete.wl_del_submit() }} +
+ +
+

New item

+
+ {{ form_it_new.hidden_tag() }} + + {{ form_it_new.title.label }} + {{ form_it_new.title() }} + + {{ form_it_new.description.label }} + {{ form_it_new.description() }} + + {{ form_it_new.price.label }} + {{ form_it_new.price() }} + + {{ form_it_new.it_new_submit() }} +
+ +
+

Delete items

+
    + {% for value in wishlist.items %} +
  • +
    + {{ form_it_delete.hidden_tag() }} + {{ form_it_delete.index(value=loop.index) }} + {{ form_it_delete.it_del_submit() }} +
    + {{ value.title }} +
  • + {% endfor %} +
\ No newline at end of file diff --git a/app/templates/new.html b/app/templates/new.html new file mode 100644 index 0000000..5667c9e --- /dev/null +++ b/app/templates/new.html @@ -0,0 +1,11 @@ +
+ {{ form.hidden_tag() }} + + {{ form.title.label }} + {{ form.title() }} + + {{ form.description.label }} + {{ form.description() }} + + {{ form.submit() }} +
\ No newline at end of file diff --git a/app/templates/view.html b/app/templates/view.html new file mode 100644 index 0000000..448bfb0 --- /dev/null +++ b/app/templates/view.html @@ -0,0 +1,10 @@ +

{{wishlist.title}}

+{{wishlist.description}} + +edit + +
    + {% for item in wishlist.items %} +
  • + {% endfor %} +
\ No newline at end of file diff --git a/app/views.py b/app/views.py index cd8117f..ce6d1b4 100644 --- a/app/views.py +++ b/app/views.py @@ -1,7 +1,108 @@ -from flask import url_for, redirect, render_template, flash, g, session -from app import app +from flask import url_for, redirect, render_template +from app import app, db +from app.forms import ( + NewWishlist, + DeleteWishlist, + EditWishlistInfo, + ResetWishlistUrls, + NewItem, + DeleteItem, +) +from app.models import Wishlist, Item +from uuid import UUID, uuid4 as uuid @app.route("/") def index(): return "hello" + + +@app.route("/new", methods=["GET", "POST"]) +def new(): + form = NewWishlist() + if form.validate_on_submit(): + wishlist = Wishlist(form.title.data, form.description.data) + db.session.add(wishlist) + db.session.commit() + + return redirect(url_for("view", id=wishlist.viewId)) + + return render_template("new.html", form=form) + + +@app.route("/edit/", methods=["GET", "POST"]) +def edit(id: str): + wishlist: Wishlist = db.one_or_404( + db.select(Wishlist).filter_by(editId=UUID(id)), + description="Failed to get wishlist. Are you sure this is the correct url?", + ) + + form_wl_delete = DeleteWishlist() + form_wl_editinfo = EditWishlistInfo() + form_wl_reseturls = ResetWishlistUrls() + form_it_new = NewItem() + form_it_delete = DeleteItem() + + # Each submit needs a different page fot it to work on the same page. + if form_wl_delete.validate_on_submit() and form_wl_delete.wl_del_submit.data: + for i in wishlist.items: + db.session.delete(i) + db.session.delete(wishlist) + db.session.commit() + + return redirect(url_for("index")) + elif form_wl_editinfo.validate_on_submit() and form_wl_editinfo.wl_edit_submit.data: + wishlist.title = form_wl_editinfo.title.data + wishlist.description = form_wl_editinfo.description.data + db.session.commit() + + return redirect(url_for("edit", id=id)) + elif form_wl_reseturls.validate_on_submit() and form_wl_reseturls.wl_reset_submit.data: + wishlist.editId = uuid() + wishlist.viewId = uuid() + db.session.commit() + + return redirect(url_for("edit", id=wishlist.editId)) + elif form_it_new.validate_on_submit() and form_it_new.it_new_submit.data: + f = form_it_new + item = Item( + f.title.data, + f.description.data, + f.price.data + ) + wishlist.items.append(item) + db.session.commit() + + return redirect(url_for("edit", id=id)) + elif form_it_delete.validate_on_submit() and form_it_delete.it_del_submit.data: + print(form_it_delete.index.data) + data = form_it_delete.index.data + i = int(data) if data != None else 0 + if i + 1 > len(wishlist.items): + return "Invalid item to delete", 400 + + wishlist.items.remove(wishlist.items[i]) + db.session.commit() + + return redirect(url_for("edit", id=id)) + + + return render_template( + "edit.html", + wishlist=wishlist, + form_wl_delete=form_wl_delete, + form_wl_editinfo=form_wl_editinfo, + form_wl_reseturls=form_wl_reseturls, + form_it_new=form_it_new, + form_it_delete=form_it_delete, + ) + + +@app.route("/view/") +def view(id: str): + wishlist = db.one_or_404( + db.select(Wishlist).filter_by(viewId=UUID(id)), + description="Failed to get wishlist. Are you sure this is the correct url?", + ) + + return render_template("view.html", wishlist=wishlist) diff --git a/flake.nix b/flake.nix index 6fa056c..a97daa2 100644 --- a/flake.nix +++ b/flake.nix @@ -14,7 +14,7 @@ { nativeBuildInputs = [ (pkgs.python3.withPackages - (x: [x.flask x.flask-wtf x.wtforms x.flask-sqlalchemy])) + (x: [x.flask x.flask-wtf x.wtforms x.flask-sqlalchemy x.uuid])) pkgs.entr ]; }; diff --git a/instance/application.db b/instance/application.db new file mode 100644 index 0000000000000000000000000000000000000000..8580d3a7c1c18024aed001ed7f2c4bf7ceb293df GIT binary patch literal 12288 zcmWFz^vNtqRY=P(%1ta$FlG>7U}R))P*7lCU=U_tU|?oI044?o1{MUDff0#~iz&>Y z*W|#p_GiD@9YQd1$w)y*}?)z8^ASOF#u*2JaR7|zHpE-TB}%m@w;m@JHD zLJ1g{D4xJkN=?Zu@k~)t0EdFH5o#bPm1U-u`15 zoG*t@kas_CKCe8_d7gS6&^W*VB*0NCM?+vV1V%$(Gz3ONU^E0qLtr!nMnhmU1O_4m zVtLpYe0}9LjgyiMOe|6@jZBQqP14MgEld*AQq0Vgj17|v%~R9N63vVg4a^f$k_?Rv zO;VCAEe*{Llguqr3{#8^EsRoYnUGV{{?xIxB?il$ha zn5U&0B^f50nVA?TC0iI+rX;5rr<$9mrkR=>8e1f%Sf&{mrx_(%rkJEzrY0H~rzNLZ zS{Nl;8XFlU8i9-hVLvX2d1mH`W{Cz#rfG>umL{g=#z~1L$rc7i#%U?Z21y3yM#)Ku zDF!B%X2xkIi6)7bW=UxV1|}BCNyY|d$!Qjr1|YLQ*pCxpo`r?6iCL;iQmSE+sfj^~ zQKFGWih+r#g=Mm#Nve@qa+0C3nT3&&adKLkg^96cYLcOmg}G_6fvLHnd0Lt!!hIYN z^ODmnl9JL=%nZ!Z%uI}pQW8_r%*_(bk`mLB%*_l9Q!EUPQjL;Ql1$Q!%}kQhj4Tr^ zQ&Z9`P0dXWjV)5s%t3Ag;Q)4!dD6m3#ugSyNtQ;52BxMaX{IJgDFzmXX66=&7Rd&t zW+rAS$*Jb1W@aXd$ti{=sm4YYCP~RA#>vTvNfzeGiOHtkS*dy1sYN;YIgxB26U8-C zEmKU)%*{*<3=-2!EG<%!j4aa<%?;8FlP!{sj1A3_(^4%{O_R+`QxXkL49$}*3=)&g z%uSP%(o&7gQVdNzQgd?h!&7r|6f!auN-|Q5QWYavAx4{~8WVXWT38sFq$FBcrlyz~BpD|q8kr|1nWrYDrFk$xOfoh|Gd3|dv^23aG%>L>HcT=$ zGd4CbHZV*!H%duKNlrFSPBSq#FflMUwJ=FcF-SBvH#am$OiVURH8)I2GfqqMV1$@t zn3!s2mYkMoVVr7cl4g)>nPy;aX_0DVmS~ikYLaSXVw#j@WR{v@Xr5wfl9-xek!GA` zZee0(Xk?I3js++b_Paic}5PN5Xi6_d^C;;0DyL~=l}o! literal 0 HcmV?d00001