Post

Django & Django REST Framework Notes

Django & Django REST Framework Notes

Django MVT (Model-View-Template)

Model

  • Represents the data layer of the application.
  • Defines the structure of the database, including tables, fields, and relationships.
  • Handles data storage, retrieval, and validation.
  • Interacts with the database using Django’s ORM (Object-Relational Mapper).

Define Relationships

Many-to-Many

Should be defined on either side of the relationship using ManyToManyField(). (Notice that Category was passed as a string, because the class Category was not defined yet. Django uses a string representation and it’s considered the best practice.)

1
2
3
4
5
6
7
8
9
# One product can belongs to many categories, such as bread -> food & on_sale_product
# One category can also has many products, such as food -> bread & chicken
class Product(models.Model):
    name = models.CharField(max_length=50)
    categories = models.ManyToManyField('Category')

    
class Category(models.Model):
    name = models.CharField(max_length=50)
Many-to-One

Should be defined on the many side of the relationship using ForeignKey()

1
2
3
4
5
6
7
class Order(models.Model):
    order_number = models.CharField(max_length=50)
    customer = models.ForeignKey('Customer', on_delete=models.CASCADE)

    
class Customer(models.Model):
    name = models.CharField(max_length=50)

The kwarg: related_name

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Author(models.Model):
    name = models.CharField(max_length=50)
    
 
# Case 1
class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey('Author', on_delete=models.CASCADE)

author = Author.objects.all(name='Zheng Yuan')
books = author.book_set.all()  
# Default method <class_lowercase>_set

# Case 2
class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey('Author', 
                               related_name='books',
                               on_delete=models.CASCADE)

books = author.books.all()
# Now, the method now becomes the value of related_name
One-to-One

Should be defined on either side of the relationship using OneToOneField()

View

  • Acts as the business logic layer.
  • Processes user requests, interacts with the Model to fetch or manipulate data, and prepares the data for rendering.
  • Returns a (Django’s) HttpResponse, often by rendering a template with the processed data.
  • In Django, views are typically Python functions or classes.

Template

  • Represents the presentation layer.
  • Defines how the data is displayed to the user.
  • Uses Django’s template language to dynamically generate HTML by combining static content with data from the View.
  • Separates the design (HTML/CSS) from the logic (Python code).

Django REST Framework

Serializer

  • Complex datatypes (querysets or model instances) serialize into Python datatypes (then converted into JSON/XML).
  • Incoming JSON/XML data unserialize into Python objects.

View

APIView (Class-based)

  • Returns a (DRF’s) Response
  • Handler methods: get(), post(), put(), delete(), etc.
  • Full control over the request handling process.
  • views.py, decorators.py
1
2
3
4
5
6
7
8
9
10
11
12
13
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status


class MyAPIView(APIView):
    def get(self, request):
        data = {"message": "This is a GET request"}
        return Response(data, status=status.HTTP_200_OK)

    def post(self, request):
        data = {"message": "This is a POST request"}
        return Response(data, status=status.HTTP_201_CREATED)
Function-based
  • @api_view()
  • API policy decorator
  • View schema decorator

GenericAPIView

  • Subclass of APIView
  • Add support for queryset for database queries.
  • Add support for serializer_class to determine which serializer to use.
  • Built-in support for Pagination, Filtering, and Ordering.
  • generics.py, mixins.py
1
2
3
4
5
6
7
8
9
10
11
12
13
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from myapp.models import MyModel
from myapp.serializers import MyModelSerializer


class MyGenericAPIView(GenericAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

    def get(self, request):
        serializer = self.get_serializer(self.get_queryset(), many=True)
        return Response(serializer.data)
Mixin
  • Mixins are used with GenericAPIView and they provide actions rather than handler methods
  • Common mixins:
    • CreateModelMixin: .list()
    • ListModelMixin: .create()
    • RetrieveModelMixin: .retrieve()
    • UpdateModelMixin: .update() and .partial_update()
    • DestroyModelMixin: .destroy()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin
from myapp.models import MyModel
from myapp.serializers import MyModelSerializer


class MyMixinView(ListModelMixin, CreateModelMixin, GenericAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

ViewSets

  • A type of class-based view which provides actions rather than handler methods
  • Bound to the corresponding actions at the point of finalizing the view using .as_view()
  • Register the viewset with a router class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from myapps.serializers import UserSerializer
from rest_framework import viewsets
from rest_framework.response import Response


class UserViewSet(viewsets.ViewSet):
    """
    A simple ViewSet for listing or retrieving users.
    """
    def list(self, request):
        queryset = User.objects.all()
        serializer = UserSerializer(queryset, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk=None):
        queryset = User.objects.all()
        user = get_object_or_404(queryset, pk=pk)
        serializer = UserSerializer(user)
        return Response(serializer.data)
1
2
3
4
5
6
7
from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouter


router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
urlpatterns = router.urls
This post is licensed under CC BY 4.0 by the author.