Maison >développement back-end >Tutoriel Python >Implémentation du modèle d'enregistrement actif en Python avec SQLModel

Implémentation du modèle d'enregistrement actif en Python avec SQLModel

Mary-Kate Olsen
Mary-Kate Olsenoriginal
2025-01-27 00:12:09441parcourir

Implementing the Active Record Pattern in Python with SQLModel

Les développeurs Python manquent souvent l'interaction élégante avec la base de données d'Active Record lors de la migration de Ruby on Rails vers Python. Bien que SQLAlchemy de Python (et donc SQLModel) adopte une approche différente par défaut, nous pouvons implémenter un modèle similaire pour apporter la commodité des modèles de style Rails aux applications Python tout en maintenant la sécurité des types et en suivant les meilleures pratiques Python.

Mode d'enregistrement actif

Le modèle Active Record (popularisé par Ruby on Rails) traite les enregistrements de base de données comme des objets avec des méthodes de manipulation de base de données. Il n'est pas nécessaire d'utiliser une classe de référentiel ou un objet d'accès aux données (DAO) distinct, le modèle lui-même sait comment interagir avec la base de données.

Par exemple, dans Rails vous pourriez écrire :

<code class="language-ruby"># 查找记录
user = User.find(123)

# 更新记录
user.name = "New Name"
user.save

# 创建新记录
post = Post.create(title: "Hello World")</code>

Utiliser SQLModel en Python

Bien que le SQLModel de Python ne fournisse pas directement ce mode, nous pouvons l'implémenter en utilisant une classe de base qui fournit ces opérations courantes. Voici comment procéder :

  1. Modèle basé sur CRUD

Tout d'abord, nous créons une classe de base qui implémente les opérations CRUD courantes :

<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>
  1. Utilisez ce modèle dans votre modèle

Après avoir défini la classe de base, nous pouvons créer des modèles qui en héritent :

<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>
  1. Utiliser le modèle

Nous pouvons désormais utiliser une syntaxe de type Rails pour consommer nos modèles tout en conservant la gestion explicite des sessions de 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>

Principales différences avec Rails

Bien que ce modèle apporte à Python des commodités similaires à celles de Rails, il existe quelques différences importantes à noter :

  1. Gestion explicite des sessions : Python nécessite une gestion explicite des sessions, ce qui permet de mieux comprendre les transactions de la base de données.
<code class="language-python"># 使用SQLModel的Python
with get_session() as session:
    article = Article.create(session, title="Hello")

# 与Rails对比
article = Article.create(title: "Hello")</code>
  1. Sécurité des types : les astuces de type de Python offrent une meilleure prise en charge de l'IDE et détectent les erreurs plus tôt.
<code class="language-python">class Article(CRUDModel, table=True):
    title: str  # 类型安全!
    views: int = Field(default=0)</code>
  1. Méthode de classe : Python utilise un décorateur @classmethod explicite pour gérer les opérations qui ne nécessitent pas d'instance.
  2. Gestion des erreurs : Python encourage la gestion explicite des exceptions :
<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>

Bonnes pratiques

Lorsque vous utilisez ce modèle en Python, gardez à l'esprit les bonnes pratiques suivantes :

  1. Toujours utiliser le gestionnaire de contexte  :
<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>
  1. Tapez la sécurité :
<code class="language-python">   # 使用正确的类型提示
   def get_article(id: str) -> Optional[Article]:
       with get_session() as session:
           return Article.find(session, id)</code>
  1. Vérification :
<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>
  1. Gestion des relations :
<code class="language-python">   class Article(CRUDModel, table=True):
       # 正确使用级联删除
       comments: List["Comment"] = Relationship(
           back_populates="article",
           sa_relationship_kwargs={"cascade": "all, delete-orphan"}
       )</code>

Conclusion

Le modèle Active Record peut être implémenté efficacement en Python tout en maintenant la sécurité des types et en suivant les meilleures pratiques Python. Bien qu'il nécessite une gestion de session plus explicite que Rails, il offre une commodité similaire tout en donnant aux développeurs plus de contrôle sur les opérations de base de données.

Ce mode est particulièrement adapté pour :

  • L'équipe est passée de Rails à Python
  • Projet du fonctionnement de la base de données du Model Center
  • La sécurité des types et les discussions explicites sont des applications importantes

N'oubliez pas, ce n'est qu'une méthode de fonctionnement de la base de données dans Python. SqlModel et Sqlalchemy prennent en charge d'autres modes, tels que le référentiel ou les objets d'accès aux données, qui peuvent être plus adaptés à certains cas.

Ressource

    Document sqlmodel
  • fastapi
  • Document sqlamy
  • Tapez des conseils dans Python

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn