An Interest In:
Web News this Week
- April 15, 2024
- April 14, 2024
- April 13, 2024
- April 12, 2024
- April 11, 2024
- April 10, 2024
- April 9, 2024
Getting started with Django Rest Framework
Photo by Markus Spiske: https://www.pexels.com/photo/a-laptop-screen-with-text-4439901/
In this article, we'll create a project that posts about rants using Django and Django Rest Framework. We'll use Django's built-in slugify
method and override its save()
method to automatically create a slug for each rant. We'll also use a third-party package called drf-writable-nested to handle the serialization of a ManyToManyField
in the model.
We'll start by creating a virtual environment, installing the initial packages, creating the django project, creating the django app and finally doing the initial migrations
python -m venv venv. venv/bin/activatepython -m pip install django djangorestframeworkdjango-admin startproject myprojectcd myprojectpython -m manage startapp rantspython -m manage migrate
We list rest_framework
and our rants app in the INSTALLED_APPS settings of our project and include them in the project urls.py
# settings.pyINSTALLED_APPS = [ ... 'rest_framework', 'rants',]# myproject/urls.pyfrom django.urls import path, includeurlpatterns = [ path('', include('rants.urls', namespace='main')), path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),]
Next we create the models inside the rants
app:
# models.pyfrom django.db import modelsclass Category(models.Model): title = models.CharField(max_length=50) slug = models.SlugField(max_length=50) def __str__(self): return self.titleclass Rant(models.Model): title = models.CharField(max_length=150) slug = models.SlugField(max_length=150) categories = models.ManyToManyField( Category, related_name='rants_categories') class Meta: verbose_name = "rant" verbose_name_plural = 'rants' def __str__(self): return self.title
We have a Rant model with a title, slug with a CharField
and a categories attribute with a ManyToManyField
connected to a Category model with a title and a slug attribute.
Then we migrate the database python -m manage makemigrations && python -m manage migrate
. Next, we create a serializer for both models:
# serializers.pyfrom rest_framework import serializersfrom .models import Rant, Categoryclass CategorySerializer(serializers.ModelSerializer): slug = serializers.SlugField(read_only=True) class Meta: model = Category fields = "__all__"class RantSerializer(serializers.ModelSerializer): categories = CategorySerializer(many=True) slug = serializers.SlugField(read_only=True) class Meta: model = Rant fields = ('id', 'title', 'slug', 'categories') many = True
Finally, we create our views and map it to our urls so we can see the API endpoints of our app.
# views.pyfrom rest_framework.response import Responsefrom rest_framework.generics import ListCreateAPIView, UpdateAPIView, DestroyAPIViewfrom .models import Rantfrom .serializers import RantSerializerclass RantList(ListCreateAPIView): queryset = Rant.objects.all() serializer_class = RantSerializer def list(self, request): queryset = self.get_queryset() serializer = RantSerializer(queryset, many=True) return Response(serializer.data)class RantUpdate(UpdateAPIView): queryset = Rant.objects.all() serializer_class = RantSerializerclass RantDelete(DestroyAPIView): queryset = Rant.objects.all() serializer_class = RantSerializer
We use ListCreateAPIView
for read-write endpoints to represent a collection of model instances which provide a get
and post
method handler, UpdateAPIView
for update-only endpoints of a single model instance which provides a put
and patch
method handler, DestroyAPIView
for a delete-only endpoint of a single model instance which provides a delete
method handler. Let's map these views to the urls.py
# urls.pyfrom django.urls import pathfrom .views import RantList, RantUpdate, RantDeletefrom .models import Rantfrom .serializers import RantSerializerapp_name = 'rants'urlpatterns = [ path('api/rants/', RantList.as_view(queryset=Rant.objects.all(), serializer_class=RantSerializer)), path('api/rants/update/<int:pk>/', RantUpdate.as_view(queryset=Rant.objects.all(), serializer_class=RantSerializer)), path('api/rants/delete/<int:pk>/', RantDelete.as_view(queryset=Rant.objects.all(), serializer_class=RantSerializer)),]
We can now view the api endpoints in the browser using the drf package but I personally prefer to see the api endpoints using another package which is the drf-yasg
package. Let's install and configure the package:
python -m pip install drf-yasg# settings.pyINSTALLED_APPS = [ .... 'rest_framework', 'drf_yasg',]# urls.pyfrom django.urls import path, includefrom rest_framework import permissionsfrom drf_yasg.views import get_schema_viewfrom drf_yasg import openapischema_view = get_schema_view( openapi.Info( title="Rants API", default_version='v1', description="Rants", terms_of_service="https://www.google.com/policies/terms/", contact=openapi.Contact(email="[email protected]"), license=openapi.License(name="BSD License"), ), public=True, permission_classes=[permissions.AllowAny],)urlpatterns = [path('api/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),]
Now we run python -m manage runserver
and head over to http://localhost:8000/api/ to see what we've created
But we have a problem, the categories field has an error despite inputting a str
in the field.
{ "categories": [ "Expected a list of items but got type \"str\"." ]}
To solve this we'll install drf-nested-writable
in our app to serialize the categories field then update the serializers.py
file to include WritableNestedModelSerializer
in our RantSerializer
python -m pip install drf-nested-writable# serializers.pyfrom drf_writable_nested.serializers import WritableNestedModelSerializerclass RantSerializer(WritableNestedModelSerializer): categories = CategorySerializer(many=True) slug = serializers.SlugField(read_only=True) class Meta: model = Rant fields = ('id', 'title', 'slug', 'categories') many = True
Now when we add new data for our app using the rants_create
endpoint, we won't get the error we got above anymore but instead. We also override the save()
method in our models for the slug
field to automatically fill the database in
The code to override the save()
method in our models
# models.py...from django.utils.text import slugify... def save(self, *args, **kwargs): self.slug = slugify(self.title) super(Category, self).save(*args, **kwargs) return self.slug... def save(self, *args, **kwargs): self.slug = slugify(self.title) super(Rant, self).save(*args, **kwargs) return self.slug...
Conclusion:
This took me a while to solve since I'm at GMT+8 but hey, at least it got solved and I learned how to override the save method in the models and learned about the drf-writable-nested package to fix my issue.
Original Link: https://dev.to/highcenburg/getting-started-with-django-rest-framework-3ma8
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To