use database

This commit is contained in:
v 2023-10-31 01:00:31 +01:00
parent 6e0023497c
commit ca762f41c9
6 changed files with 59 additions and 45 deletions

View file

@ -20,9 +20,11 @@ package_dir =
packages = find: packages = find:
python_requires = >=3.10 python_requires = >=3.10
install_requires = install_requires =
aiosqlite
jinja2 jinja2
python-multipart python-multipart
starlette starlette
tortoise-orm
uvicorn[standard] uvicorn[standard]
[options.packages.find] [options.packages.find]

View file

@ -1,5 +1,3 @@
from uuid import UUID, uuid4
from jinja2 import PackageLoader from jinja2 import PackageLoader
from starlette.applications import Starlette from starlette.applications import Starlette
from starlette.requests import Request from starlette.requests import Request
@ -7,19 +5,32 @@ from starlette.responses import Response
from starlette.routing import Route, Mount from starlette.routing import Route, Mount
from starlette.staticfiles import StaticFiles from starlette.staticfiles import StaticFiles
from starlette.templating import Jinja2Templates from starlette.templating import Jinja2Templates
from uvicorn import run from tortoise import fields, Tortoise
from tortoise.models import Model
from uvicorn import Config, Server
from uvloop import run
class Note(Model):
id = fields.UUIDField(pk=True)
paragraphs: fields.ReverseRelation["Paragraph"]
class Paragraph(Model):
id = fields.UUIDField(pk=True)
note: fields.ForeignKeyRelation[Note] = fields.ForeignKeyField(
"model.Note", related_name="paragraphs"
)
content = fields.TextField(default="")
templates = Jinja2Templates( templates = Jinja2Templates(
directory="/nonexistent", # just a dummy, FileSystemLoader is not used directory="/nonexistent", # just a dummy, FileSystemLoader is not used
loader=PackageLoader("wikinotes"), loader=PackageLoader("wikinotes"),
) )
notes: dict[UUID, list[UUID]] = {}
paragraphs: dict[UUID, str] = {}
async def home(request: Request) -> Response: async def home(request: Request) -> Response:
only_body = request.headers.get("hx-request", "false") == "true"
return templates.TemplateResponse( return templates.TemplateResponse(
"home.html", "home.html",
headers={ headers={
@ -27,89 +38,79 @@ async def home(request: Request) -> Response:
}, },
context={ context={
"request": request, "request": request,
"only_body": only_body,
}, },
) )
async def create_note(request: Request) -> Response: async def create_note(request: Request) -> Response:
note_id = uuid4() note = await Note.create()
notes[note_id] = [] await note.fetch_related("paragraphs")
return templates.TemplateResponse( return templates.TemplateResponse(
"note.html", "note.html",
headers={ headers={
"HX-Push-Url": str(request.url_for("note", note_id=note_id)), "HX-Push-Url": str(request.url_for("note", note_id=note.id)),
}, },
context={ context={
"request": request, "request": request,
"only_body": True, "note": note,
"note_id": note_id,
"paragraphs": [],
}, },
) )
async def note(request: Request) -> Response: async def note(request: Request) -> Response:
note_id = request.path_params["note_id"] note = await Note.get(id=request.path_params["note_id"])
only_body = request.headers.get("hx-request", "false") == "true" await note.fetch_related("paragraphs")
return templates.TemplateResponse( return templates.TemplateResponse(
"note.html", "note.html",
headers={ headers={
"HX-Push-Url": str(request.url_for("note", note_id=note_id)), "HX-Push-Url": str(request.url_for("note", note_id=note.id)),
}, },
context={ context={
"request": request, "request": request,
"only_body": only_body, "note": note,
"note_id": note_id,
"paragraphs": [(id, paragraphs[id]) for id in notes[note_id]],
}, },
) )
async def create_paragraph(request: Request) -> Response: async def create_paragraph(request: Request) -> Response:
note_id = request.path_params["note_id"] note = await Note.get(id=request.path_params["note_id"])
paragraph_id = uuid4()
async with request.form() as form: async with request.form() as form:
paragraphs[paragraph_id] = str(form["content"]) paragraph = await Paragraph.create(note=note, content=str(form["content"]))
notes[note_id] = [paragraph_id]
return templates.TemplateResponse( return templates.TemplateResponse(
"paragraph.html", "paragraph.html",
context={ context={
"request": request, "request": request,
"note_id": note_id, "note": note,
"paragraph_id": paragraph_id, "paragraph": paragraph,
"content": paragraphs[paragraph_id],
}, },
) )
async def paragraph(request: Request) -> Response: async def paragraph(request: Request) -> Response:
note_id = request.path_params["note_id"] note = await Note.get(id=request.path_params["note_id"])
paragraph_id = request.path_params["paragraph_id"] paragraph = await Paragraph.get(id=request.path_params["paragraph_id"])
if request.method == "PUT": if request.method == "PUT":
async with request.form() as form: async with request.form() as form:
paragraphs[paragraph_id] = str(form["content"]) await paragraph.update_from_dict({"content": str(form["content"])}).save()
return templates.TemplateResponse( return templates.TemplateResponse(
"paragraph.html", "paragraph.html",
context={ context={
"request": request, "request": request,
"note_id": note_id, "note": note,
"paragraph_id": paragraph_id, "paragraph": paragraph,
"content": paragraphs[paragraph_id],
}, },
) )
async def edit_paragraph(request: Request) -> Response: async def edit_paragraph(request: Request) -> Response:
note_id = request.path_params["note_id"] note = await Note.get(id=request.path_params["note_id"])
paragraph_id = request.path_params["paragraph_id"] paragraph = await Paragraph.get(id=request.path_params["paragraph_id"])
return templates.TemplateResponse( return templates.TemplateResponse(
"edit.html", "edit.html",
context={ context={
"request": request, "request": request,
"note_id": note_id, "note": note,
"paragraph_id": paragraph_id, "paragraph": paragraph,
"content": paragraphs[paragraph_id],
}, },
) )
@ -147,5 +148,16 @@ app = Starlette(
) )
async def async_main() -> None:
await Tortoise.init(db_url="sqlite://:memory:", modules={"model": ["wikinotes"]})
await Tortoise.generate_schemas()
config = Config("wikinotes:app", port=8080, log_level="info")
server = Server(config)
await server.serve()
await Tortoise.close_connections()
def main() -> None: def main() -> None:
run("wikinotes:app", port=8080, log_level="info") run(async_main())

