الوثائقالوحدة الثانية: البيانات

إنشاء تطبيق ملاحظات باستخدام Nexy، الجزء 8: تحرير ملاحظة وحذفها

يضع الجزء 8 نموذجين على صفحة الملاحظة: أحدهما يحفظ نصًا جديدًا، والآخر يحذف الملاحظة، وكلاهما عبارة عن إجراءات مثل تلك التي تنشئها.

قبل أن تبدأ: تطبيق الجزء 7.

تغيير وإزالة ملاحظة في قاعدة البيانات

src/notes.py
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 True
src/notes.py
from 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 يعود إلى القائمة:

src/actions/notes.py
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، اسم المعلمة:

src/routes/notes/[id].nexy
---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>

يحتاج العرض الآن إلى الملاحظة بأكملها، وليس النص فقط:

src/apps/notes/note_view.nexy
---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>
src/apps/notes/notes_controller.py
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، وقم بتغيير شراء الحليب إلى شراء حليب الشوفان ثم اضغط على حفظ: تعود الصفحة بالنص الجديد. اضغط على حذف: ستصل إلى القائمة، بدون الملاحظة.

ما تعلمته

  • يمكن أن تحتوي الصفحة الواحدة على عدة نماذج، كل منها ينشر في الإجراء الخاص به.
  • حقل مخفي يحمل المعرف؛ اسمه هو اسم معلمة الإجراء.
  • القواعد المشتركة بين إجراءين تعيش في وظيفة واحدة، يتم استدعاؤها من قبل كليهما.

الخطوات التالية

حرِّر على GitHub

Enter يفتح النتيجة، وEsc يغلق.