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 thekwarg
. -
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}
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
Register the app in settings.py
INSTALLED_APPS = [$APP_NAME, ...]
Make super user (admin)
python3 manage.py makesuperuser
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
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?>
Filters
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
Signals
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):
...
DRF
Serialization
Serialization is the following data flow sequence:
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)
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
API Views
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})
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})
Validation
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
Global: DEFAULT_PERMISSIONS_LEVEL
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
]
}
Specific views only
- for @api_view, add decorator
@permission_classes([])
- for class-based view, add variable
permission_classes = [ ]
Pagination
Global: DEFAULT_PAGINATION_CLASS
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 3
}
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
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": [
…
]
}
Authentication
Global: DEFAULT_AUTHENTICATION_CLASSES
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
]
}
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)
]
which returns the response below if given a valid username and password
{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }
Top comments (0)