View file

@ -1,4 +1,4 @@
{% if not only_body %} {% if request.headers.get("hx-request", "false") != "true" %}
{% extends "base.html" %} {% extends "base.html" %}
{% endif %} {% endif %}
{% block body %} {% block body %}

View file

@ -1,5 +1,5 @@
<form hx-put="{{ url_for("paragraph", note_id=note_id, paragraph_id=paragraph_id) }}" hx-target="this" hx-swap="outerHTML"> <form hx-put="{{ url_for("paragraph", note_id=note.id, paragraph_id=paragraph.id) }}" hx-target="this" hx-swap="outerHTML">
<textarea class="form-control mb-3" name="content" rows="10" autofocus>{{ content }}</textarea> <textarea class="form-control mb-3" name="content" rows="10" autofocus>{{ paragraph.content }}</textarea>
<button class="btn btn-primary">Submit</button> <button class="btn btn-primary">Submit</button>
<button class="btn" hx-get="{{ url_for("paragraph", note_id=note_id, paragraph_id=paragraph_id) }}">Cancel</button> <button class="btn" hx-get="{{ url_for("paragraph", note_id=note.id, paragraph_id=paragraph.id) }}">Cancel</button>
</form> </form>

View file

@ -5,10 +5,10 @@
</div> </div>
{% endblock %} {% endblock %}
{% block main %} {% block main %}
{% for paragraph_id, content in paragraphs %} {% for paragraph in note.paragraphs %}
{% include "paragraph.html" %} {% include "paragraph.html" %}
{% else %} {% else %}
<form hx-post="{{ url_for("create_paragraph", note_id=note_id) }}" hx-target="this" hx-swap="outerHTML"> <form hx-post="{{ url_for("create_paragraph", note_id=note.id) }}" hx-target="this" hx-swap="outerHTML">
<textarea class="form-control mb-3" name="content" rows="10" autofocus></textarea> <textarea class="form-control mb-3" name="content" rows="10" autofocus></textarea>
<button class="btn btn-primary">Submit</button> <button class="btn btn-primary">Submit</button>
</form> </form>

View file

@ -1 +1 @@
<p class="editable" title="Click to edit" hx-get="{{ url_for("edit_paragraph", note_id=note_id, paragraph_id=paragraph_id) }}" hx-target="this" hx-swap="outerHTML">{{ content }}</p> <p class="editable" title="Click to edit" hx-get="{{ url_for("edit_paragraph", note_id=note.id, paragraph_id=paragraph.id) }}" hx-target="this" hx-swap="outerHTML">{{ paragraph.content }}</p>