文档第二单元:数据
使用 Nexy 构建笔记应用程序,第 8 部分:编辑和删除笔记
第 8 部分在注释页面上放置了两种形式:一种保存新文本,另一种删除注释,两者都是与创建注释的操作类似的操作。
开始之前:第 7 部分的应用程序。
更改和删除数据库中的注释
from sqlmodel import Session, select from src.configs.db import enginefrom src.models.note_model import Note def all_notes() -> list[Note]: with Session(engine) as session: return list(session.exec(select(Note).order_by(Note.id))) def find(note_id: int) -> Note | None: with Session(engine) as session: return session.get(Note, note_id) def add_note(text: str) -> Note: note = Note(text=text) with Session(engine) as session: session.add(note) session.commit() session.refresh(note) return note def change_note(note_id: int, text: str) -> bool: with Session(engine) as session: note = session.get(Note, note_id) if note is None: return False note.text = text session.commit() return True def remove_note(note_id: int) -> bool: with Session(engine) as session: note = session.get(Note, note_id) if note is None: return False session.delete(note) session.commit() return Truefrom sqlmodel import Session, select from src.apps.notes.note_model import Notefrom src.configs.db import engine def all_notes() -> list[Note]: with Session(engine) as session: return list(session.exec(select(Note).order_by(Note.id))) def find(note_id: int) -> Note | None: with Session(engine) as session: return session.get(Note, note_id) def add_note(text: str) -> Note: note = Note(text=text) with Session(engine) as session: session.add(note) session.commit() session.refresh(note) return note def change_note(note_id: int, text: str) -> bool: with Session(engine) as session: note = session.get(Note, note_id) if note is None: return False note.text = text session.commit() return True def remove_note(note_id: int) -> bool: with Session(engine) as session: note = session.get(Note, note_id) if note is None: return False session.delete(note) session.commit() return True另外两个动作
update_note 使用与 create_note相同的规则检查文本,并将失败发送回注释页面。 delete_note 返回列表:
from urllib.parse import urlencode from nexy import notFoundfrom nexy.decorators import Actionfrom nexy.http import RedirectResponsefrom src.notes import add_note, change_note, remove_note MAX_LENGTH = 200 def back(path: str = "/notes", error: str = "") -> RedirectResponse: query = f"?{urlencode({'error': error})}" if error else "" return RedirectResponse(f"{path}{query}", status_code=303) def problem(text: str) -> str: if not text: return "A note cannot be blank." if len(text) > MAX_LENGTH: return f"A note is at most {MAX_LENGTH} characters." return "" @Actionasync def create_note(text: str) -> RedirectResponse: text = text.strip() if error := problem(text): return back(error=error) add_note(text) return back() @Actionasync def update_note(note_id: int, text: str) -> RedirectResponse: text = text.strip() if error := problem(text): return back(f"/notes/{note_id}", error) if not change_note(note_id, text): notFound() return back(f"/notes/{note_id}") @Actionasync def delete_note(note_id: int) -> RedirectResponse: if not remove_note(note_id): notFound() return back()create_note 的规则移至 problem,因此采用文本的两个操作不能分开。不再存在的注释会回答 404,就像它的页面一样。
笔记页的形式
id 在名为 note_id 的隐藏字段中传输,参数名称:
from nexy import notFound, useSearchParamsfrom src.notes import find id: prop[int]note = find(id)if note is None: notFound()error = useSearchParams().get("error", "")<section class="app-page"> <h1>{{ note.text | e }}</h1> {% if error %} <p class="note-error" role="alert">{{ error | e }}</p> {% endif %} <form method="post" action="{{ __actions_path['update_note'] }}"> <input type="hidden" name="note_id" value="{{ note.id }}"> <input name="text" value="{{ note.text | e }}" maxlength="200" required> <button type="submit">Save</button> </form> <form method="post" action="{{ __actions_path['delete_note'] }}"> <input type="hidden" name="note_id" value="{{ note.id }}"> <button type="submit">Delete</button> </form> <a href="/notes">All notes</a></section>视图现在需要整个注释,而不仅仅是其文本:
from nexy import useSearchParams note: prop[object]error = useSearchParams().get("error", "")<section class="app-page"> <h1>{{ note.text | e }}</h1> {% if error %} <p class="note-error" role="alert">{{ error | e }}</p> {% endif %} <form method="post" action="{{ __actions_path['update_note'] }}"> <input type="hidden" name="note_id" value="{{ note.id }}"> <input name="text" value="{{ note.text | e }}" maxlength="200" required> <button type="submit">Save</button> </form> <form method="post" action="{{ __actions_path['delete_note'] }}"> <input type="hidden" name="note_id" value="{{ note.id }}"> <button type="submit">Delete</button> </form> <a href="/notes">All notes</a></section>from nexy import notFoundfrom nexy.decorators import Controllerfrom nexy.hooks import useViewsfrom src.notes import all_notes, find LAYOUT = "src/apps/layout.nexy" def page(view: str, **context: object): return useViews(LAYOUT, context={"children": useViews(view, context=context)}) @Controller("/notes")class NotesController: async def get(self): return page("src/apps/notes/notes_view.nexy") @Controller("/notes/{id}")class NoteController: async def get(self, id: int): note = find(id) if note is None: notFound() return page("src/apps/notes/note_view.nexy", note=note) @Controller("/api/notes")class NotesApiController: async def get(self) -> list[dict]: return [note.model_dump() for note in all_notes()]打开 http://localhost:3000/notes/1,将购买牛奶更改为购买燕麦牛奶,然后按 保存:页面返回并显示新文本。按 删除:您将进入列表,但不包含注释。
你学到了什么
- 一个页面可以包含多个表单,每个表单都发布到自己的操作。
- 隐藏字段携带 id;它的名称是操作的参数名称。
- 两个操作共享的规则存在于一个函数中,由两者调用。
后续步骤
- 第 9 部分:对笔记进行分页
- 没有 JavaScript 的表单:表单如何到达操作