简体   繁体   中英

Django REST framework foreign keys and filtering

I have following models in django app:

models.py :

class Make(BaseModel):
    slug = models.CharField(max_length=32) #alfa-romeo
    name = models.CharField(max_length=32) #Alfa Romeo

    def __unicode__(self):
        return self.name

class Model(BaseModel):
    make = models.ForeignKey(Make)  #Alfa Romeo
    name = models.CharField(max_length=64) # line[2]
    engine_capacity = models.IntegerField()
    trim = models.CharField(max_length=128) # line[4]

And serializers.py :

from .models import Make,Model
from rest_framework import serializers


class MakeSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Make
        fields = ('url', 'slug', 'name')


class ModelSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Model
        fields = ('url', 'make', 'name', 'trim', 'engine_capacity')

and also views.py :

from rest_framework import viewsets
from rest_framework import filters
from rest_framework import generics

from .models import Make, Model
from .serializers import MakeSerializer, ModelSerializer


class MakeViewSet(viewsets.ModelViewSet):
    queryset = Make.objects.all()
    serializer_class = MakeSerializer
    filter_backends = (filters.DjangoFilterBackend,)

class ModelViewSet(viewsets.ModelViewSet):
    make = MakeSerializer
    queryset = Model.objects.all()
    serializer_class = ModelSerializer
    filter_backends = (filters.DjangoFilterBackend,)

What I need to to, I want to fetch all Models manufactured by specific make. How can I get all models with particular make foreign key using query params? And my 2nd question - can I filter results using queryparams to get models with specific engine_capacity?

One comment: It would be perfect, if I can to query results using something like this in url: /api/models/?make=ford where make is slug field in Make model

You can specify filter_fields = ('make__slug', ) in your view set. Don't forget to include filter_backends = (DjangoFilterBackend, ) as well. Also you will need to add django-filter dependency.

class ModelViewSet(viewsets.ModelViewSet):
    queryset = Model.objects.all()
    serializer_class = ModelSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filter_fields = ('make__slug',)

Then you query like /api/models/?make__slug=ford . Note double underscore symbol.

Docs .

If you don't like make__slug keyword argument in the URL, then you can create a filter class:

import django_filters

from myapp.models import Make


class ModelFilter(django_filters.FilterSet):
    make = django_filters.ModelChoiceFilter(field_name="make__slug",
                                            queryset=Make.objects.all())

    class Meta:
        model = Model
        fields = ('make',)

and then

class ModelViewSet(viewsets.ModelViewSet):
    make = MakeSerializer
    queryset = Model.objects.all()
    serializer_class = ModelSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filter_class = ModelFilter

/api/models/?make=ford should work.

urls.py

url('^model/by/(?P<make>\w+)/$', ModelByMakerList.as_view()),

views.py

class ModelByMakerList(generics.ListAPIView):
    serializer_class = ModelSerializer

    def get_queryset(self):
        """
        This view should return a list of all models by
        the maker passed in the URL
        """
        maker = self.kwargs['make']
        return Model.objects.filter(make=maker)

For more info checkout the docs .

You can also use filtering with QUERY_PARAMS, but IMHO this looks better.

To expand on @vladimir-prudnikov's answer :

Things changed a bit in recent versions of django-filter. You probably want:

class ModelFilter(django_filters.FilterSet):
    make = django_filters.ModelChoiceFilter(field_name='make__slug',
                                            to_field_name='slug',
                                            queryset=Make.objects.all())

    class Meta:
        model = Model
        fields = ('make',)

See https://django-filter.readthedocs.io/en/master/ref/filters.html#field-name and https://django-filter.readthedocs.io/en/master/ref/filters.html#to-field-name

What you need to do in your view is something like this: It is called "Lookups that span relationships"

queryset = Model.objects.filter(make__name__exact='Alfa Romeo')

the filtering of models with specific engine capacity is similar

queryset = Model.objects.filter(engine_capacity__exact=5)

if you want both filters combined, you can chain them:

queryset = Model.objects.filter(make__name__exact='Alfa Romeo').filter(engine_capacity__exact=5)

more examples can be found here django query making

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