Analysis of source code of DRF framework from scratch - serialization and deserialization


Now I will take everyone to implement an interface for adding, deleting, modifying and checking, including single query and group query, single addition and group addition, single modification and group modification, single deletion and group deletion, Let’s Go

Use of serialized components

First introduce the use of serialization components, and then introduce the source code in detail. There are three serialization components

Serializer(bottom layer),ModelSerializer(focus),ListModelSerializer(Auxiliary group reform)
Use of the Serializers component

Let's talk about Serializer first, first create a class in models.py, and then perform data migration.

class User(models.Model):
    SEX_CHOICES = [
        [0,'male'],
        [1,'Female'],
    ]

    name = models.CharField(max_length=64)
    pwd = models.CharField(max_length=32)
    phone = models.CharField(max_length=11,null=True,default=None)
    sex = models.IntegerField(choices=SEX_CHOICES,default=0)
    icon = models.ImageField(upload_to='icon',default='icon/default.jpg')

    class Meta:
        db_table='user'
        verbose_name='user'
        verbose_name_plural=verbose_name

    def __str__(self):
        return '%s' %self.name
Use of ModelSerializers multi-table serialization components

Now I will take everyone to implement an interface for adding, deleting, modifying and checking, including single query and group query, single addition and group addition, single modification and group modification, single deletion and group deletion
ModelSerializer is the same as regular Serializer, but provides:

  1. Automatically generate a series of fields based on the model class

  2. Contains default implementations of create() and update()

  3. Automatically generate validators for Serializer based on model classes, such as unique_together

  4. It is not easy to cross tables with Serializer, and ModelSerializer can be used if there are foreign keys

  5. For less complex logic, ModelSerializers can integrate serialization and deserialization.

model layer

It involves things that DRF novices are not familiar with. I will explain it later, and first clarify the relationship between each data table.

from django.db import models

# base class
class BaseModel(models.Model):
    is_delete = models.BooleanField(default=False)  # The default is not to delete, it is 0/1 in the database
    create_time = models.DateTimeField(auto_now_add=True)

    # Set abstract = True to declare the base table, the Model as the base table cannot form a corresponding table in the database
    class Meta:
        abstract = True  # Declare that the table is just an abstract table does not appear in the database


# book list
class Book(BaseModel):
    name = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=5,decimal_places=2)
    img = models.ImageField(upload_to='img',default='default.jpg')
    #Associated Press Table
    publish = models.ForeignKey(
        to='Publish',   # Associated publish table
        db_constraint=False,   # Disassociation (Disconnect the association between the Book table and the Publish table to facilitate data deletion. Although the association is disconnected, it can still be used normally)
        related_name='books',  # Reverse query field: publish_obj.books can find all books published by the current publishing house
        on_delete=models.DO_NOTHING,  # Set up the connection table operation relationship
    )
    #Associated Author Table
    authors = models.ManyToManyField(
        to='Author',
        db_constraint=True,  #disconnect
        related_name='books'  #reverse lookup field
    )

    """
    Define the table connection operation, and it can be pluggable,@property as a static method
    Then get_Book_Publish wrote serialize file field in
    """
    @property
    def get_Book_Publish(self):
        return self.publish.name

    @property
    def get_Book_Authors(self):
        return self.authors.values('name', 'age')

    class Meta:
        db_table = 'book'
        verbose_name = 'books'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


#Publisher table
class Publish(BaseModel):
    """name,address,is_delete,create_time"""
    name = models.CharField(max_length=64)
    addres = models.CharField(max_length=64)

    class Meta:
        db_table = 'publish'
        verbose_name = 'publishing house'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

#author list
class Author(BaseModel):
    """name,age,is_delete,create_time"""
    name = models.CharField(max_length=64)
    age = models.IntegerField()

    class Meta:
        db_table = 'author'
        verbose_name = 'author'
        verbose_name_plural = verbose_name


    def __str__(self):
        return self.name



#author details
class AuthorDetail(BaseModel):
    """mobile, author,is_delete,create_time"""
    mobile = models.CharField(max_length=11)
    author = models.OneToOneField(
        to = 'Author',
        db_constraint = False,
        related_name = 'detail',
        on_delete = models.CASCADE
    )

    class Meta:
        db_table = 'author_detail'
        verbose_name = 'author details'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.author.name

Create serializes.py in the app directory, and create serialization and deserialization classes. In fact, compared with uncomplicated logic, basically we will write in the future. It is not particularly complicated. For example, the fields of the classes obtained except for different permissions are different. In this case, others can integrate the serialization class and the deserialization class. Here we first introduce the separate writing.

from rest_framework.serializers import ModelSerializer, SerializerMethodField
from . import models
from rest_framework.response import Response
from django.core.exceptions import ValidationError

