CodeNewbie Community 🌱

Cover image for Django Rest Framework Beginner Cheatsheet
Estee Tey
Estee Tey

Posted on • Edited on

Django Rest Framework Beginner Cheatsheet

Hello there! This cheatsheet is written by a user who just learnt Django & DRF recently, for other users who are also relatively new to Django Rest Framework (DRF) like @jasonbraganza. Do check out his series on his learning journey for Django😄

Join in the fun with #CNC2021 if you are not part of it already!


Table of Contents


Python 🐍

Keyword and positional arguments

  • Keyword argument: kwarg=value
  • Example: def function(name='hehe') , name is the kwarg.
  • In a function call, keyword arguments must follow positional arguments.

    def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
          -----------    ----------     ----------
            |             |                  |
            |        Positional or keyword   |
            |                                - Keyword only
             -- Positional only
    

Retrieved from Python 3.9.4 documentation

*args, **kwargs

>>> def functionA(*a, **kw):
       print(a)
       print(kw)

>>> functionA(1, 2, 3, 4, 5, 6, a=2, b=3, c=5)
(1, 2, 3, 4, 5, 6)
{'a': 2, 'c': 5, 'b': 3}

>>> lis=[1, 2, 3, 4]
>>> dic={'a': 10, 'b':20}
>>> functionA(*lis, **dic)
(1, 2, 3, 4)
{'a': 10, 'b': 20}
Enter fullscreen mode Exit fullscreen mode

Retrieved from Stackoverflow Post on What do * and ** before a variable name mean in a function signature?


Django

Basic commands

Make an app

djangoadmin startapp $APP_NAME
Enter fullscreen mode Exit fullscreen mode

Register the app in settings.py

INSTALLED_APPS = [$APP_NAME, ...]
Enter fullscreen mode Exit fullscreen mode

Make super user (admin)

python3 manage.py makesuperuser
Enter fullscreen mode Exit fullscreen mode

Make migrations and perform migrations

Migration file created will be named like $APP_NAME/migrations/001.py

python3 manage.py makemigration $APP_NAME
python3 manage.py migrate $APP_NAME
Enter fullscreen mode Exit fullscreen mode

Using Django Shell to get Model Object Instance

Getters

>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]> # if model does not have __str__

>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> # if related model has __str__

>>> Question.objects.get(pk=1)
<Question: What's up?> 
Enter fullscreen mode Exit fullscreen mode

Filters

>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
Enter fullscreen mode Exit fullscreen mode

Signals

Docs

from django.db.models.signals import pre_save
from django.dispatch import receiver
from myapp.models import MyModel

@receiver(pre_save, sender=MyModel)
def my_handler(sender, **kwargs):
    ...
Enter fullscreen mode Exit fullscreen mode

DRF

Serialization

Serialization is the following data flow sequence:

Serialization Data Flow

In Code,

from rest_framework import serializers

class Comment:
    def __init__(self, email, content, created=None):
        self.email = email
        self.content = content
        self.created = created or datetime.now()

class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

# Create the Model Instance
comment = Comment(email='leila@example.com', content='foo bar')

# Serialize the data and store it in native python data type
serializer = CommentSerializer(comment) 

# Render the native python data type into JSON
json = JSONRenderer().render(serializer.data)
Enter fullscreen mode Exit fullscreen mode

Deserialization

The reverse of serialization

import io
from rest_framework.parsers import JSONParser

# Translate the JSON into a parsable stream
stream = io.BytesIO(json) 

# Parse the stream 
data = JSONParser().parse(stream)
serializer = CommentSerializer(data=data)

# Check if serialized data is valid, if yes then do something with it
if (serializer.is_valid()):
 validated_data = serializer.validated_data
Enter fullscreen mode Exit fullscreen mode

API Views

Docs

API Views are split into Functional decorators @api_view & Class-based APIView

Functional decorator with @api_view

from rest_framework.views import APIView
from rest_framework.response import Response

@api_view(['GET', 'POST']) # add other methods as necessary
def hello_world(request):
  if request.method == 'GET':
   return Response({"message": "Hello, world!"})
    else if request.method == 'POST':
        return Response({"message": "Got some data!", "data": request.data})
Enter fullscreen mode Exit fullscreen mode

Class-based view with APIView

class HelloWorld(APIView):
    def get(self, request):
        return Response({"message": "Hello, world!"})
  def post(self, request):
    return Response({"message": "Got some data!", "data": request.data})
Enter fullscreen mode Exit fullscreen mode

Validation

Docs

Field level validation and object level validation

Field level validation relies on the declaration of specific fields within a model class, while object level validation relies on the use of serializers and then checking whether serializer.is_valid().

Permissions

Docs

Global: DEFAULT_PERMISSIONS_LEVEL

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ]
}
Enter fullscreen mode Exit fullscreen mode

Specific views only

  • for @api_view, add decorator @permission_classes([])
  • for class-based view, add variable permission_classes = [ ]

Pagination

Docs

Global: DEFAULT_PAGINATION_CLASS

 REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 3
}
Enter fullscreen mode Exit fullscreen mode

Specific views only: make a class that extends PageNumberPagination

Also, for APIs that you intend to add pagination, you should make sure that the items are ordered, otherwise it may yield inconsistent results.

class StandardResultsSetPagination(PageNumberPagination):
    page_size = 100
    page_size_query_param = 'page_size'
    max_page_size = 1000

class BillingRecordsView(generics.ListAPIView):
    queryset = Billing.objects.all().order_by("-id")
    serializer_class = BillingRecordsSerializer
    pagination_class = StandardResultsSetPagination
Enter fullscreen mode Exit fullscreen mode

After adding pagination, the response will look like the below

{
    "count": 1023
    "next": "https://api.example.org/accounts/?page=5",
    "previous": "https://api.example.org/accounts/?page=3",
    "results": [
       
    ]
}
Enter fullscreen mode Exit fullscreen mode

Authentication

Docs

Global: DEFAULT_AUTHENTICATION_CLASSES

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ]
}
Enter fullscreen mode Exit fullscreen mode

Specific Views only

  • @api_view: add decorator @authentication_classes([])
  • Class-based view: add variable authentication_classes = []

If using token authentication, DRF provides a default view

from rest_framework.authtoken import views
urlpatterns += [
    path('api-token-auth/', views.obtain_auth_token)
]
Enter fullscreen mode Exit fullscreen mode

which returns the response below if given a valid username and password

{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }
Enter fullscreen mode Exit fullscreen mode

Top comments (0)