【问题标题】:How to handle nested relationships in marshmallow-sqlalchemy如何处理 marshmallow-sqlalchemy 中的嵌套关​​系
【发布时间】:2019-01-16 00:36:21
【问题描述】:

我有一个项目,我正在尝试使用 marshmallow-sqlalchemy 包将数据从 JSON 加载到 Sqlalchemy 数据库中。该模型包含与子模型的一对多关系。

使用作者多书的经典例子:

class Book(Base):
    __tablename__ = "book"
    id = Column(Integer, primary_key=True)
    title = Column(String(50))

    author_id = Column(Integer, ForeignKey("author.id"), nullable=False)
    author = relationship("Author", backref=backref("books"))


class Author(Base):
    __tablename__ = "author"
    id = Column(Integer, primary_key=True)
    name = Column(String(250))

    books = relationship("Author", back_populates="author")


class BookSchema(ModelSchema):
    class Meta:
        model = Book
        sqla_session = Session


class AuthorSchema(ModelSchema):
    class Meta:
        model = Author
        sqla_session = Session

    books = fields.Nested(BookSchema, many=True)

输入的 JSON 是

{
    "name": "Author A",
    "books": [
        {"title": "Book 1"},
        {"title": "Book 2"}
    ]
 }

当我尝试加载该 JSON 时,使用以下内容:

json_repr = {...}
author_schema = AuthorSchema()
obj_repr = author_schema.load(json_repr)

当它试图反序列化文档说 Book 是一种不可散列的类型时,它会引发异常。

发生了什么,我相信反序列化器正在尝试使用类似的东西来创建对象

obj = model(**data)

这不适用于一对多关系,因为列表的实例需要append()'ed 到 Author.books 属性。

我在网络上的任何地方都找不到任何这样的示例,我看到的每个示例似乎都嵌套了一个实例,而不是一个列表。是否有推荐的方法来使用 marshmallow-sqlalchemy 完成此操作,或者我应该恢复使用直接 marshmallow 包,并使用 @post_load 方法手动附加关系对象。

【问题讨论】:

    标签: python sqlalchemy marshmallow


    【解决方案1】:

    我遇到了与这篇旧帖子类似的问题。设法在稍微不同的框架 Flask + SQLAlchemy + Marshmallow-SQLAlchemy(版本 2)下修复了这篇文章。贴出代码以防万一。

    大部分更改是models.py

    1. 换行books = relationship("Book", back_populates="author")
    2. 使用back_populates 而不是backref,因为我收到错误sqlalchemy.exc.ArgumentError: Error creating backref 'books' on relationship 'Book.author': property of that name exists on mapper 'mapped class Author->author

    models.py

    class Book(db.Model):
        __tablename__ = "book"
        id = Column(db.Integer, primary_key=True)
        title = Column(db.String(50))
    
        author_id = Column(db.Integer, db.ForeignKey("author.id"), nullable=False)
        author = relationship("Author", back_populates="books")
    
    
    class Author(db.Model):
        __tablename__ = "author"
        id = Column(db.Integer, primary_key=True)
        name = Column(db.String(250))
    
        books = relationship("Book", back_populates="author")
    

    schemas.py - 大体相同

    class BookSchema(ModelSchema):
        class Meta:
            model = Book
            sqla_session = db.session
    
    
    class AuthorSchema(ModelSchema):
        class Meta:
            model = Author
            sqla_session = db.session
    
        books = fields.Nested(BookSchema, many=True)
    

    views.py

    @api.route('/author/', methods=['POST'])
    def new_author():
        schema = AuthorSchema()
        author = schema.load(request.get_json())
        db.session.add(author.data) # version 2 marshmallow
        db.session.commit()
        return jsonify({"success": True})
    

    【讨论】:

      【解决方案2】:

      看来,处理这个问题的一种方法是在 sqlalchemy 模型上添加一个初始化器,它显式地附加到集合中

      class Author(Model):
          __tablename__ = "author"
      
          def __init__(self, books=None, *args, **kwargs):
              super(Author, self).__init__(*args, **kwargs)
              books = books or []
              for book in books:
                  self.books.append(book)
      

      仍然很好奇是否有更好的解决方案。

      【讨论】:

      • 你有没有找到其他的方法?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-25
      相关资源
      最近更新 更多