الوثائقالوحدة الثانية: البيانات
إنشاء تطبيق ملاحظات باستخدام Nexy، الجزء 5: تخزين الملاحظات في SQLite
ينقل الجزء الخامس الملاحظات من قاموس Python إلى قاعدة بيانات SQLite، مع نموذج وترحيل وملاحظتين للبدء بهما، وتقرأها الصفحات من هناك.
قبل أن تبدأ: تطبيق الجزء الرابع.
قم بتثبيت ORM وأداة الترحيل
uv add sqlmodel alembic
SQLModel يصف الجدول كفئة Python، و Alembic يحول التغييرات على تلك الفئات إلى عمليات ترحيل. لا يتم تغليف Nexy أيضًا: يمكنك استخدامها كما هي.
افتح قاعدة البيانات
عنوان قاعدة البيانات موجود في .env، في جذر المشروع. nx migrate ويقوم التطبيق بقراءتها من هناك:
DATABASE_URL=sqlite:///dev.dbsqlite:///dev.db هو ملف، dev.db، بجوار nexyconfig.py: SQLite لا يحتاج إلى خادم. احتفظ بـ .env خارج Git: فهو يحتوي على أسرار قاعدة بيانات حقيقية.
المحرك هو الاتصال بقاعدة البيانات. يقرأ DATABASE_URL، مع نفس الملف كافتراضي عندما يكون المتغير غير مضبوط:
import os from dotenv import load_dotenvfrom sqlmodel import create_engine load_dotenv() engine = create_engine(os.getenv("DATABASE_URL", "sqlite:///dev.db"))وصف ملاحظة
from sqlmodel import Field, SQLModel class Note(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True) text: strيعيش النموذج مع الميزة التي ينتمي إليها، بجانب وحدة التحكم الخاصة به:
from sqlmodel import Field, SQLModel class Note(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True) text: strtable=True يجعل الفصل جدولاً، note. يتم توفير id بواسطة قاعدة البيانات عند حفظ ملاحظة.
قم بإنشاء الجدول بالترحيل
nx migrate --initnx migrate -m "create notes"nx migrate --upgrade
--init يكتب المجلد alembic/ ويوجهه نحو النموذج الخاص بك. -m يقارن النموذج بقاعدة البيانات ويكتب الترحيل الذي ينشئ الجدول؛ --upgrade يديره. قم بتغيير النموذج لاحقًا، وقم بتشغيل الأمرين الأخيرين مرة أخرى، وسيتبعك الجدول.
قراءة الملاحظات من قاعدة البيانات
تحتفظ وظائف src/notes.py بأسمائها، وبالتالي تتغير الصفحات قليلاً: فهي الآن تفتح جلسة، وتقوم بالاستعلام عن الجدول، وتعيد كائنات Note بدلاً من السلاسل.
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)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)أضف ملاحظتين لتبدأ بهما
يملأ البرنامج النصي الأساسي قاعدة بيانات فارغة، لذا تبدأ كل نسخة جديدة من المشروع بشيء لإظهاره:
from sqlmodel import Session, select from src.configs.db import enginefrom src.models.note_model import Note def seed() -> None: with Session(engine) as session: if session.exec(select(Note)).first() is None: session.add_all([Note(text="Buy milk"), Note(text="Ship Nexy")]) session.commit() if __name__ == "__main__": seed()from sqlmodel import Session, select from src.apps.notes.note_model import Notefrom src.configs.db import engine def seed() -> None: with Session(engine) as session: if session.exec(select(Note)).first() is None: session.add_all([Note(text="Buy milk"), Note(text="Ship Nexy")]) session.commit() if __name__ == "__main__": seed()python -m src.seed
عند بدء التشغيل، يقوم Nexy باستيراد كل وحدة من src/ للعثور على إجراءات الجزء 6. if __name__ == "__main__": يحتفظ ببذرة الأمر أعلاه: يتم استيراده بواسطة التطبيق، ويحدد الملف فقط seed، ولا يمس أي شيء.
إظهار الملاحظات من قاعدة البيانات
from "@/components/NoteCard.nexy" import NoteCardfrom src.notes import all_notes notes = all_notes()<section class="app-page"> <h1>Notes</h1> {% for note in notes %} <NoteCard id="{{ note.id }}" text="{{ note.text }}" /> {% endfor %}</section>from nexy import notFoundfrom src.notes import find id: prop[int]note = find(id)if note is None: notFound()<section class="app-page"> <h1>{{ note.text }}</h1> <a href="/notes">All notes</a></section>from src.notes import all_notes async def GET(): return [note.model_dump() for note in all_notes()]from "@/components/NoteCard.nexy" import NoteCardfrom src.notes import all_notes notes = all_notes()<section class="app-page"> <h1>Notes</h1> {% for note in notes %} <NoteCard id="{{ note.id }}" text="{{ note.text }}" /> {% endfor %}</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", text=note.text) @Controller("/api/notes")class NotesApiController: async def get(self) -> list[dict]: return [note.model_dump() for note in all_notes()]فتح http://localhost:3000/notes: نفس الورقتين، اقرأ الآن من dev.db. أعد تشغيل nexy dev، وما زالوا هناك.
ما تعلمته
- SQLModel يصف الجدول كفئة؛ المحرك الموجود في
src/configs/db.pyيفتح قاعدة البيانات. nx migrateيكتب ويدير الترحيلات:--initمرة واحدة، ثم-mو--upgradeلكل تغيير.- تستدعي الصفحات ووحدات التحكم وظائف Python عادية تستعلم عن قاعدة البيانات: لا يضيف Nexy أي طبقة.
الخطوات التالية
- الجزء السادس: إنشاء ملاحظة من النموذج
- SQLite: البراغماتية والوصول غير المتزامن والاختبارات على قاعدة بيانات في الذاكرة