Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 8, 2023 03:47 am GMT

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

Rants API screenshot

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

Rants API

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

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To