Please consider these three models:
class Movie(models.Model):
name = models.CharField(max_length=254, unique=True)
language = models.CharField(max_length=14)
synopsis = models.TextField()
class TimeTable(models.Model):
date = models.DateField()
class Show(models.Model):
day = models.ForeignKey(TimeTable)
time = models.TimeField(choices=CHOICE_TIME)
movie = models.ForeignKey(Movie)
class Meta:
unique_together = ('day', 'time')
And each of them has their serializers:
class MovieSerializer(serializers.HyperlinkedModelSerializer):
movie_id = serializers.IntegerField(read_only=True, source="id")
class Meta:
model = Movie
fields = '__all__'
class TimeTableSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = TimeTable
fields = '__all__'
class ShowSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Show
fields = '__all__'
And their routers
router.register(r'movie-list', views.MovieViewSet)
router.register(r'time-table', views.TimeTableViewSet)
router.register(r'show-list', views.ShowViewSet)
Now I would like to get all the TimeTable objects (i.e. date list) by filtering all the Show objects by a specific movie object. This code seems to be the working and getting the list like I want it
m = Movie.objects.get(id=request_id)
TimeTable.objects.filter(show__movie=m).distinct()
But I have no clue how to use this in django rest framework? I tried doing this way (which I am pretty sure its wrong), and I am getting error:
views.py:
class DateListViewSet(viewsets.ModelViewSet, movie_id):
movie = Movie.objects.get(id=movie_id)
queryset = TimeTable.objects.filter(show__movie=movie).distinct()
serializer_class = TimeTableSerializer
urls.py:
router.register(r'date-list/(?P<movie_id>.+)/', views.DateListViewSet)
error:
class DateListViewSet(viewsets.ModelViewSet, movie_id): NameError: name 'movie_id' is not defined
How can I filter using viewsets in django rest framework? Or if there is any other prefered way than please list it out. Thank you.
ModelViewSet
by design assumes that you want to implement a CRUD(create, update, delete)
There is also a ReadOnlyModelViewSet
which implements only the GET
method to read only endpoints.
For Movie
and Show
models, a ModelViewSet
or ReadOnlyModelViewSet
is a good choice whether you want implement CRUD or not.
But a separate ViewSet
for a related query of a TimeTable
which describes a Movie
model's schedule doesn't looks so good.
A better approach would be to put that endpoint to a MovieViewSet
directly. DRF provided it by @detail_route
and @list_route
decorators.
from rest_framework.response import Response
from rest_framework.decorators import detail_route
class MovieViewSet(viewsets.ModelViewset):
queryset = Movie.objects.all()
serializer_class = MovieSerializer
@detail_route()
def date_list(self, request, pk=None):
movie = self.get_object() # retrieve an object by pk provided
schedule = TimeTable.objects.filter(show__movie=movie).distinct()
schedule_json = TimeTableSerializer(schedule, many=True)
return Response(schedule_json.data)
This endpoint will be available by a movie-list/:id/date_list
url
Docs about extra routes
The error
class DateListViewSet(viewsets.ModelViewSet, movie_id): NameError: name 'movie_id' is not defined
happens because movie_id
is being passed as parent class of DataListViewSet and not as parameter as you imagined
This example in the documentation should be what you are looking for.
Adjust your URL:
url(r'date-list/(?P<movie_id>.+)/', views.DateListView.as_view())
Adjust your Model:
class Show(models.Model):
day = models.ForeignKey(TimeTable, related_name='show')
time = models.TimeField(choices=CHOICE_TIME)
movie = models.ForeignKey(Movie)
class Meta:
unique_together = ('day', 'time')
Your view would look like this:
class DateListView(generics.ListAPIView):
serializer_class = TimeTableSerializer
def get_queryset(self):
movie = Movie.objects.get(id=self.kwargs['movie_id'])
return TimeTable.objects.filter(show__movie=movie).distinct()
Another way to do it would be:
Adjust your URL:
router.register(r'date-list', views.DateListViewSet)
Adjust your Model:
class Show(models.Model):
day = models.ForeignKey(TimeTable, related_name='show')
time = models.TimeField(choices=CHOICE_TIME)
movie = models.ForeignKey(Movie)
class Meta:
unique_together = ('day', 'time')
Your view would look like this:
class DateListViewSet(viewsets.ModelViewSet):
serializer_class = TimeTableSerializer
queryset = TimeTable.objects.all()
filter_backends = (filters.DjangoFilterBackend,)
filter_fields = ('show__movie_id')
Which will allow you to make requests such as:
http://example.com/api/date-list?show__movie_id=1
See documentation
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With