[英]Pydantic from_orm to load Django model with related list field
我有以下 Django 模型:
from django.db import models
class Foo(models.Model):
id: int
name = models.TextField(null=False)
class Bar(models.Model):
id: int
foo = models.ForeignKey(
Foo,
on_delete=models.CASCADE,
null=False,
related_name="bars",
)
和 Pydantic 模型(将orm_mode
设置为True
):
from pydantic import BaseModel
class BarPy(BaseModel):
id: int
foo_id: int
class FooPy(BaseModel):
id: int
name: str
bars: list[BarPy]
现在我想对模型Foo
执行查询并将其加载到FooPy
中,所以我写了这个查询:
foo_db = Foo.objects.prefetch_related("bars").all()
pydantic_model = FooPy.from_orm(foo_db)
但它给了我这个错误:
pydantic.error_wrappers.ValidationError: 1 validation error for FooPy bars value is not a valid list (type=type_error.list)
当显式使用FooPy
构造函数并手动分配值时,我能够做到这一点,但我想使用from_orm
。
Foo
模型上的bars
属性是一个ReverseManyToOneDescriptor
,它只为Bar
模型返回一个RelatedManager
。 与 Django 中的任何管理器一样,要获取由它管理的所有实例的查询集,您需要调用它的all
方法。 通常你会做类似foo.bars.all()
的事情。
您可以将自己的自定义验证器添加到FooPy
并使其成为pre=True
以获取所有相关的Bar
实例并将它们的序列传递给默认验证器:
from django.db.models.manager import BaseManager
from pydantic import BaseModel, validator
...
class FooPy(BaseModel):
id: int
name: str
bars: list[BarPy]
@validator("bars", pre=True)
def get_all_from_manager(cls, v: object) -> object:
if isinstance(v, BaseManager):
return list(v.all())
return v
请注意,仅执行.all()
是不够的,因为这将返回一个查询集,该查询集不会通过 Pydantic 模型中内置的默认序列验证器。 你会得到同样的错误。
您需要给它一个实际的序列(例如list
或tuple
)。 QuerySet
不是一个序列,而是一个可迭代对象。 但是您可以使用它并将其变成一个序列,例如调用它的list
。
您可以尝试概括该验证器并将其添加到您自己的 (Pydantic) 基础模型中。 这样的事情应该适用于您注释为list[Model]
的任何字段,其中Model
是pydantic.BaseModel
的子类:
from django.db.models.manager import BaseManager
from pydantic import BaseModel, validator
from pydantic.fields import ModelField, SHAPE_LIST
...
class CustomBaseModel(BaseModel):
@validator("*", pre=True)
def get_all_from_manager(cls, v: object, field: ModelField) -> object:
if not (isinstance(field.type_, type) and issubclass(field.type_, BaseModel)):
return v
if field.shape is SHAPE_LIST and isinstance(v, BaseManager):
return list(v.all())
return v
我还没有彻底测试过这个,但我想你明白了。
值得一提的是prefetch_related
与问题无关。 无论您是否这样做,问题及其解决方案都是一样的。 不同之处在于,如果没有prefetch_related
,您将在调用from_orm
时触发额外的数据库查询,从而执行使用.bars.all()
查询集的验证器。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.