Python開發者從Ruby on Rails遷移到Python時,常常會懷念Active Record的優雅資料庫互動方式。雖然Python的SQLAlchemy(以及SQLModel)預設採用不同的方法,但我們可以實現類似的模式,將Rails風格模型的便利性引入Python應用程序,同時保持類型安全並遵循Python最佳實踐。
Active Record模式
Active Record模式(由Ruby on Rails推廣)將資料庫記錄視為具有資料庫操作方法的物件。它無需使用單獨的儲存庫類別或資料存取物件(DAO),模型本身就知道如何與資料庫互動。
例如,在Rails中,你可以這樣寫:
<code class="language-ruby"># 查找记录 user = User.find(123) # 更新记录 user.name = "New Name" user.save # 创建新记录 post = Post.create(title: "Hello World")</code>
在Python中使用SQLModel實作
雖然Python的SQLModel沒有直接提供這種模式,但我們可以使用一個提供這些常用操作的基底類別來實作它。方法如下:
首先,我們建立一個實作常用CRUD運算的基底類別:
<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>
定義好基底類別後,我們可以建立繼承它的模型:
<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>
現在,我們可以使用類似Rails的語法來使用我們的模型,同時保持Python顯式的會話管理:
<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>
與Rails的主要差異
雖然這種模式為Python帶來了類似Rails的便利性,但需要注意一些重要的區別:
<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
裝飾器來處理不需要實例的操作。 <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>
最佳實踐
在Python中使用此模式時,請記住以下最佳實踐:
<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>
結論
Active Record模式可以在Python中有效實現,同時保持類型安全並遵循Python最佳實踐。雖然它比Rails需要更明確的會話管理,但它提供了類似的便利性,同時為開發人員提供了對資料庫操作的更多控制。
這種模式特別適用於:
請記住,這只是Python中資料庫操作的一種方法。 SQLModel和SQLAlchemy支援其他模式,例如儲存庫或資料存取對象,這些模式可能更適合某些用例。
資源
以上是使用 SQLModel 在 Python 中實作活動記錄模式的詳細內容。更多資訊請關注PHP中文網其他相關文章!