View sets and Mixins

ViewSets

1) View sets are used when dealing with multiple instances of a model. They are used when we intend to query or filter model objects

2) We will create an API that does not include the user details in it, but lists tags corresponding to a specific user. That is, the following API

/api/recipe/tags

can be used by all users to get their tags and also post new tags

Mixins

Mixins are extensions that can be added to genericViewSet to allow for different requests. We ll discuss more about writing custom code for each request in section 18

1) CreateModelMixin allows for POST requests

2) ListModelMixin allows a GET request for all the objects in the model

3) RetrieveModelMixin allows for GET request with specific primary key (URL requires PK, Section 18)

4) UpdateModelMixin allows for PUT requests (URL requries PK)

5) DestroyModelMixin for DELETE requests (URL requires PK)

To have a class which has all these mixins, extend from ModelViewSet (Section 18)

Demonstration : What should our API do?

This is demonstrated with our new Tag model. We ll create an API that will,

1) Let the authenticated user create new tag (add CreateModelMixin to genericViewSet extension)

2) Let the authenticated user list the tags created by that user (add ListModelMixin to genericViewSet extension)

3) To get a specific tag, add RetrieveModelMixin. This will allow for urls

/api/recipe/tags/1

Tests are available in next section

Create serializer

from rest_framework import serializers
from core.models import Tag


class TagSerializer(serializers.ModelSerializer):
    """Serializer for tag object"""

    class Meta:
        model = Tag
        fields = ('id', 'name')
        read_only_fields = ('id',)

    # create is not implemented here because
    # the user which is needed to create Tag is not defined here

Create View

We ll use viewsets, which can then be used by a router to map to corresponding user

API GET call to this view should list tags created by the authenticated user (Use ListModelMixin and define queryset)

API POST call to this view should create new tag (Use CreateModelMixin and define perform_create)

from rest_framework import viewsets, mixins
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated

from core.models import Tag
from recipe import serializers


# viewset is used when dealing with multiple instances of a model.
# viewset is used when we intend to query or filter model objects
# /api/recepi/tags/ : mapped separately for each user
class TagViewSet(viewsets.GenericViewSet,
                 mixins.ListModelMixin,
                 mixins.CreateModelMixin):
    """manage tags in the database"""
    authentication_classes = (TokenAuthentication,)
    permission_classes = (IsAuthenticated,)

    queryset = Tag.objects.all()
    serializer_class = serializers.TagSerializer

    # This method should be overriden
    # if we dont want to modify query set based on current instance attributes
    def get_queryset(self):
        return self.queryset.filter(user=self.request.user).order_by('-name')

    # override this method for CreateModelMixin
    # create operation is done here (unlike in UserModelSerializer)
    # because serializer does not have user
    # we pass user to serializer and save it
    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

Map urls

Use rest_framework.routers.DefaultRouter to map URLs

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from recipe import views

app_name = 'recipe'

# mapping the user to corresponding viewset
router = DefaultRouter()
#reverse url is 'recipe:tag-list'
router.register('tags', views.TagViewSet)

urlpatterns = [
    path('', include(router.urls)),
]