iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
📮

Implementing CRUD with Django and Postman: Design Phase

に公開

Overview

While developing an application, I wanted an environment where I could minimally verify the behavior of CRUD functions after setting up an API environment in Django. I would like to share a tool that is relatively easy to implement with low operational costs, along with its implementation method.

Implemented Features

  • Integration with localhost using Postman
  • API integration and CRUD functionality using Django REST Framework (DRF)
    • Function-based Views
    • Class-based Views
No. Article
1 Frontend/Backend Data Integration using React + Django + CORS
2 Customizing the Django Admin Interface
3 Data Integration between API Server and React using Django REST framework (DRF)
4 Referencing Foreign Key Models using Serializers in Django REST framework
5 Verifying Asynchronous Communication using React + Redux / Redux Toolkit
6 Implementing CRUD functionality with Django using the test tool "Postman" (Design Edition) (This article)
7 [Implementing CRUD functionality with Django using the test tool "Postman" (Implementation Edition)] (To be published later)

Folder Structure

Excerpts focusing on the main files used this time:

  • Backend (Python / Django)
.
├── backend_django
│   └── settings.py
├── django_app
│   ├── models.py
│   ├── serializers.py
│   ├── urls.py
│   └── views.py

What is Postman?

As many of those who have used APIs might know, Postman is a type of testing tool for calling API endpoints, sending requests, receiving responses, and analyzing them.
I first learned about it while studying backend development. While setting it up for localhost is virtually free and keeps configuration costs low, it also seems capable of handling environment settings for AWS or other external infrastructure.

There are other API testing tools used in the field, such as Swagger, but I felt Postman is more suitable for minimal development or for individuals who prioritize cost.

Implementation Method

Postman

Both web and desktop applications are available, but I used the desktop version this time.
For the environment settings, Headers are configured by default. If you are integrating data in JSON format, you only need to change the Content-Type to application/json.

Screenshot 2024-02-09 14.58.07.png

Backend

Partly for verification purposes, I implemented two types of view designs on the backend: function-based and class-based views.
Until now, I primarily used function-based views as my focus was on GET functionality. However, I felt quite stuck when implementing UPDATE, so I am planning to shift toward class-based views.

app/urls.py
from django.urls import path, include
from . import views

urlpatterns = [
    path("postman_test/", views.postman_test, name="postman_test"),
    path("postman_class_test/<int:pk>", views.postman_class_test.as_view(), name="postman_class_test"),
]

For function-based views, you specify views.function_name, while for class-based views, you use views.ClassName.as_view().

app/views.py
from django.shortcuts import render
from django.http import JsonResponse

from django.views.decorators.csrf import csrf_exempt  # Added
from django.utils.decorators import method_decorator  # Added

from rest_framework.views import APIView  # Added
from rest_framework import status  # Added
from rest_framework.decorators import api_view  # Added

@csrf_exempt
@api_view(['GET', 'POST', 'PUT'])
def postman_test(request):
  return JsonResponse({
    "message": "postman_test",
    "request": {
      "method": request.method,
      "path": request.path,
      "data": request.data,
    }
  })

# class based view
@method_decorator(csrf_exempt, name='dispatch')
class postman_class_test(APIView):
  
  # def get(self, request, *args, **kwargs):
  def get(self, request, pk):

    # Retrieve only one record
    queryset = MypageUserProfile.objects.get(id=pk)
    serializer_class = MypageUserProfileUpdateSerializer(queryset)

    data = serializer_class.data

    return JsonResponse(data, safe=False)

  def post(self, request, pk):
    
    queryset = MypageUserProfile.objects.get(id=pk)

    serializer_class = MypageUserProfileUpdateSerializer(queryset, data=request.data)
    if serializer_class.is_valid():
      serializer_class.save()
      return JsonResponse(serializer_class.data, status=201)

    return JsonResponse(serializer_class.errors, status=400)

The key point in the design of views.py is the configuration of "CSRF (Cross-Site Request Forgery)".
If this is not configured, you will encounter a 403 access error when making requests from Postman. You can resolve this by removing access restrictions in ways such as:

  • Removing 'django.middleware.csrf.CsrfViewMiddleware' from settings.py.
  • Adding @csrf_exempt as a prefix to function-based views.
  • Adding @method_decorator(csrf_exempt, name='dispatch') as a prefix to class-based views.

(While this can also be implemented using the <csrf_token> tag when using Django templates, we are not using that this time.)
However, please be aware that disabling CSRF globally is discouraged and poses security vulnerabilities, so this should only be done for verification purposes.

As stated on the official DRF website, the request argument corresponds to Django's original HTTPRequest. By:

  • Specifying the @api_view decorator for function-based views
  • Specifying APIView as the parent class for class-based views

you can design views that utilize the API.

The @api_view decorator for working with function based views.
The APIView class for working with class-based views.

Additionally, status is imported to reference status codes in the response return values.

app/models.py
from django.db import models
  :
class MypageUserProfile(BaseMeta):
  id = models.AutoField(primary_key=True)
  name = models.CharField(max_length=255)
  account_id = models.CharField(max_length=255)
  password = models.CharField(max_length=255)
  email = models.CharField(max_length=255)
  zip = models.CharField(max_length=7)
  address = models.CharField(max_length=255)
  phone = models.CharField(max_length=11, null=True)
  member_type = models.ForeignKey(PricingPlan, on_delete=models.PROTECT, null=True)

  class Meta:
    db_table = 'mypage_user_profile'
    verbose_name_plural = 'My Page_User Profile'

  def __str__(self):
    return self.name
app/serializers.py
from rest_framework import serializers
from .models import (
    :
  MypageUserProfile,
)

class MypageUserProfileUpdateSerializer(serializers.ModelSerializer):
  
  class Meta:
    model = MypageUserProfile
    fields = ('id', 'name', 'account_id', 'password', 'email', 'zip', 'address', 'phone')

The model used as the data source has a structure that combines foreign keys, but the serializers are configured to implement GET and POST functions by extracting only a portion of the fields.

Implementation Views & Summary

Function-based View

Screenshot 2024-02-09 16.34.27.png
Screenshot 2024-02-09 16.34.47.png

In the case of function-based views, the only argument is request.

  • request.method: Request method
  • request.path: API endpoint
  • request.data: Return value during CRUD processing

You can obtain parameters like these, but since you cannot obtain the request object alone—it returns as shown below—there seem to be some conventions to keep in mind.

request:  <WSGIRequest: POST '/api/postman_test/'>

Class-based View

  • GET
    Screenshot 2024-02-09 15.50.22.png

This configuration retrieves only one record using id as the key and displays its return value. You can see that foreign key items configured in the model are not displayed, and only the items filtered by the serializer are returned.

  • POST
    Screenshot 2024-02-09 15.50.41.png

When performing a POST process, select Body > Raw from the Params and input JSON data extracting only the parts to be updated. Note that making a mistake here can be quite troublesome. If successful, the updated data is displayed in the return value (in the screenshot, zip is updated).
When designing with django-admin, you can also verify this update process, and I was able to implement the same configuration when sending request data from a frontend form, etc.

References

  • Postman

https://www.postman.com/

  • Django REST Framework: Requests

https://www.django-rest-framework.org/api-guide/requests/

https://www.django-rest-framework.org/tutorial/2-requests-and-responses/#request-objects

  • 【Django】 How the csrf_token Works and Methods to Disable CSRF or Customize the Screen

https://djangobrothers.com/blogs/django_csrf/

  • [Django] Considering the usage differences between class-based views and function-based views

https://zenn.dev/ikemo/articles/django-class-based-view-or-function-based-view

Discussion