Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django rest ModelViewSet filtering objects

I have two models: Library and a Book. A Library will have Many Books.

class Library(models.Model):
    library_id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=30)
    city = models.CharField(max_length=30)
    address = models.CharField(max_length=80)
    phone = models.CharField(max_length=30, blank=True, null=True)
    website = models.CharField(max_length=60, blank=True, null=True)

class Book(models.Model):
    book_id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=30)
    # A library has many books
    which_library = models.ForeignKey('Library', related_name='books', on_delete=models.CASCADE)
    reader = models.ManyToManyField('Reader', related_name='wishlist')

my serializers:

class LibrarySerializer(serializers.ModelSerializer):
    class Meta:
        model = Library
        fields = '__all__'

class BookSerializer(serializers.ModelSerializer):

    wishlist = ReaderSerializer(many=True, read_only=True)

    class Meta:
        model = Book
        fields = '__all__'

And my view:

class LibraryViewSet(viewsets.ModelViewSet):
    queryset = Library.objects.all()
    serializer_class = LibrarySerializer

class BookViewSet(viewsets.ModelViewSet):
    queryset = Book.objects.filter(which_library=library_id)
    serializer_class = BookSerializer

My Urls:

router = routers.DefaultRouter()
router.register(r'libraries', LibraryViewSet)
router.register(r'books/(?P<library_id>[0-9]+)', BookViewSet)

urlpatterns = router.urls

my Library routes work correct. However for Books, I need to return the books only belonging to specific library. The default URL and all() returns all the books in database and can retrieve single by hitting books/1.

How can I achieve this? I could do it previously with :

@api_view(['GET', 'POST'])
def books(request, library_id): 

but now I want to do it with ModelViewSet.

like image 447
Thinker Avatar asked Jan 04 '18 11:01

Thinker


1 Answers

Override get_queryset method of you viewset where you can define to return specific books. This works like this.

class BookViewSet(viewsets.ModelViewSet):

    serializer_class = LibrarySerializer
    def get_queryset(self):
        user = self.request.user
        books = Book.objects.filter(which_library=self.kwargs['library_id'])
        return books

You can also define list and detail methods where you can define your custom queries for Books. This is shown here.

Base name should be set like this.

router.register(r'books/(?P<library_id>[0-9]+)', BookViewSet, base_name='books')
like image 133
Muhammad Hassan Avatar answered Nov 07 '22 18:11

Muhammad Hassan