【问题标题】:fastapi + sqlalchemy + pydantic → how to process many-to-many relationsfastapi + sqlalchemy + pydantic → 如何处理多对多关系
【发布时间】:2021-09-24 09:04:32
【问题描述】:

我有editors 和articles。许多编辑可能与许多文章相关,许多文章可能同时有许多编辑。

我的数据库表是

  • 文章
id subject text
1 New Year Holidays In this year... etc etc etc
  • 编辑
id name email
1 John Smith some@email
  • EditorArticleRelation
editor_id article_id
1 1

我的模型是

from sqlalchemy import Boolean, Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship

from database import Base

class Editor(Base):
    __tablename__ = "editor"

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String(32), unique=False, index=False, nullable=True)
    email = Column(String(115), unique=True, index=True)
    articles = relationship("Article",
                    secondary=EditorArticleRelation,
                    back_populates="articles",
                    cascade="all, delete")

class Article(Base):
    __tablename__ = "article"

    id = Column(Integer, primary_key=True, index=True)
    subject = Column(String(32), unique=True, index=False)
    text = Column(String(256), unique=True, index=True, nullable=True)
    editors = relationship("Editor",
                    secondary=EditorArticleRelation,
                    back_populates="editors",
                    cascade="all, delete")

EditorArticleRelation = Table('editorarticlerelation', Base.metadata,
    Column('editor_id', Integer, ForeignKey('editor.id')),
    Column('article_id', Integer, ForeignKey('article.id'))
)

我的架构是

from typing import Optional, List
from pydantic import BaseModel

class EditorBase(BaseModel):
    name: Optional[str]
    email: str

class EditorCreate(EditorBase):
    pass

class Editor(EditorBase):
    id: int

    class Config:
        orm_mode = True

class ArticleBase(BaseModel):
    subject: str
    text: str

class ArticleCreate(ArticleBase):
    # WHAT I NEED TO SET HERE???
    editor_ids: List[int] = []

class Article(ArticleBase):
    id: int
    editors: List[Editor] = []

    class Config:
        orm_mode = True

我的垃圾

def create_article(db: Session, article_data: schema.ArticleCreate):
    db_article = model.Article(subject=article_data.subject, text=article_data.text, ??? HOW TO SET EDITORS HERE ???)
    db.add(db_article)
    db.commit()
    db.refresh(db_article)
    return db_article

我的路线

@app.post("/articles/", response_model=schema.Article)
def create_article(article_data: schema.ArticleCreate, db: Session = Depends(get_db)):
    db_article = crud.get_article_by_name(db, name=article_data.name)
    if db_article:
        raise HTTPException(status_code=400, detail="article already registered")
    if len(getattr(article_data, 'editor_ids', [])) > 0:
        ??? WHAT I NEED TO SET HERE???
    return crud.create_article(db=db, article_data=article_data)

我想要的→

我想发布文章创建 API 的数据并自动解析和添加编辑器关系,或者如果某些编辑器不存在则引发错误:

{
  "subject": "Fresh news"
  "text": "Today is ..."
  "editor_ids": [1, 2, ...]
}

问题是:

  1. 如何正确设置 crud 操作(HOW TO SET EDITORS HEREplace)?
  2. 如何正确设置创建/读取模式和关系字段(尤其是WHAT I NEED TO SET HERE地点)?
  3. 如何正确设置路线代码(尤其是WHAT I NEED TO SET HERE地点)?
  4. 如果这里无法自动解决关系,那么在哪里解决关系会更好(检查编辑器是否存在等)?路线还是杂物?
  5. 也许我的方式很糟糕?如果您知道如何处理与pydanticsqlalchemy 的多对多关系的任何示例,欢迎提供任何信息

【问题讨论】:

    标签: python sqlalchemy fastapi pydantic


    【解决方案1】:

    不确定我的解决方案是否最有效,但我是这样做的:

    • 路线(与问题相同):
    ...
    @app.post("/articles/", response_model=schema.Article)
    def create_article(article_data: schema.ArticleCreate, db: Session = Depends(get_db)):
        db_article = crud.get_article_by_name(db, name=article_data.name)
        if db_article:
            raise HTTPException(status_code=400, detail="article already registered")
        return crud.create_article(db=db, article_data=article_data)
    ...
    
    • 架构(与问题相同):
    ...
    class ArticleCreate(ArticleBase):
        editor_ids: List[int] = []
    ...
    
    • crud(解决方案在这里):
    def create_article(db: Session, article_data: schema.ArticleCreate):
        db_article = model.Article(subject=article_data.subject, text=article_data.text)
        if (editors := db.query(model.Editor).filter(model.Editor.id.in_(article_data.editor_ids))).count() == len(endpoint_data.topic_ids):
            db_article.topics.extend(editors)
        else:
            # even if at least one editor is not found, an error is raised
            # if existence is not matter you can skip this check and add relations only for existing data
            raise HTTPException(status_code=404, detail="editor not found")
        db.add(db_article)
        db.commit()
        db.refresh(db_article)
        return db_article
    

    欢迎任何更好的想法

    【讨论】:

      【解决方案2】:

      我找到了一个解决方案。

      def create_user_groups(db: Session, user_groups: schemas.UserGroupsBase):
          db_user = db.query(models.User).filter(models.User.id == user_groups.id_user).first()
          db_group = db.query(models.Group).filter(models.Group.id == user_groups.id_group).first()
      
          if not db_user and db_group:
              raise HTTPException(status_code=409, detail="User or Group not found in system.")
      
          db_user.groups.append(db_group)
      
          db.add(db_user)
          db.commit()
          db.refresh(db_user)
          return db_user
      

      【讨论】:

      • 这个答案可以从解释中受益。以下是How do I write a good answer? 的一些指南。仅代码答案不被视为好的答案,并且可能会被否决和/或删除,因为它们对学习者社区不太有用。它只对你很明显。解释它的作用,以及它与 OP 作者的现有答案有何不同/更好。 From Review
      • @Brayan 您的回答主要是在我自己的回答中重复create_article 方法。
      猜你喜欢
      • 2021-11-01
      • 2021-04-15
      • 1970-01-01
      • 2022-01-25
      • 2020-11-17
      • 2015-02-11
      • 2013-11-05
      • 2021-11-21
      相关资源
      最近更新 更多