Django Cheatsheet
Table Of Contents
- Django Cheatsheet
- Table Of Contents
- Django-Admin Commands
- Middleware
- CSRF Protection
- Handwrite Notes
- Forms
- Authentication
- Django Admin
HttpRequest- Http Method Decorators
- Generic Views
- Cursor Pagination
- URL Dispatcher
- Redirect
- Flash Messages
- Template Language
- Models
- Transaction
- Django Rest Framework
- Django Environment
- django-environ
Django-Admin Commands
Built-in Commands
django-admin startproject mysite: Create a project namedmysitepython manage.py runserver: Run the serverpython manage.py startapp polls: Create an app namedpollspython manage.py makemigrations polls: Create migrations forpollsapppython manage.py migrate: Apply migrationspython manage.py createsuperuser: Create a superuserpython manage.py shell: Open a shellpython manage.py collectstatic: Collect static files intoSTATIC_ROOTpython manage.py makemigrations --settings=toolbox.settings.dev: Change settings file
Custom Commands
Add management/commands directory to the application. _ modules will be ignored.
polls/
__init__.py
models.py
management/
__init__.py
commands/
__init__.py
_private.py
closepoll.py
tests.py
views.py
Add this sample class:
from django.core.management.base import BaseCommand, CommandError
class Command(BaseCommand):
help = "Closes the specified poll for voting"
def add_arguments(self, parser):
# Positional arguments
parser.add_argument("poll_ids", nargs="+", type=int)
# Named (optional) arguments
parser.add_argument(
"--delete",
action="store_true",
help="Delete poll instead of closing it",
)
def handle(self, *args, **options):
for poll_id in options["poll_ids"]:
try:
poll = Poll.objects.get(pk=poll_id)
except Poll.DoesNotExist:
raise CommandError('Poll "%s" does not exist' % poll_id)
poll.opened = False
poll.save()
self.stdout.write(
self.style.SUCCESS('Successfully closed poll "%s"' % poll_id)
)
Middleware
Middlewares are like decorators.
def simple_middleware(get_response):
# One-time configuration and initialization.
def middleware(request):
# Code to be executed for each request before
# the view (and later middleware) are called.
# Change request object. e.g. request.profile = Profile.objects.get(user=request.user.id)
response = get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
return middleware
CSRF Protection
-
The CSRF middleware is activated by default in the MIDDLEWARE setting. If you override that setting, remember that
django.middleware.csrf.CsrfViewMiddlewareshould come before any view middleware that assume that CSRF attacks have been dealt with.If you disabled it, which is not recommended, you can use
csrf_protect()on particular views you want to protect (see below). - In any template that uses a POST form, use the csrf_token tag inside the
<form>element if the form is for an internal URL, e.g.:<form method="post">{% csrf_token %}This should not be done for POST forms that target external URLs, since that would cause the CSRF token to be leaked, leading to a vulnerability.
- In the corresponding view functions, ensure that
RequestContextis used to render the response so that{% csrf_token %}will work properly. If you’re using therender()function, generic views, or contrib apps, you are covered already since these all useRequestContext.
Setting the token on the AJAX request
const request = new Request(
/* URL */,
{
method: 'POST',
headers: {'X-CSRFToken': csrftoken},
mode: 'same-origin' // Do not send CSRF token to another domain.
}
);
fetch(request).then(function(response) {
// ...
});
Dynamic Forms
If your view is not rendering a template containing the csrf_token template tag, Django might not set the CSRF token cookie. This is common in cases where forms are dynamically added to the page. To address this case, Django provides a view decorator which forces setting of the cookie: ensure_csrf_cookie().
Decorators
csrf_exempt(view): This decorator marks a view as being exempt from the protection ensured by the middleware.csrf_protect(view): Decorator that provides the protection of CsrfViewMiddleware to a view.requires_csrf_token(view): Normally the csrf_token template tag will not work if CsrfViewMiddleware.process_view or an equivalent like csrf_protect has not run. The view decorator requires_csrf_token can be used to ensure the template tag does work. This decorator works similarly to csrf_protect, but never rejects an incoming request.ensure_csrf_cookie(view): This decorator forces a view to send the CSRF cookie.
Handwrite Notes
-
CORS: When site A wants to access content from another site B, it is called a Cross-Origin request. As it is disabled for security reasons, B sends anAccess-Control-Allow-Originheader in the response. By default, a domain is not allowed to access an API hosted on another domain. TheOriginheader should be in request.django-cors-headers is a Django application for handling the server headers required for Cross-Origin Resource Sharing (CORS).
-
How to correctly set a user password:
def save(self, commit=True): user = super().save(commit=False) user.set_password(self.cleaned_data['password']) if commit: user.save() return user
Forms
How to write a minimal form in Django
<!-- polls/templates/polls/detail.html -->
<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
<fieldset>
<legend><h1>{{ question.question_text }}</h1></legend>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
{% for choice in question.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
{% endfor %}
</fieldset>
<input type="submit" value="Vote">
</form>
# polls/views.py
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from .models import Choice, Question
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
# Redisplay the question voting form.
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice.",
})
else:
selected_choice.votes += 1
selected_choice.save()
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
Above codes are available in gists
A simple form in Django
# forms.py
from django import forms
class NameForm(forms.Form):
your_name = forms.CharField(label='Your name', max_length=100)
<!-- index.html -->
<form action="/your-name/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
# views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import NameForm
def get_name(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'name.html', {'form': form})
Above codes are available in gists
Bound and unbound form instances
- An unbound form has no data associated with it. When rendered to the user, it will be empty or will contain default values.
- A bound form has submitted data, and hence can be used to tell if that data is valid. If an invalid bound form is rendered, it can include inline error messages telling the user what data to correct.
Access form values
v = form.cleaned_data['my_form_field_name']
Custom Validators
A validator is a callable object or function that takes a value and returns nothing if the value is valid or raises a ValidationError if not
slug = forms.CharField(validators=[validators.validate_slug])
The clean_<fieldname>() method is called on a form subclass – where <fieldname> is replaced with the name of the form field attribute. This method does any cleaning that is specific to that particular attribute, unrelated to the type of field that it is. This method is not passed any parameters. You will need to look up the value of the field in self.cleaned_data and remember that it will be a Python object at this point, not the original string submitted in the form (it will be in cleaned_data because the general field clean() method, above, has already cleaned the data once).
#forms.py
from django.forms import ModelForm, ValidationError # Import ValidationError
from .models import Palindrome
class PalindromeForm(ModelForm):
def clean_word(self):
# Get the user submitted word from the cleaned_data dictionary
data = self.cleaned_data["word"]
# Check if the word is the same forward and backward
if data != "".join(reversed(data)):
# If not, raise an error
raise ValidationError("The word is not a palindrome")
# Return data even though it was not modified
return data
class Meta:
model = Palindrome
fields = ["word"]
ModelForm
class ArticleForm(ModelForm):
class Meta:
model = Article
fields = ['pub_date', 'headline', 'content', 'reporter']
To save the form, call form.save(). This will return the created object.
If we want to not immediately save the object to the database, we can call form.save(commit=False). This will return an object that hasn’t yet been saved to the database. This is useful if we want to do custom processing on the object before saving it, or if we want to use one of the specialized model saving options.
save_m2m() is another method that is provided by ModelForm that gives us the ability to save the many-to-many relationships along with the object.
Authentication
Authentication Settings
-
AUTH_USER_MODEL Default:
auth.User -
LOGIN_REDIRECT_URL Default:
/accounts/profile/ -
LOGIN_URL Default:
/accounts/login/ -
LOGOUT_REDIRECT_URL Default:
None
Authentication Views
urlpatterns = [
path("accounts/", include("django.contrib.auth.urls")),
]
This will include the following URL patterns:
accounts/login/ [name='login']
accounts/logout/ [name='logout']
accounts/password_change/ [name='password_change']
accounts/password_change/done/ [name='password_change_done']
accounts/password_reset/ [name='password_reset']
accounts/password_reset/done/ [name='password_reset_done']
accounts/reset/<uidb64>/<token>/ [name='password_reset_confirm']
accounts/reset/done/ [name='password_reset_complete']
If you want more control over your URLs, you can reference a specific view in your URLconf:
urlpatterns = [
path(
"change-password/",
auth_views.PasswordChangeView.as_view(template_name="change-password.html"),
),
]
is_authenticated
if request.user.is_authenticated:
# Do something for authenticated users.
else:
# Do something for anonymous users.
How to log a user in
To log a user in, from a view, use login(). It takes an HttpRequest object and a User object. login() saves the user’s ID in the session, using Django’s session framework.
from django.contrib.auth import authenticate, login
def my_view(request):
username = request.POST["username"]
password = request.POST["password"]
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
# Redirect to a success page.
...
else:
# Return an 'invalid login' error me
How to log a user out
def logout_view(request):
logout(request)
# Redirect to a success page.
Decorators
login_required(): If the user isn’t logged in, redirect tosettings.LOGIN_URL, passing the current absolute path in the query string. Example:@login_required(login_url='/accounts/login/')user_passes_test(): Decorator for views that checks that the user passes the given test, redirecting to the log-in page if necessary. The test should be a callable that takes the user object and returns True if the user passes.from django.contrib.auth.decorators import user_passes_test @user_passes_test(lambda user: user.is_staff) def staff_place(request): return HttpResponse("Employees must wash hands", content_type="text/plain")
Django Admin
Register Models
from django.contrib import admin
from core.models import Blog
@admin.register(Blog)
class BlogAdmin(admin.ModelAdmin):
pass
Tip: You can use UserAdmin for the User model.
Customizing Models
sortable_by: Add sort option by clicking on the columns.list_filterlist_editablesearch_fieldslist_displaylist_display_links: Add object detail admin link to other display fieldsreadonly_fieldsinlinesactionslist_editablereadonly_fieldsform: Custom model formfieldsetsclass FlatPageAdmin(admin.ModelAdmin): fieldsets = [ ( None, { "fields": ["url", "title", "content", "sites"], }, ), ( "Advanced options", { "classes": ["collapse"], "fields": ["registration_required", "template_name"], }, ), ]
Adding the ordering attribute will default all queries on Person to be ordered by last_name then first_name.
class Person(models.Model):
# ...
class Meta:
ordering = ("last_name", "first_name")
# ...
It can also reference a method in the admin.ModelAdmin itself:
@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
list_display = ("last_name", "first_name", "show_average")
def show_average(self, obj):
from django.db.models import Avg
result = Grade.objects.filter(person=obj).aggregate(Avg("grade"))
return result["grade__avg"]
show_average() is called once for each object displayed in the list.
Adding Search Box
Anything the user types in the search box is used in an OR clause of the fields filtering the QuerySet. By default, each search parameter is surrounded by % signs. You can be more precise by specifying a __ modifier on the search field.
@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
search_fields = ("last_name__startswith", )
Inlines
# OR use -> admin.StackedInline
class BookInline(admin.TabularInline):
model = Book
extra = 3 # Number of empty slots
class AuthorAdmin(admin.ModelAdmin):
inlines = [BookInline]
For many-to-many relations we should use this:
class Film(Model):
director = ManyToManyField('Director')
class DirectorInline(admin.TabularInline):
model = Film.director.through
class FilmAdmin(admin.ModelAdmin):
inlines = (
DirectorInline,
)
Actions
class ArticleAdmin(admin.ModelAdmin):
actions = ["make_published"]
@admin.action(description="Mark selected stories as published")
def make_published(self, request, queryset):
queryset.update(status="p")
HttpRequest
| Attribute | Description | Examples |
|---|---|---|
| scheme | URL scheme | “http” or “https” |
| path | Path portion of the URL | “/music/bands/” |
| method | HTTP method used | “GET” or “POST” |
| GET | Query string parameters | <QueryDict: {'band_id':['123']}> |
| POST | Fields from an HTTP POST | <QueryDict: {'name':['Bob']}> |
| user | Object describing the user |
Http Method Decorators
The decorators in django.views.decorators.http can be used to restrict access to views based on the request method. These decorators will return a django.http.HttpResponseNotAllowed if the conditions are not met.
require_http_methods(request_method_list)from django.views.decorators.http import require_http_methods @require_http_methods(["GET", "POST"]) def my_view(request): # I can assume now that only GET or POST requests make it this far passrequire_GET()require_POST()-
require_safe()Decorator to require that a view only accepts the
GETandHEADmethods. These methods are commonly considered “safe” because they should not have the significance of taking an action other than retrieving the requested resource.
Generic Views
- CreateView: Needs absolute url of detail view
- ListView
- DetailView
- UpdateView
- DeleteView
- FormView
Attributes and Methods
model: The model that this view will display data for. Specifyingmodel = Publisheris effectively the same as specifyingqueryset = Publisher.objects.all().form_class: The form class to use when instantiating the form.template_nameextra_contextform_valid: Called when a valid form is POSTed. It should return anHttpResponse.get_context_data: Returns a dictionary representing the template context. It takes an existing context dictionary (as returned bysuper().get_context_data(**kwargs)) and adds new context variables to it.success_url: The URL to redirect to after processing a valid form. This takes precedence overget_success_url().pk_url_kwarg
Cursor Pagination
URL Dispatcher
from news import views
path("archive/", views.archive, name="news-archive")
you can use any of the following to reverse the URL:
# using the named URL
reverse("news-archive")
Redirect
The arguments could be redirect():
- A model: the model’s
get_absolute_url()function will be called. - A view name, possibly with arguments:
reverse()will be used to reverse-resolve the name. - An absolute or relative URL, which will be used as-is for the redirect location.
Flash Messages
Add and use message
To add a message, call:
from django.contrib import messages
messages.add_message(request, messages.INFO, "Hello world.")
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
Message_TAGS
Default:
{
messages.DEBUG: "debug",
messages.INFO: "info",
messages.SUCCESS: "success",
messages.WARNING: "warning",
messages.ERROR: "error",
}
Override:
from django.contrib.messages import constants as message_constants
MESSAGE_TAGS = {message_constants.INFO: ""}
Template Language
Filter
{{ value|default:"hello" }}- lower
- truncateword
- filesizeformat
- join:
{{ my_list|join:", " }}
Tip
<h1>Number of posts: {{ posts.count }}</h1>
<h1>Number of posts: {{ posts|length }}</h1>
Actually, in this very specific case, using the length template filter - which just calls len() - would be more efficient. That’s because calling .count() on a queryset that hasn’t been evaluated causes it to go back to the database to do a SELECT COUNT, whereas len() forces the queryset to be evaluated.
Custom Template Tags and Filters
First Step
polls/
__init__.py
models.py
templatetags/
__init__.py
poll_extras.py
views.py
Load Modules
In every templates that we want to use our custom tags:
{% load poll_extras %}
Register
To be a valid tag library, the module must contain a module-level variable named register that is a template.Library instance, in which all the tags and filters are registered. So, near the top of your module, put the following:
from django import template
register = template.Library()
Custom filters are Python functions that take one or two arguments:
- The value of the variable (input) – not necessarily a string.
- The value of the argument – this can have a default value, or be left out altogether.
For example, in the filter
{{ var|foo:"bar" }}, the filter foo would be passed the variablevarand the argumentbar.
Once you’ve written your filter definition, you need to register it with your Library instance, to make it available to Django’s template language:
register.filter("cut", cut)
register.filter("lower", lower)
The Library.filter() method takes two arguments:
- The name of the filter – a string.
- The compilation function – a Python function (not the name of the function as a string).
You can use register.filter() as a decorator instead:
@register.filter(name="cut")
def cut(value, arg):
return value.replace(arg, "")
@register.filter
def lower(value):
return value.lower()
If you leave off the name argument, as in the second example above, Django will use the function’s name as the filter name.
Finally, register.filter() also accepts three keyword arguments, is_safe, needs_autoescape, and expects_localtime.
url
{% url 'some-url-name' arg1=v1 arg2=v2 %}
Context Processors
def site_settings(request):
return {'site_name': 'My awesome site', 'site_creation_date': '12/12/12'}
# Add this to the context_processors list
'yourapp.context_processors.site_settings' # <-- our custom context processor
Models
Methods
__str__()get_absolute_url()
Abstract Model
If you want to define a model that you can reuse by deriving from it, abstract base classes are the way to go.
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
Choices
The first element in each tuple is the value that will be stored in the database. The second element is displayed by the field’s form widget.
YEAR_IN_SCHOOL_CHOICES = [
("FR", "Freshman"),
("SO", "Sophomore"),
("JR", "Junior"),
("SR", "Senior"),
("GR", "Graduate"),
]
Integer choices:
class Card(models.Model):
class Suit(models.IntegerChoices):
DIAMOND = 1
SPADE = 2
HEART = 3
CLUB = 4
suit = models.IntegerField(choices=Suit.choices)
Field Type Parameters
editable: DefaultTrue
Deep Copy
Just change the primary key of your object and run save().
obj = Foo.objects.get(pk=<some_existing_pk>)
obj.pk = None
obj.save()
Manager
Manager Names
Using this example model, Person.objects will generate an AttributeError exception, but Person.people.all() will provide a list of all Person objects.
from django.db import models
class Person(models.Model):
# ...
people = models.Manager()
Extra Manager
Adding extra Manager methods is the preferred way to add table-level functionality to your models. (For row-level functionality – i.e., functions that act on a single instance of a model object – use Model methods, not custom Manager methods.)
For example, this custom Manager adds a method with_counts():
from django.db import models
from django.db.models.functions import Coalesce
class PollManager(models.Manager):
def with_counts(self):
return self.annotate(num_responses=Coalesce(models.Count("response"), 0))
class OpinionPoll(models.Model):
question = models.CharField(max_length=200)
objects = PollManager()
Modifying a manager’s initial QuerySet
You can override a Manager’s base QuerySet by overriding the Manager.get_queryset() method.
class DahlBookManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(author="Roald Dahl")
Default managers
Django interprets the first Manager defined in a class as the default Manager. You can specify a custom default manager using Meta.default_manager_name.
Field lookups
gtgteltltestartswith: abc%istartswith: Case-insensitive version ofstartswithendswith: %abccontains: %abc%in: WHERE id IN (1, 3, 4)
Aggregate
Person.objects.aggregate(
average=Avg("age"),
max_age=Max("age"),
min_age=Min("age"),
sum_age=Sum("age"),
count=Count("age"),
)
Relations
OneToMany
By default, this Manager is named FOO_set, where FOO is the source model name, lowercased. This Manager returns QuerySets.
You can override the FOO_set name by setting the related_name parameter in the ForeignKey definition.
>>> b = Blog.objects.get(id=1)
>>> b.entries.all() # Returns all Entry objects related to Blog.
# b.entries is a Manager that returns QuerySets.
>>> b.entries.filter(headline__contains="Lennon")
>>> b.entries.count()
ManyToMany
from django.db import models
class Publication(models.Model):
title = models.CharField(max_length=30)
class Meta:
ordering = ["title"]
def __str__(self):
return self.title
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
class Meta:
ordering = ["headline"]
def __str__(self):
return self.headline
Add or Create:
>>> a2.publications.add(p1, p2)
>>> a2.publications.create(title="Science News")
OneToOne
class EntryDetail(models.Model):
entry = models.OneToOneField(Entry, on_delete=models.CASCADE)
details = models.TextField()
ed = EntryDetail.objects.get(id=2)
ed.entry # Returns the related Entry object.
e = Entry.objects.get(id=2)
e.entrydetail # returns the related EntryDetail object (Reverse)
If no object has been assigned to this relationship, Django will raise a DoesNotExist exception.
Transaction
Atomic
from django.db import transaction
@transaction.atomic
def viewfunc(request):
# This code executes inside a transaction.
do_stuff()
with transaction.atomic():
user = User.objects.create_user(username=username, password=password)
Profile.objects.create(
user=user,
nickname=request.POST.get('nickname'),
bio=request.POST.get('bio'),
location=request.POST.get('location'),
weight=request.POST.get('weight'),
website=request.POST.get('website'),
)
Django Rest Framework
Serializer
Serializer
Custom create, update and validation functions.
class SnippetSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(required=False, allow_blank=True, max_length=100)
code = serializers.CharField(style={'base_template': 'textarea.html'})
linenos = serializers.BooleanField(required=False)
def create(self, validated_data):
"""
Create and return a new `Snippet` instance, given the validated data.
"""
return Snippet.objects.create(**validated_data)
def update(self, instance, validated_data):
"""
Update and return an existing `Snippet` instance, given the validated data.
"""
instance.title = validated_data.get('title', instance.title)
instance.linenos = validated_data.get('linenos', instance.linenos)
instance.language = validated_data.get('language', instance.language)
instance.save()
return instance
ModelSerializer
Very looks like model forms:
rom rest_framework import serializers
from .models import Product
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = '__all__'
# Other attributes
exclude = ['users']
depth = 1 # Specifying nested serialization
Custom Method Field
class PostSerializer(serializers.ModelSerializer):
owner = serializers.SerializerMethodField()
class Meta:
model = Post
fields = ('title', 'body', 'created', 'owner')
def get_owner(self, obj):
return obj.owner.username
Serializer Fields
read_only: IfTrue, the field will be used when serializing a representation, but will not be used when creating or updating an instance during deserialization.write_only: IfTrue, the field will be used when creating or updating an instance during deserialization, but will not be used when serializing a representation.
ViewSet
First of all let’s refactor our UserList and UserDetail views into a single UserViewSet. We can remove the two views, and replace them with a single class:
from rest_framework import viewsets
class UserViewSet(viewsets.ReadOnlyModelViewSet):
"""
This viewset automatically provides `list` and `retrieve` actions.
"""
queryset = User.objects.all()
serializer_class = UserSerializer
Actions
@action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])
def highlight(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted)
def list(self, request):
pass
def create(self, request):
pass
def retrieve(self, request, pk=None):
pass
def update(self, request, pk=None):
pass
def partial_update(self, request, pk=None):
pass
def destroy(self, request, pk=None):
pass
Router
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from snippets import views
# Create a router and register our viewsets with it.
router = DefaultRouter()
router.register(r'snippets', views.SnippetViewSet,basename="snippet")
router.register(r'users', views.UserViewSet,basename="user")
# The API URLs are now determined automatically by the router.
urlpatterns = [
path('', include(router.urls)),
]
Registering the viewsets with the router is similar to providing a urlpattern. We include two arguments - the URL prefix for the views, and the viewset itself.
Views
Function Based Views
from .models import Product
from .serializers import ProductSerializer
from rest_framework.response import Response
from rest_framework.decorators import api_view
@api_view(['GET'])
def my_api(request):
instance = Product.objects.all()
data = ProductSerializer(instance, many=True).data
return Response(data)
APIView
APIView classes are different from regular View classes in the following ways:
- Requests passed to the handler methods will be REST framework’s
Requestinstances, not Django’sHttpRequestinstances. - Handler methods may return REST framework’s
Response, instead of Django’sHttpResponse. The view will manage content negotiation and setting the correct renderer on the response. - Any APIException exceptions will be caught and mediated into appropriate responses.
- Incoming requests will be authenticated and appropriate permission and/or throttle checks will be run before dispatching the request to the handler method.
- Implement
get,post,put,deletemethods.
RetrieveAPIView
Used for read-only endpoints to represent a single model instance. Provides a get method handler.
from rest_framework import generics
from .models import Product
from .serializers import ProductSerializer
class MyAPIView(generics.RetrieveAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
path('<int:pk>', views.MyAPIView.as_view())
CreateAPIView
Used for create-only endpoints. Provides a post method handler.
You can override the save method and customize what data is saved. For example:
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
The owner content will override.
ListAPIView
Used for read-only endpoints to represent a collection of model instances. Provides a get method handler.
ListCreateAPIView
Used for read-write endpoints to represent a collection of model instances. Provides get and post method handlers.
Serializer Relations
SlugRelatedField
many- If applied to a to-many relationship, you should set this argument toTrue.queryset- The queryset used for model instance lookups when validating the field input. Relationships must either set aquerysetexplicitly, or setread_only=True.
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.SlugRelatedField(
many=True,
read_only=True,
slug_field='title'
)
class Meta:
model = Album
fields = ['album_name', 'artist', 'tracks']
HyperlinkedRelatedField
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.HyperlinkedRelatedField(
many=True,
read_only=True,
view_name='track-detail'
)
class Meta:
model = Album
fields = ['album_name', 'artist', 'tracks']
Requests
.data: returns the parsed content of the request body.
Authentication
Token Authentication
Change settings:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
]
}
Generate Token
This view will return a JSON response with the token. The token is a string of random characters, and is unique to the user. This view gets username and password.
from rest_framework.authtoken.views import obtain_auth_token
urlpatterns += [
path('api-token-auth/', obtain_auth_token)
]
Django Environment
DJANGO_SETTINGS_MODULE: When you use Django, you have to tell it which settings you’re using.