2015-10-06 3 views
0

Я пытаюсь просмотреть вложенные аннотатные (агрегированные/вычисленные) поля в сериализаторах Django REST Framework. Это позволит работать более чисто с аннотированными полями. Это сообщение похоже на Aggregate (and other annotated) fields in Django Rest Framework serializers, но я хотел бы, чтобы подобный метод работал вложенным. Ниже методология видна, как это работает без вложенности и как она, похоже, не работает с вложением.Вложенные аннотированные поля в Django REST Framework serializers

Я знаю, что это может быть достигнуто вручную (с помощью 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') 

сериализаторы

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') 

Viewset

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') 

Viewset

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'. 
+0

Разве вы не должны указывать поле типа amount_of_drivers вместо truck__amount_of_drivers? – Roba

+0

Если я использую amount_of_drivers в функции аннотаций в наборе запросов, он пытается найти поле amount_of_drivers в IceCreamCompanySerializer, которое не будет вложенным аннотированным полем. Я хочу, чтобы это было возможно внутри IceCreamTruckSerializer. – Robin

+0

Нет, сэр, я предлагаю переименовать аннотацию: .annotate (amount_of_drivers = Count ('trucks__drivers')); ошибка называет эту проблему, тот факт, что имя аннотированного столбца не было найдено в наборе запросов. Имя аннотированного столбца - просто имя, не пересекающее отношения для вас. – Roba

ответ

1

я получил ответ, используя группы Django REST Google использовать read_only = True внутри IntegerField, что помогло удаление ошибки но тогда поле больше не отображалось. Возможно, моя аннотация была неправильной. В любом случае, я закончил использование пользовательского представления в 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') 

Viewset

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 (это видно в самом коде), но я не выбирал его, так что я не должен изменить мои модели слишком много. Это также вызывает слишком много запросов.