Search code examples
pythondjangodjango-modelsserializationdjango-rest-framework

Send the values ​of several columns of the table Django rest framework


i want Send the values ​​of several columns of the table ("id", "meter_id", "State", "date", "VII1", "VII2", "VII3", "VII_avg") here is my code:

class VAndI(APIView):
    def get(self, request):
        queryset = PowerMeter.objects.all().order_by('-id')[:5].values("id", "meter_id", "State", "date", "VII1", "VII2", "VII3", "VII_avg")
        serializer = VAndISerializer(instance=queryset, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

but got this ERROR:

'int' object has no attribute 'pk'

here is my serializer:

class DynamicFieldsModelSerializer(serializers.ModelSerializer):
    """
    A ModelSerializer that takes an additional `fields` argument that
    controls which fields should be displayed.
    """

    def __init__(self, *args, **kwargs):
        # Don't pass the 'fields' arg up to the superclass
        fields = kwargs.pop('fields', None)

        # Instantiate the superclass normally
        super().__init__(*args, **kwargs)

        if fields is not None:
            # Drop any fields that are not specified in the `fields` argument.
            allowed = set(fields)
            existing = set(self.fields)
            for field_name in existing - allowed:
                self.fields.pop(field_name)


class VAndISerializer(DynamicFieldsModelSerializer):
    class Meta:
        model = PowerMeter
        fields = ["id", "meter_id", "State", "date", "VII1", "VII2", "VII3", "VII_avg"]

Please note that with this model and serializer, I get the data of all the columns, but in this case, I only want the information of the specified columns.

class VAndI(APIView):
    def get(self, request):
        queryset = PowerMeter.objects.all().order_by('-id')[:5].values("id", "meter_id", "State", "date", "VII1", "VII2", "VII3", "VII_avg")
        serializer = VAndISerializer(instance=queryset, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

serializer:

class PowerMeterSerializer(serializers.ModelSerializer):
    class Meta:
        model = PowerMeter
        fields = '__all__'

Solution

  • Please don't use .values() [django-antipaterns]: .values() provides dictionaries, and other native types, it is a primitive obsession antipattern [refactoring.guru] and will for example for ForeignKeys return their primary key, not the corresponding model item.

    You thus can work with:

    class VAndI(APIView):
        def get(self, request):
            queryset = PowerMeter.objects.order_by('-id')[:5]
            serializer = VAndISerializer(instance=queryset, many=True)
            return Response(serializer.data, status=status.HTTP_200_OK)

    and if meter_id is a ForeignKey, we can add the data in the same query as:

    class VAndI(APIView):
        def get(self, request):
            queryset = PowerMeter.objects.select_related('meter_id').order_by('-id')[
                :5
            ]
            serializer = VAndISerializer(instance=queryset, many=True)
            return Response(serializer.data, status=status.HTTP_200_OK)

    If you want to limit the fields, as your DynamicFieldsModelSerializer seems to want to do, you are supposed to list the allowed fields when constructing the serializer:

    class VAndI(APIView):
        def get(self, request):
            queryset = PowerMeter.objects.select_related('meter_id').order_by('-id')[
                :5
            ]
            serializer = VAndISerializer(
                instance=queryset,
                many=True,
                fields=[
                    'id',
                    'meter_id',
                    'State',
                    'date',
                    'VII1',
                    'VII2',
                    'VII3',
                    'VII_avg',
                ],
            )
            return Response(serializer.data, status=status.HTTP_200_OK)

    Note: In Django, class-based API views (CBV) often have a …APIView suffix, to avoid a clash with the model names. Therefore you might consider renaming the view class to VAndIAPIView, instead of VAndI.