This post begins with already working project and app's, I found that there some few requirement's
that my project needed to handle and the best option for those requirement's was to use the Django's Rest Framework. The way that I will tackle this task is more specific to the needs of the project rather than a one to one how to..., that being said you can still follow along, the approach that I'm going to use is easy to follow since I'll be providing a lot of information a log the way for better understanding of the why and how.....this code is available on Github, enough with the alerts and on with the show.
Note: If you would want to mimic the exactly settings then you will need to enable user authentication on your project you can follow this link for details.
Start with the DRF (Django Rest Framework) installation
pip3 install djangorestframework
For our app to use DRF, we'll have to add
rest_framework
into our settings.py.
nano bio_metric/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework', ------ # new
'accounts',
'base',
]
nano base/models.py
from django.conf import settings
from django.db import models
from django.utils import timezone
class Post(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=50)
text = models.CharField(max_length=200)
created_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
Serialization
A Serializer transforms model instances into JSON or XML. This the real great function that Django Rest Framework provides for us since ultimately a web API endpoint returns data we can understand, like JSON or XML and available HTTP verbs.
Adding endpoints to the Post and User models
nano base/serializers.py
from django.contrib.auth.models import User
from rest_framework import serializers
from base.models import Post
class PostSerializer(serializers.ModelSerializer):
author = serializers.ReadOnlyField(source='author.username')
class Meta:
model = Post
fields = ('id', 'title', 'text', 'author', )
class UserSerializer(serializers.ModelSerializer):
posts = serializers.PrimaryKeyRelatedField(
many=True, queryset=Post.objects.all())
class Meta:
model = User
fields = ('id', 'username', 'posts')
nano base/views.py
from django.conf import settings
from django.utils import timezone
from django.shortcuts import render, get_object_or_404
from .models import Post
from .forms import PostForm
from django.shortcuts import redirect
from django.contrib.auth.models import User # new
from rest_framework import generics, permissions # new
from .serializers import PostSerializer, UserSerializer # new
# Create your views here.
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'base/post_detail.html', {'post': post})
def post_new(request):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm()
return render(request, 'base/post_edit.html', {'form': form})
def post_edit(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, 'base/post_edit.html', {'form': form})
class PostList(generics.ListCreateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def perform_create(self, serializer): # new
serializer.save(author=self.request.user)
class PostDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,) # new
class UserList(generics.ListAPIView): # new
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView): # new
queryset = User.objects.all()
serializer_class = UserSerializer
Note on the view above I've used ListCreateAPIView to create a read-only endpoint that lists all available Post instances and then RetrieveUpdateDestroyAPIView for a detail view of individual posts which supports CRUD-like functionality.
nano base/urls.py
from django.urls import path
from . import views
from rest_framework.urlpatterns import format_suffix_patterns # new
urlpatterns = [
path('post//', views.post_detail, name='post_detail'),
path('post//edit/', views.post_edit, name='post_edit'),
path('post/new/', views.post_new, name='post_new'),
path('posts/', views.PostList.as_view()), # new
path('posts//', views.PostDetail.as_view()), # new
path('users/', views.UserList.as_view()), # new
path('users//', views.UserDetail.as_view()), # new
]
urlpatterns = format_suffix_patterns(urlpatterns) # new
Adding login to the browsable API
We can add login to the API page so that we don't have to go to the main site when we want to login,to add a login view to the browsable API we need to edit the
URLconf
in our project-level bio_metric/urls.py
file.
Add rest_framework.urls to the route.
nano bio_metric/urls.py
from django.contrib import admin
from django.urls import path, include
from django.views.generic.base import TemplateView
urlpatterns = [
path('base/', include('base.urls')),
path('accounts/', include('accounts.urls')),
path('login/', include('rest_framework.urls')), # new
path('', TemplateView.as_view(template_name='home.html'), name='home'),
path('accounts/', include('django.contrib.auth.urls')),
path('admin/', admin.site.urls),
]
Notice the name "login" above could be anything The important part is that "rest_framework.urls" .
Fire up the server and test CRUD functionality
python3 manage.py runserver 192.168.0.245:8000
Post Lists ----- I get all the posts
Post detail ----- details of a particular post
API login
User have login
Now you can delete the post
I have deleted one post only two post remaining
Updating the second post
Creating a new post
New post has been created
List of the current posts
The JSON file
Again here the List of the current posts same info being returned by the JSON file above
We cant enumerate the deleted or non existing content by id
We can enumerate existing content by the id
Associating Posts with Users
if you want to enumerate users you will run into this
'User' object has no attribute 'posts'
Based on my requirement's this is enough. I can do the crud functionality on the Post if you want to do the crud functionally to the user's as well you will need to do some modification on the
serializers.py
and views.py
you may olso want to consider securing the API probably with https. Very unfortunate those topic are out of scope for this post.