About the Django Application
The Django application is the main component of the backend and handles the following:
- Return responses for web requests (using gunicorn)
- Process asynchronous tasks (using Celery)
Data Model
The Django project is a simple blogging application. The main data model is a Post
model that contains a few different types of fields:
body
: The title of the post, aCharField
image
: An optional image associated with the post, anImageField
likes
: ManyToMany field toPostLike
model (through model)created_on
:DateTimeField
(inherited fromBaseModel
)modified_on
:DateTimeField
(inherited fromBaseModel
)created_by
:ForeignKey
toCustomUser
model (inherited fromBaseModel
)
Core App
- RequestLog
Blog App
- Blog Post
- Like
Views
Views are where most of the work of the request/response cycle takes place. In Django, views are functions or classes responsible for handling requests and returning responses.
This application uses several different types of views:
- function-based views
- class-based views
The function based views are fairly easy to read and understand and are find for most use cases. Class-based views can be used to achieve the same functionality as function-based views but require less code. Customizing the behavior of class-based views requires overwriting methods of view class being used.
Custom User Model
Django has a default model that provides a user model. Instead of using this, we can subclass the AbstractBaseUser
model to keep most of the same behavior, but also have a more flexible model that will allow us to add custom fields.
See Customizing authentication in Djangoopen in new window for more information on customizing authentication in Django.
Poetry for managing Django project dependencies
Poetry is used as a dependency manager for Django projects. First we can have a look at how poetry works at a high level. We can list all of the project's python dependencies in pyproject.toml
, including development dependencies (such as pytest
for example) and product-only dependencies (such as the Django
package). This pyproject.toml
file is then used to create a poetry.lock
file that contains the exact version of each dependency.
GraphQL with Graphene
Django REST Framework
Django REST Framework (DRF) is used to build the REST API for μblog. The REST API is implemented in two different ways:
- using function-based views
- using class-based views
Function based views
Django REST Framework allows you to use functions to handle API requests. The functions must be decorated with the @api_view
decorator, and other decorators can be used to customize view behavior. One function is used per CRUD operation on our blog post model. There is also a function used to handle requests made when an authenticated user clicks the like
button.
ModelViewSet
Similar to Django, DRF allows you to express views with both functions and classes. There are multiple approaches that can be used with class-based. ModelViewSet
is used in this sample application. The ModelViewSet
class is used to create a view that can be used to create, retrieve, update, and delete a model instance.
@action
decorator
For the like button functionality, the @action
decorator is used to create a custom action on blog posts.
get_queryset
method
The get_queryset
method is used to efficiently fetch the number of likes for each blog post, and to a boolean property that indicates that the current user (if there is one) has liked a given post.
OpenAPI Documentation
OpenAPI is used to build documentation for the API built with Django REST Framework.
JWT Authentication and HttpOnly Cookies
How to do authentication in Django REST Framework project is sometimes a heated debate over web standards and security best practices. This project implements one of the most popular packages for authentication in Django REST Framework, djangorestframework-simplejwt
with some modifications.
The modifications involve sub-classing certain classes used for authentication so that the refresh_token
can be stored in an HttpOnly
cookie. This behavior is not provided by default, but it is important to do this so that credentials do not need to be stored in the browser's localStorage
or in cookies that can be accessed by JavaScript.
The access token is retrieved from the body of an authentication endpoint (where the username and password are supplied) and is then attached to each request that the browser makes to the API (using axios). The access token is stored in memory in a Vue.js ref
object. The access token is used to authenticate requests on the server without having to make extra database queries.
The payload of the JWT access token is not used to retrieve any information. Instead, a separate authenticated request is made to fetch information on the user (user profile information).
When the user authenticates with a username and password and gets the access
token as described above, there is no refresh
token in the response body. Instead, the refresh
token is set in an HttpOnly
cookie on the server that is then set on the web browser client. The refresh cookie is used to refresh the short-lived access
token.
See auth_views.py
for the code that customizes the djangorestframework-simplejwt
package behavior.