Python-Entwickler vermissen bei der Migration von Ruby on Rails zu Python oft die elegante Datenbankinteraktion von Active Record. Während Pythons SQLAlchemy (und damit SQLModel) standardmäßig einen anderen Ansatz verfolgt, können wir ein ähnliches Muster implementieren, um den Komfort von Modellen im Rails-Stil in Python-Anwendungen zu integrieren und gleichzeitig die Typsicherheit zu wahren und die Best Practices von Python zu befolgen.
Aktiver Aufnahmemodus
Das Active Record-Muster (populär gemacht durch Ruby on Rails) behandelt Datenbankdatensätze als Objekte mit Datenbankmanipulationsmethoden. Es ist nicht erforderlich, eine separate Repository-Klasse oder ein Datenzugriffsobjekt (DAO) zu verwenden, das Modell selbst weiß, wie es mit der Datenbank interagiert.
Zum Beispiel könnten Sie in Rails schreiben:
<code class="language-ruby"># 查找记录 user = User.find(123) # 更新记录 user.name = "New Name" user.save # 创建新记录 post = Post.create(title: "Hello World")</code>
SQLModel in Python verwenden
Obwohl Pythons SQLModel diesen Modus nicht direkt bereitstellt, können wir ihn mithilfe einer Basisklasse implementieren, die diese allgemeinen Operationen bereitstellt. So geht's:
Zuerst erstellen wir eine Basisklasse, die gängige CRUD-Operationen implementiert:
<code class="language-python">from typing import TypeVar, List, Optional, Tuple from datetime import datetime import uuid from sqlmodel import SQLModel, Session, select from sqlalchemy import func T = TypeVar("T", bound="CRUDModel") class CRUDModel(SQLModel): id: str = Field( default_factory=lambda: str(uuid.uuid4()), primary_key=True ) created_at: datetime = Field(default_factory=datetime.utcnow) updated_at: datetime = Field(default_factory=datetime.utcnow) @classmethod def all(cls: type[T], session: Session) -> List[T]: statement = select(cls) return session.exec(statement).all() @classmethod def find(cls: type[T], session: Session, id: str) -> Optional[T]: statement = select(cls).where(cls.id == id) return session.exec(statement).first() @classmethod def create(cls: type[T], session: Session, **kwargs) -> T: db_obj = cls(**kwargs) session.add(db_obj) session.commit() session.refresh(db_obj) return db_obj def update(self: T, session: Session, **kwargs) -> T: kwargs['updated_at'] = datetime.utcnow() for key, value in kwargs.items(): setattr(self, key, value) session.add(self) session.commit() session.refresh(self) return self def delete(self: T, session: Session) -> None: session.delete(self) session.commit() @classmethod def paginate( cls: type[T], session: Session, page: int = 1, per_page: int = 20 ) -> Tuple[List[T], int]: statement = select(cls) total = session.exec(select(func.count()).select_from(statement)).one() offset = (page - 1) * per_page results = session.exec( statement.offset(offset).limit(per_page) ).all() return results, total</code>
Nachdem wir die Basisklasse definiert haben, können wir Modelle erstellen, die sie erben:
<code class="language-python">class Article(CRUDModel, table=True): title: str = Field(..., description="Article title") content: str = Field(..., description="Article content") status: str = Field(default="draft") # Relationships comments: List["Comment"] = Relationship( back_populates="article", sa_relationship_kwargs={"cascade": "all, delete-orphan"} )</code>
Jetzt können wir eine Rails-ähnliche Syntax verwenden, um unsere Modelle zu nutzen und gleichzeitig die explizite Sitzungsverwaltung von Python beizubehalten:
<code class="language-python">from db.session import get_session # 列出所有文章 with get_session() as session: articles = Article.all(session) # 查找特定文章 with get_session() as session: article = Article.find(session, "some-uuid") if article: print(f"Found: {article.title}") # 创建新文章 with get_session() as session: article = Article.create( session, title="My New Article", content="Some content here" ) # 更新文章 with get_session() as session: article = Article.find(session, "some-uuid") if article: updated = article.update( session, title="Updated Title", content="New content" ) # 删除文章 with get_session() as session: article = Article.find(session, "some-uuid") if article: article.delete(session) # 分页 with get_session() as session: articles, total = Article.paginate(session, page=2, per_page=10)</code>
Hauptunterschiede zu Rails
Während dieses Muster Rails-ähnliche Annehmlichkeiten für Python bietet, sind einige wichtige Unterschiede zu beachten:
<code class="language-python"># 使用SQLModel的Python with get_session() as session: article = Article.create(session, title="Hello") # 与Rails对比 article = Article.create(title: "Hello")</code>
<code class="language-python">class Article(CRUDModel, table=True): title: str # 类型安全! views: int = Field(default=0)</code>
@classmethod
-Dekorator, um Vorgänge abzuwickeln, die keine Instanz erfordern. <code class="language-python">with get_session() as session: try: article = Article.find(session, "non-existent") if article is None: raise HTTPException(status_code=404, detail="Article not found") except Exception as e: # 处理其他数据库错误 raise HTTPException(status_code=500, detail=str(e))</code>
Best Practices
Beachten Sie bei der Verwendung dieses Musters in Python die folgenden Best Practices:
<code class="language-python"> # 正确的做法 with get_session() as session: article = Article.create(session, title="Hello") # 不正确的方法 session = get_session() article = Article.create(session, title="Hello") session.close()</code>
<code class="language-python"> # 使用正确的类型提示 def get_article(id: str) -> Optional[Article]: with get_session() as session: return Article.find(session, id)</code>
<code class="language-python"> class Article(CRUDModel, table=True): title: str = Field(..., min_length=1, max_length=100) status: str = Field( default="draft", validate_default=True, validator=lambda x: x in ["draft", "published"] )</code>
<code class="language-python"> class Article(CRUDModel, table=True): # 正确使用级联删除 comments: List["Comment"] = Relationship( back_populates="article", sa_relationship_kwargs={"cascade": "all, delete-orphan"} )</code>
Fazit
Das Active Record-Muster kann effizient in Python implementiert werden, wobei die Typsicherheit gewahrt bleibt und die Best Practices von Python befolgt werden. Obwohl es eine explizitere Sitzungsverwaltung erfordert als Rails, bietet es einen ähnlichen Komfort und gibt Entwicklern gleichzeitig mehr Kontrolle über Datenbankvorgänge.
Dieser Modus eignet sich besonders für:
Denken Sie daran, dass dies nur eine Möglichkeit ist, Datenbankoperationen in Python durchzuführen. SQLModel und SQLAlchemy unterstützen andere Modi wie Repositorys oder Datenzugriffsobjekte, die für bestimmte Anwendungsfälle möglicherweise besser geeignet sind.
Ressourcen
Das obige ist der detaillierte Inhalt vonImplementieren des Active Record Pattern in Python mit SQLModel. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!