简体   繁体   中英

DRF queryset to return specific field

I'm creating a django rest framework application with this structure (assuming imports are correct, so I omit them from the code below.

models.py:

class Door(models.Model):
  type = models.CharField(max_length=40)
  color = models.CharField(max_length=40)

serializers.py:

class DoorSerializer(serializers.ModelSerializer):
  class Meta:
    model = Door
    fields = ['type', 'color']

views.py:

class DoorViewSet(viewsets.ModelViewSet):
  serializer_class = DoorSerializer
  queryset = Door.objects.all()
  def get_queryset(self, *args, **kwargs):
    queryset = Door.objects.all()
    parameter = self.request.query_params.get('type', '')
    if parameter:
      return queryset.filter(type=parameter)
    else:
      return queryset

So far this behaves as intended, when I make an api call to localhost/Doors it lists all the doors. And when I make an api call to localhost/Doors/?type=big it lists all the doors that have the value "big" in their "type" field.

The addition I would like to make is another parameter check which would return a list of all the unique door types that exist in the database. This can be achieved in the manage.py shell by using: Door.objects.all().values('type').distinct()

My attempt was the following modifications to views.py:

...
parameter = self.request.query.params.get('type', '')
unique = self.request.query.params.get('unique', '')
if parameter:
  ...
elif unique:
  return Door.objects.all().values('type').distinct()
...

My assumption was that this would return the same as Door.objects.all().values('type').distinct() when I make a call to localhost/Doors/?unique=whatever

However I am getting the error: "Got KeyError when attempting to get a value for field color on serializer DoorSerializer .\nThe serializer field might be named incorrectly and not match any attribute or key on the dict instance.\nOriginal exception text was: 'color'."

I assume this means that the serializer expects an object or a list of objects that contains all the fields of the corresponding model.

Is there some way I could circumvent this by fixing the view or should I create a different serializer? In either case, since I've gotten pretty confused with DRF / django differences and it is possible I won't be able to follow abstract instructions, could you provide a code solution that addresses the issue? Also, in the very likely case that my assumption is completely off, could you also explain what is causing the problem? Thank you for your time!

Edit for clarifying the desired result:

Assuming my database has 4 doors which are:

{
  "id": 1,
  "type": "big",
  "color": "blue"
},
{
  "id": 2,
  "type": "big",
  "color": "yellow"
},
{
  "id": 3,
  "type": "small",
  "color": "green"
},
{
  "id": 4,
  "type": "big",
  "color": "red"
},

I would like to make a get request to some url, for instance localhost/Doors/?unique=Yes and have the api return to me the list {"big", "small}

  1. WRITING YOUR OWN VIEW: Short view that returns the list of type. You need to set up a new path here. I'd personally go for this option as the response you expect is way different to what the rest of your view does.
from rest_framework.decorators import api_view
from rest_framework.response import Response

@api_view()
def Unique_door_types(request):
   types = Door.objects.values_list('type', flat=True).distinct()
   return Response({"types": list(types)})
  1. WITHOUT AN ADDITIONAL VIEW:

No need for additional view or serializer. Override the list method. Note that this is closer to a trick than to a good way of programming.

from rest_framework.response import Response

class DoorViewSet(viewsets.ModelViewSet):
    serializer_class = DoorSerializer
    
    def get_queryset(self, *args, **kwargs):
        queryset = Door.objects.all()
        parameter = self.request.query_params.get('type', '')
        if parameter:
            return queryset.filter(type=parameter)
        else:
            return queryset

    def list(self, request):
        unique = self.request.query_params.get('unique', '')
        if unique:
            types = Door.objects.values_list('type', flat=True).distinct()
            return Response({"types": list(types)})
        return super().list()

My suggestion would be to create a separate route like /doors/types/ . You do this by adding a method to your DoorViewSet class with a @action decorator. See https://www.django-rest-framework.org/api-guide/viewsets/#marking-extra-actions-for-routing for more details about how to do this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM