【问题标题】:Flask SQLAlchemy 'dict' object has no attribute '_sa_instance_state'Flask SQLAlchemy \'dict\' 对象没有属性 \'_sa_instance_state\'
【发布时间】:2022-11-06 17:22:52
【问题描述】:

尝试创建新文档以及与一组交易对手的关联关系时出现以下错误。

AttributeError:“dict”对象没有属性“_sa_instance_state”

我认为这个问题一定存在于我的模型定义中,如果我为交易对手关系删除“backref="documents”,我会得到同样的错误,但在下一行尝试添加文档时。

数据库模型:

documents_counterparties = Table(
    "documents_counterparties",
    Base.metadata,
    Column("document_id", ForeignKey("documents.id"), primary_key=True),
    Column("counterparty_id", ForeignKey(
        "counterparties.id"), primary_key=True)
)


class Document(Base):
    __tablename__ = "documents"

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    start_date = Column(Date)
    end_date = Column(Date)
    owner_id = Column(Integer, ForeignKey("users.id"))

    owner = relationship("User", back_populates="documents")

    counterparties = relationship(
        "Counterparty", secondary=documents_counterparties, backref="documents"
    )

解析器:

def create_document(db: Session, document: DocumentCreate, user_id: int):
    db_document = models.Document(**document.dict(), owner_id=user_id) #<- errors here
    db.add(db_document)
    db.commit()
    db.refresh(db_document)
    return db_document

编辑:

文档创建

class DocumentBase(BaseModel):
    name: str
    start_date: datetime.date
    end_date: datetime.date


class DocumentCreate(DocumentBase):
    counterparties: "list[CounterpartyClean]"

【问题讨论】:

  • 你可以添加文档创建类吗?
  • 您的 counterparties 是字典列表,而不是 SQLAlchemy 对象列表。 SQLAlchemy 不知道如何存储字典。

标签: python sqlalchemy fastapi


【解决方案1】:

正如@MatsLindh 所暗示的那样,问题在于类型。解决方案在这里:

How to use nested pydantic models for sqlalchemy in a flexible way

编辑以包括使用的解决方案:

归功于 Daan Beverdam:

我给每个嵌套的 pydantic 模型一个包含相应 SQLAlchemy 模型的 Meta 类。像这样:

from pydantic import BaseModel
from models import ChildDBModel, ParentDBModel

class ChildModel(BaseModel):
    some_attribute: str = 'value'
    class Meta:
        orm_model = ChildDBModel

class ParentModel(BaseModel):
    child: ChildModel

这让我可以编写一个通用函数,循环遍历 pydantic 对象并将子模型转换为 SQLAlchemy 模型:

def is_pydantic(obj: object):
    """ Checks whether an object is pydantic. """
    return type(obj).__class__.__name__ == "ModelMetaclass"


def parse_pydantic_schema(schema):
    """
        Iterates through pydantic schema and parses nested schemas
        to a dictionary containing SQLAlchemy models.
        Only works if nested schemas have specified the Meta.orm_model.
    """
    parsed_schema = dict(schema)
    for key, value in parsed_schema.items():
        try:
            if isinstance(value, list) and len(value):
                if is_pydantic(value[0]):
                    parsed_schema[key] = [schema.Meta.orm_model(**schema.dict()) for schema in value]
            else:
                if is_pydantic(value):
                    parsed_schema[key] = value.Meta.orm_model(**value.dict())
        except AttributeError:
            raise AttributeError("Found nested Pydantic model but Meta.orm_model was not specified.")
    return parsed_schema

parse_pydantic_schema 函数返回 pydantic 模型的字典表示,其中子模型被 Meta.orm_model 中指定的相应 SQLAlchemy 模型替换。您可以使用此返回值一次性创建父 SQLAlchemy 模型:

parsed_schema = parse_pydantic_schema(parent_model)  # parent_model is an instance of pydantic ParentModel 
new_db_model = ParentDBModel(**parsed_schema)
# do your db actions/commit here

如果您愿意,您甚至可以扩展它以自动创建父模型,但这需要您还为所有 pydantic 模型指定Meta.orm_model

【讨论】:

  • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。
  • @Tyler2P 包括解决方案。
猜你喜欢
  • 2018-12-23
  • 2016-11-14
  • 2020-07-08
  • 1970-01-01
  • 1970-01-01
  • 2023-03-17
  • 2016-01-10
  • 2021-12-27
  • 2014-12-29
相关资源
最近更新 更多