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
4 Answers
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 and
1What 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
1