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中文网其他相关文章!