Django REST Framework 序列化程序中的嵌套注释字段
Posted
技术标签:
【中文标题】Django REST Framework 序列化程序中的嵌套注释字段【英文标题】:Nested annotate fields in Django REST Framework serializers 【发布时间】:2016-01-03 01:36:30 【问题描述】:我正在尝试查看 Django REST Framework 序列化程序中的嵌套注释(聚合/计算)字段。这将允许更干净地处理带注释的字段。这篇文章与Aggregate (and other annotated) fields in Django Rest Framework serializers 类似,但是我想要一种类似的嵌套技术。在方法下方可以看到如何在没有嵌套的情况下工作,以及如何在嵌套情况下不工作。
我知道这可以手动实现(使用 Django 视图)或通过使用使我不感兴趣的数据库重载的方法来实现。但也许有一个高性能且优雅的解决方案来解决这个问题。
以下作品(未嵌套)
模型
class IceCreamCompany(models.Model):
name = models.CharField(max_length=255)
class IceCreamTruck(models.Model):
company = models.ForeignKey('IceCreamCompany', related_name='trucks')
capacity = models.IntegerField()
class IceCreamTruckDriver(models.Model):
name = models.CharField(max_length=255)
first_name = models.CharField(max_length=255)
truck = models.ForeignKey('IceCreamTruck', related_name='drivers')
序列化器
class IceCreamTruckDriverSerializer(serializers.ModelSerializer):
class Meta:
model = IceCreamTruckDriver
fields = ('name', 'first_name')
class IceCreamTruckSerializer(serializers.ModelSerializer):
drivers = IceCreamTruckDriverSerializer(many=True, read_only=True)
class Meta:
model = IceCreamTruck
fields = ('capacity', 'drivers')
class IceCreamCompanySerializer(serializers.ModelSerializer):
trucks = IceCreamTruckSerializer(many=True, read_only=True)
amount_of_trucks = serializers.IntegerField()
class Meta:
model = IceCreamCompany
fields = ('name', 'trucks', 'amount_of_trucks')
视图集
class IceCreamCompanyViewSet(viewsets.ModelViewSet):
queryset = IceCreamCompany.objects.prefetch_related('trucks', 'trucks__drivers')\
.annotate(amount_of_trucks=Count('trucks'))\
.all()
serializer_class = IceCreamCompanySerializer
结果
"results": [
"name": "Pete Ice Cream",
"trucks": [
"capacity": 35,
"drivers": [
"name": "Damian",
"first_name": "Ashley"
,
"name": "Wilfrid",
"first_name": "Lesley"
]
,
"capacity": 30,
"drivers": [
"name": "Stevens",
"first_name": "Joseph"
]
,
"capacity": 30,
"drivers": []
],
"amount_of_trucks": 3
]
以下不起作用(嵌套)
同款
序列化器
class IceCreamTruckDriverSerializer(serializers.ModelSerializer):
class Meta:
model = IceCreamTruckDriver
fields = ('name', 'first_name')
class IceCreamTruckSerializer(serializers.ModelSerializer):
drivers = IceCreamTruckDriverSerializer(many=True, read_only=True)
amount_of_drivers = serializers.IntegerField()
class Meta:
model = IceCreamTruck
fields = ('capacity', 'drivers', 'amount_of_drivers')
class IceCreamCompanySerializer(serializers.ModelSerializer):
trucks = IceCreamTruckSerializer(many=True, read_only=True)
class Meta:
model = IceCreamCompany
fields = ('name', 'trucks')
视图集
class IceCreamCompanyViewSet(viewsets.ModelViewSet):
queryset = IceCreamCompany.objects.prefetch_related('trucks', 'trucks__drivers')\
.annotate(trucks__amount_of_drivers=Count('trucks__drivers'))\
.all()
serializer_class = IceCreamCompanySerializer
结果
AttributeError at /ice/
Got AttributeError when attempting to get a value for field `amount_of_drivers` on serializer `IceCreamTruckSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `IceCreamTruck` instance.
Original exception text was: 'IceCreamTruck' object has no attribute 'amount_of_drivers'.
【问题讨论】:
您不应该将字段命名为 amount_of_drivers 而不是 trucks__amount_of_drivers 吗? 如果我在查询集中的注释函数中使用 amount_of_drivers,它会尝试在 IceCreamCompanySerializer 中查找 amount_of_drivers 字段,该字段不是嵌套的注释字段。我希望它在 IceCreamTruckSerializer 中成为可能。 不,先生,我只是建议重命名注释: .annotate(amount_of_drivers=Count('trucks__drivers'));错误命名了这个问题,在查询集中没有找到带注释的列的名称这一事实。注释列的名称只是一个名称,不会为您遍历关系。 您好,您最终能否在不使用SerializerMethodField
的情况下实现这一目标?
@NicholasColes 是的,我仍然使用自定义视图,这不是最佳的。但是,您可以尝试自定义管理器,如 Campi 在另一个答案中所述。对于我的用例,这种方法会使其过于复杂。我也没有检查查询量。
【参考方案1】:
作为参考,还可以在模型 IceCreamTruck
上注释每辆卡车的司机数量,例如使用自定义管理器:
class AnnotatedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(amount_of_drivers=Count('drivers'))
class IceCreamTruck(models.Model):
company = models.ForeignKey('IceCreamCompany', related_name='trucks')
capacity = models.IntegerField()
objects = AnnotatedManager()
那么您不需要注释视图集,因为amount_of_drivers
已经在trucks
上注释:
class IceCreamCompanyViewSet(viewsets.ModelViewSet):
queryset = IceCreamCompany.objects.prefetch_related('trucks', 'trucks__drivers').all()
serializer_class = IceCreamCompanySerializer
它应该比在序列化器中计数更有效。
【讨论】:
好主意,“number 个驱动程序”也是如此。 ;-)【参考方案2】:我使用 Django REST google 组在 IntegerField 中使用 read_only=True 得到了答案,这有助于消除错误,但该字段不再显示。也许我的注释是错误的。无论如何,我最终在 Django 中使用了自定义视图,因为我最终需要更多数据。但是您可以通过其他方式获取数据:
一个非常优雅的解决方案是删除注释函数并使用可以给我结果的 SerializerMethodField。
但是:这确实会引起很多查询!
同款
序列化器
class IceCreamTruckDriverSerializer(serializers.ModelSerializer):
class Meta:
model = IceCreamTruckDriver
fields = ('name', 'first_name')
class IceCreamTruckSerializer(serializers.ModelSerializer):
drivers = IceCreamTruckDriverSerializer(many=True, read_only=True)
amount_of_drivers = serializers.SerializerMethodField()
def get_amount_of_drivers(self, obj):
return obj.drivers.count()
class Meta:
model = IceCreamTruck
fields = ('capacity', 'drivers', 'amount_of_drivers')
class IceCreamCompanySerializer(serializers.ModelSerializer):
trucks = IceCreamTruckSerializer(many=True, read_only=True)
class Meta:
model = IceCreamCompany
fields = ('name', 'trucks')
视图集
class IceCreamCompanyViewSet(viewsets.ModelViewSet):
queryset = IceCreamCompany.objects.prefetch_related('trucks', 'trucks__drivers').all()
serializer_class = IceCreamCompanySerializer
结果
"results": [
"name": "Pete Ice Cream",
"trucks": [
"capacity": 35,
"drivers": [
"name": "Damian",
"first_name": "Ashley"
,
"name": "Wilfrid",
"first_name": "Lesley"
],
"amount_of_drivers": 2
,
"capacity": 30,
"drivers": [
"name": "Stevens",
"first_name": "Joseph"
],
"amount_of_drivers": 1
,
"capacity": 30,
"drivers": [],
"amount_of_drivers": 0
]
]
也可以像这样在模型中使用函数:Django Rest Framework Ordering on a SerializerMethodField(它在代码本身中可见)但我没有选择它,所以我不必过多地修改我的模型。这也会产生过多的查询。
【讨论】:
以上是关于Django REST Framework 序列化程序中的嵌套注释字段的主要内容,如果未能解决你的问题,请参考以下文章
python django-rest-framework 3.3.3 更新嵌套序列化程序
django-rest-framework、多表模型继承、ModelSerializers 和嵌套序列化器
Django.rest_framework:如何序列化一对多?
Django 序列化器与 rest_framework 序列化器