use database
This commit is contained in:
parent
6e0023497c
commit
ca762f41c9
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
|
|
@ -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 %}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue