الوثائقالوحدة الثانية: البيانات
إنشاء تطبيق ملاحظات باستخدام 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، إعجاب بصفحتها.
نماذج صفحة المذكرة
ينتقل المعرف في حقل مخفي اسمه 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، وقم بتغيير شراء الحليب إلى شراء حليب الشوفان ثم اضغط على حفظ: تعود الصفحة بالنص الجديد. اضغط على حذف: ستصل إلى القائمة، بدون الملاحظة.
ما تعلمته
- يمكن أن تحتوي الصفحة الواحدة على عدة نماذج، كل منها ينشر في الإجراء الخاص به.
- حقل مخفي يحمل المعرف؛ اسمه هو اسم معلمة الإجراء.
- القواعد المشتركة بين إجراءين تعيش في وظيفة واحدة، يتم استدعاؤها من قبل كليهما.
الخطوات التالية
- الجزء التاسع: ترقيم الصفحات للملاحظات
- النماذج بدون JavaScript: كيف يصل النموذج إلى إجراء