class BookModelSerializer(ModelSerializer):
    # Customize field information through linked tables
    publish_address = SerializerMethodField()
    def get_publish_address(self, obj):
        # obj is the Book object
        return obj.publish.addres

    class Meta:
        model = models.Book
        # show all fields
        # fields = '__all__'
        # show the specified field
        fields = ('name', 'price', 'img', 'get_Book_Publish', 'publish_address', 'get_Book_Authors')
        # Do not display specified fields
        # exclude = ('image', )


class BookModelDeSerializer(ModelSerializer):
    # ModelSerializer has already implemented update and create for us, we don’t have to write it ourselves
    # the field to be serialized
    class Meta:
        # Note that it is model, not models
        model = models.Book
        fields = ('name', 'price', 'publish', 'authors')
        extra_kwargs = {
            'name':{
                # Set the name field is required
                'required': True,
                'max_length': 10,
                'error_messages':{
                    'max_length': 'title is too long'
                }
            }
        }
    def validate_name(self, values):
        # Verification hook function, here is a simple verification: if the title of the book contains g, it will not work
        if 'g' in values:
            return Response({
                'status': 1,
                'message': 'Book title cannot contain g'
            })
        return values
    def validate(self, attrs):
        publish = attrs.get('publish')  # If publish is a foreign key field, this is the publish object
        name = attrs.get('name')
        if models.Book.objects.filter(name=name, publish=publish):
            raise ValidationError({'book': 'the book already exists'})
        return attrs
Single query and group query interface implementation

It is stipulated that specifying pk is a single search, and not specifying pk is a group search

from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from . import models, serializers
# Create your views here.

class Book(APIView):
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        if pk:
            try:
                book_obj = models.Book.objects.get(is_delete=False, pk=pk)
                return Response(1)
            except:
                return Response({
                    'status':1,
                    'message':'no information found'
                })
        # If no pk is written, conduct a group search
        else:
            book_obj_all = models.Book.objects.filter(is_delete=False).all()
            # Because the queried data is a list of queryset objects, remember to add many=True
            book_obj_all_ser = serializers.BookModelSerializer(book_obj_all, many=True).data
            return Response({
                'status':0,
                'message':'success',
                'result':book_obj_all_ser
            })

Group search test (return all data)

Single increase and group increase interface implementation

Interface: It is stipulated that passing a dictionary list from the front end is a group increase, and passing a dictionary is a new addition (these rules are set by ourselves, and there may be corresponding interface documents in the enterprise)

def post(self, request, *args, **kwargs):
    """
    post It should be possible to realize group increase and single increase,
    Passing a list of dictionaries from the front end is a group increase.
    Passing a dictionary is a new addition
    :param request:
    :param args:
    :param kwargs:
    :return:
    """
    request_data = request.data
    # deserialize
    if isinstance(request_data, dict) and request_data != {}:
        # single increase
        many = False
    elif isinstance(request_data, list) and request_data != []:
        # Qunzeng, in fact, the logic here is not perfect, I just demonstrate it here, just write it simply
        many = True
    else:
        return Response({
            'status': 1,
            'message': 'Data type exception'
        })
    book_ser = serializers.BookModelDeSerializer(data=request_data, many=many)
    # raise_exception=True: When the verification fails, immediately terminate the current view method, throw an exception and return to the foreground
    book_ser.is_valid(raise_exception=True)  # Check whether it is qualified raise_exception=True Required
    book_obj = book_ser.save()  # save an object
    return Response({
        'status': 0,
        'msg': 'ok',
        'results': serializers.BookModelDeSerializer(book_obj, many=many).data
    })

test:

Send data by group increase

Group check to see if the newly added data has been successfully entered into the database

It can be seen that the storage is successful!

Single delete and group delete interface implementation
def delete(self, request, *args, **kwargs):
    """
    Single delete: yes pk,exist postman pass parameters by path
    Group delete:{pks:[1,2,3]} pass json pass parameters
    The logical idea is to turn single deletion into group deletion
    :param request:
    :param args:
    :param kwargs:
    :return:
    """
    pk = request.GET.get('pk', None)
    if pk:
        # book_obj = models.Book.objects.filter(pk=pk).first().delete()
        pks = [pk]
    else:
        pks = request.data.get('pks')
        if not pks:
            return Response({
                'status':1,
                'message': 'Please pass in the parameters to be changed'
            })
    """
    The logic here is to filter out pk exist pks The books inside, that is, the books to be deleted
    as well as is_delete=False(have not been deleted), to avoid repeated deletion
    then put is_delete=True,indicates that it has been deleted
    """
    if models.Book.objects.filter(pk__in=pks, is_delete=False).update(is_delete=True):
        return Response({
            'status': 0,
            'message':'successfully deleted'
        })
    return Response({
        'status': 1,
        'msg': 'failed to delete'
    })

test

Realization of single-reform and group-reform interfaces

It is divided into single partial reform, single overall reform and single partial reform.

Tags: Python Django

Posted by pearllysun on Mon, 12 Dec 2022 21:07:36 +1030