Django REST Framework序列化程序中的嵌套注释字段

Rob*_*bin 2 django django-rest-framework

我正在尝试查看Django REST Framework序列化程序中的嵌套注释(汇总/计算)字段。这样可以更清晰地处理带注释的字段。这篇文章与Django Rest Framework序列化程序中的Aggregate(和其他带注释的)字段相似,但是我希望使用类似的技术来嵌套。在该方法下面可以看到无嵌套的工作方式以及嵌套似乎不起作用的方法。

我知道这可以手动实现(使用Django View),也可以使用使我不感兴趣的数据库过载的方法来实现。但是,也许有一个性能卓越的解决方案可以解决此问题。

以下作品(未嵌套)

楷模

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')
Run Code Online (Sandbox Code Playgroud)

序列化器

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')
Run Code Online (Sandbox Code Playgroud)

视图集

class IceCreamCompanyViewSet(viewsets.ModelViewSet):
    queryset = IceCreamCompany.objects.prefetch_related('trucks', 'trucks__drivers')\
                           .annotate(amount_of_trucks=Count('trucks'))\
                           .all()

    serializer_class = IceCreamCompanySerializer
Run Code Online (Sandbox Code Playgroud)

结果

"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
        }
    ]
Run Code Online (Sandbox Code Playgroud)

以下无效(嵌套)

相同型号

序列化器

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')
Run Code Online (Sandbox Code Playgroud)

视图集

class IceCreamCompanyViewSet(viewsets.ModelViewSet):
    queryset = IceCreamCompany.objects.prefetch_related('trucks', 'trucks__drivers')\
                           .annotate(trucks__amount_of_drivers=Count('trucks__drivers'))\
                           .all()

    serializer_class = IceCreamCompanySerializer
Run Code Online (Sandbox Code Playgroud)

结果

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'.
Run Code Online (Sandbox Code Playgroud)

Cam*_*mpi 7

作为参考,也可以在模型上注释每辆卡车的驾驶员数量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()
Run Code Online (Sandbox Code Playgroud)

然后,您无需注释视图集,因为amount_of_drivers已经对进行了注释trucks:

class IceCreamCompanyViewSet(viewsets.ModelViewSet):
    queryset = IceCreamCompany.objects.prefetch_related('trucks', 'trucks__drivers').all()
    serializer_class = IceCreamCompanySerializer 
Run Code Online (Sandbox Code Playgroud)

它应该比在串行器内部计数更有效。


Rob*_*bin 1

我使用 Django REST google groups 在 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')
Run Code Online (Sandbox Code Playgroud)

视图集

class IceCreamCompanyViewSet(viewsets.ModelViewSet):
    queryset = IceCreamCompany.objects.prefetch_related('trucks', 'trucks__drivers').all()

    serializer_class = IceCreamCompanySerializer
Run Code Online (Sandbox Code Playgroud)

结果

"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
                }
            ]
        }
    ]
Run Code Online (Sandbox Code Playgroud)

也可以在模型中使用函数,如下所示:Django Rest Framework Ordering on a SerializerMethodField(它在代码本身中可见),但我没有选择它,因此我不必过多修改我的模型。这也造成了太多的查询。