19
Django REST Framework
Not knowing much about how Django works, but needing to use it for professional reasons, I decided to sign up for a beginners course on the Django REST Framework. I recently completed it and thought I'd share my notes, which serves as a starter guide for anyone wanting to set up a Django project (with the primary purpose of serving a REST API). The corresponding repo can be found on my GitHub.
What I didn't initially appreciate, was that the Django REST Framework is an add-on package to the base Django framework. It needs to be installed separately, but once installed, allows for the creation of REST API endpoints in a more efficient way.
vagrant up
.vagrant ssh
, cd /vagrant
, and python -m venv ~/env
.source ~/env/bin/activate
.pip install -r requirements.txt
.requirements.txt
file with the same packages as what's in the repo, then...django-admin startproject profiles_project .
settings.py
, add ‘rest_framework’ and ‘rest_framework.authtoken’ to the INSTALLED_APPS list.profiles_project
: python manage.py startapp profiles_api
.settings.py
, add ‘profiles_api’ to the INSTALLED_APPS list.vagrant up
, vagrant ssh
, cd /vagrant
, source ~/env/bin/activate
.python manage.py runserver 0.0.0.0:8000
. If there’s an infinite loop situation, try python manage.py runserver 0.0.0.0:8000 --noreload
models.py
file within the profiles_api
app.python manage.py makemigrations profiles_api
. This generates the migration file in the migrations
folder of the app in question.python manage.py migrate
to apply the migration file to the database. This goes through our entire project and runs all migration files.admin.py
, import the model(s) from the various apps.- Need full control over the logic (e.g. multiple data sources).
- Processing files and rendering a synchronous response.
- Call other APIs / services in the same response.
- Accessing local files or data.
- Open
profiles_api/views.py
. Create class based on APIView class. - Define HTTP methods to handle. Each method must return a
Response
object. TheResponse
object takes in either a list or dictionary (to allow it to convert to JSON). - Define a URL endpoint and assign it to this new view.
- In our
profiles_api
app, create aurls.py
file. - In
urls.py
, be sure to importinclude
fromdjango.urls
. This allows us to include other app URLs into our root. - Add a URL to the list.
profiles_api
app, create a serializers.py
file.profiles_api/views.py
.- A simple CRUD API on existing database model.
- A quick and simple API for pre-defined objects.
- Little to no customisation on the logic.
- Working with standard data structures.
- In
views.py
, create a class based on ViewSet. - Define the action methods.
- Define the URL endpoint in
urls.py
and register our ViewSet through a router (provided by REST framework). Once then, add this to the urlpatterns (one time requirement).
Serializer
serializers.py
file, create a UserProfileSerializer
which inherits a ModelSerializer
. Define the Meta
class which is used to specify:
- What the relevant model is.
- The fields to include.
- Extra settings e.g. to make the password write only and to style the field in the browsable API.
create
method to ensure the password input is saved as a hash. We also need to add an update
method to handle the password hashing properly.Vietset
viewsets.ModelViewset
. Designed to manage models through the API. Provide a queryset so it knows which objects will be managed.serializer_class
and queryset
on which to perform these functions.URLs
urls.py
, register a new URL. We don’t need to add a base_name
because we defined a queryset in the viewset. This therefore allows it to default to the model in question. We can add a base_name
if we want to override this.Test it works
vagrant up
, vagrant ssh
, cd /vagrant
, source ~/env/bin/activate
.python manage.py runserver 0.0.0.0:8000
and go to http://localhost:8000/api/profile/
Setup permissions
permissions.py
to create a custom permission class. This will inherit from BasePermission
which provides the has_permission
and has_object_permission
methods.user_id
making the request.views.py
file and import TokenAuthentication. This utilises a random string that’s appended to each request from the user.authentication_classes = (TokenAuthentication,)
to the UserProfileViewSet
class. Add a comma, to ensure it’s added as a tuple. Note that you can add more than one authentication class if you wish.permission_classes = (permissions.UpdateOwnProfile,)
which is the custom permissions class we just created.Add filters
views.py
file, add filter_backends = (filters.SearchFilter,)
to the UserProfileViewSet
.Add login API
views.py
file, add from rest_framework.authtoken.views import ObtainAuthToken
to import the ObtainAuthToken
class.UserLoginApiView
which inherits from ObtainAuthToken
. Whilst we can add this directly to our URLs, it won’t allow us to see this in our browsable API, so we need to override this and create our custom class.renderer_classes
. Note that the ModelViewset comes with this as default.urls.py
and define a ‘login/’ url with our new UserLoginApiView
.ProfileFeedItem
. Each item will be linked to a user_profile
, so we need to set this as a ForeignKey
.on_delete=models.CASCADE
means that when a user is deleted, all of their other ProfileFeedItems will be deleted as well. Alternatively, we can set to null.python manage.py makemigrations
and python manage.py migrate
.Add to admin dashboard
admin.py
, add admin.site.register(models.ProfileFeedItem)
.Add serializer
ProfileFeedItemSerializer
which inherits from serializers.ModelSerializer
.ProfileFeedItem
, and make the user_profile
field to read only.Add viewset
UserProfileFeedViewSet
.queryset
which is the ProfileFeedItem
model.perform_create
to override the create method.urls.py
file.Create custom permission class
UpdateOwnStatus
. Do this in permissions.py
.permission_classes = (permissions.UpdateOwnStatus, IsAuthenticatedOrReadOnly)
. This means that a user must be authenticated, otherwise it’s readonly. The can also only update an item that they own.Ensure only authenticated users can view the feed
IsAuthenticatedOrReadOnly
just use IsAuthenticated
in the viewset.Continue the conversation on https://bionicjulia.com or Instagram
19