DjangoRestFramework Learn Two of Serialization Components, View Components

Keywords: Python Django JSON Java less

A serialized component

First let's create some api interfaces according to the restful specification. Write as follows:

Courses --- GET ---> View Data ---> Return to all data lists [{}, {},]

Courses---POST--->Add Data--->Return Added Data{}

Courses/1 - PUT--->Update pk=1 data--->Return updated data{}

Courses/1 --- DELETE --->Delete data for pk=1 --->Return empty

Courses/1 --- GET ---> View Single Data ---> Return Single Data {}

In this way, let's first look at a page that drf provides us with Postman-like functionality. First we create a django project, create a Course table, add some data, and then follow the steps below.

Step 1: Introduce the Reponse object of drf

from django.shortcuts import render,HttpResponse,redirect
import json
from django.views import View
from app01 import models
from rest_framework.views import APIView

#Reference to Response object provided by drf
from rest_framework.response import Response
#Write our CBV view
class CourseView(APIView):
  #Return all Course data
    def get(self,request):
        course_obj_list = models.Course.objects.all()
        ret = []
        for course_obj in course_obj_list:
            ret.append({
                "title":course_obj.title,
                "desc":course_obj.desc,
            })
        # return HttpResponse(json.dumps(ret, ensure_ascii=False))
        return Response(json.dumps(ret, ensure_ascii=False)) #Use Response here to return messages

    def post(self,request):
        print(request.data)
        return HttpResponse('POST')

Step 2: Configure App and configure it in our settings profile

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
    'rest_framework',  #Register it as App
]

Step 3, Configure our routes

"""
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^courses/', views.CourseView.as_view(),name='courses'),
]

Step 4: Start the project, access our route through the browser (must be browser access to see the corresponding function), see the effect:

      

Here we can send different types of requests and see the corresponding return data, similar to Postman, but not as useful as Postman, so we'll use the Postman tool for future debugging, but we know it's a little expensive.

The data above is serialized by json himself, but django also provides us with a simple serialization component for usage:

from django.shortcuts import render,HttpResponse,redirect
import json
from django.views import View
from app01 import models
from rest_framework.views import APIView
from django.core.serializers import serialize  #The serialization component of django is not the serialization component of drf we want to learn

#It's too cumbersome to serialize without json, we use the serialization component provided by drf
from rest_framework.response import Response

class CourseView(APIView):

    def get(self,request):
        course_obj_list = models.Course.objects.all()
        # ret = []
        # for course_obj in course_obj_list:
        #     ret.append({
        #         "title":course_obj.title,
        #         "desc":course_obj.desc,
        #     })
        # return HttpResponse(json.dumps(ret, ensure_ascii=False))
        # return Response(json.dumps(ret, ensure_ascii=False)
        se_data = serialize('json',course_obj_list,ensure_ascii=False)
        print(se_data)#You also get the serialized data, which is much simpler
        #[{"model": "app01.course", "pk": 1, "fields": {"title": "python", "desc": "666"}}, {"model": "app01.course", "pk": 2, "fields": {"title": "linux", "desc": "\u4e5f\u5f88\u597d"}}, {"model": "app01.course", "pk": 3, "fields": {"title": "go", "desc": "\u5c06\u6765\u53ef\u80fd\u5f88\u597d"}}]

        return Response(se_data)

Then we know two ways to serialize. Is this a lot easier, but DRF has made a more powerful serialization component for us, which can do more than just serialization, so when we do api, we still use the serialization component provided by drf.

import json
from datetime import datetime
from datetime import date

#Converting json data with date format data
class JsonCustomEncoder(json.JSONEncoder):
    def default(self, field):
        if isinstance(field,datetime):
            return field.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(field,date):
            return field.strftime('%Y-%m-%d')
        else:
            return json.JSONEncoder.default(self,field)


d1 = datetime.now()

dd = json.dumps(d1,cls=JsonCustomEncoder)
print(dd)

Next, let's take a look at the data serialization components provided by drf:

1. We use the GET method to view all Course data.

from django.shortcuts import render,HttpResponse,redirect
import json
from django.views import View
from app01 import models
from rest_framework.views import APIView
from django.core.serializers import serialize  #The serialization component of django is not the serialization component of drf we want to learn
#from rest_framework import status #Used when returning the specified status code
#return Response(se_data,status=status=HTTP_400_BAD_REQUEST)
#Or return in this way to specify the status code: return JsonResponse(serializer.data, status=201)
from rest_framework.response import Response

# Serialization methods 3, 1. Introducing drf serialization components
from rest_framework import serializers

# 2. First instantiate a class, inheriting the serializers.Serializer of drf, similar to the usage of our form components and models
class CourseSerializers(serializers.Serializer):
    #This also writes the corresponding fields. Which fields you write will serialize the data of which fields, fields that are not serialized, and no data will be returned. You can comment out one and see what data is returned.
    title = serializers.CharField(max_length=32,required=False) #Field can also be checked when serializing
    desc = serializers.CharField(max_length=32)

class CourseView(APIView):

    def get(self,request):
        course_obj_list = models.Course.objects.all()
        # 3. Use the serialized classes we created
        cs = CourseSerializers(course_obj_list, many=True)  # When serializing multiple objects, you need some many=True parameters
        #4. The serialized data can be obtained by returning the data property of the object
        se_data = cs.data
        print(se_data) #[OrderedDict ([('title','python', ('desc','666')]), OrderedDict ([('title','linux', ('desc','Very good')]), OrderedDict ([('title','go'), ('desc','may be good in the future)])] List nested ordered dictionaries.

        #Remember another way to create a dictionary?It's nothing to worry about. Let's review what we've learned before
        # d1 = {'name':'chao'}
        # d2 = dict([('name','chao'),('age',18)])
        # print(d1) #{'name': 'chao'}
        # print(d2) #{'age': 18, 'name': 'chao'}
        # # Ordered Dictionary
        # from collections import OrderedDict
        # d3 = OrderedDict([('name','Jaden'),('age',22)])
        # print(d3) #OrderedDict([('name', 'Jaden'), ('age', 22)])

        return Response(se_data) #Respense of drf If it returns drf serialized data, the client gets a formatted data, not a row

Depending on the effect:

    

2. Add a piece of data through the POST method:

from django.shortcuts import render,HttpResponse,redirect
from django.views import View
from app01 import models
from rest_framework.views import APIView

from rest_framework.response import Response

from rest_framework import serializers

class CourseSerializers(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    desc = serializers.CharField(max_length=32)

class CourseView(APIView):

    def get(self,request):
        course_obj_list = models.Course.objects.all()
        cs = CourseSerializers(course_obj_list, many=True)
        se_data = cs.data
        return Response(se_data)

    def post(self,request):
        # print(request.data) #{'desc':'java is also good','title':'java'}
        #Is the data sent to be validated? The serialization component of the drf can also validate the data
        cs = CourseSerializers(data=request.data,many=False) #Note that it must be a data = keyword parameter. Note that the many=False parameter is written when validating a single data, and we will serialize the data because we want to return it to the client
        # print(cs.is_valid()) #True, if there is only a few data, you get False
        if cs.is_valid():
            print(cs.data)
            models.Course.objects.create(**cs.data)#Add data
            return Response(cs.data) #Follow the api rules for adding data to post s, so let's return the correct data
        else:
            # If the client sends data like this, less title data
            # {
            #     "desc": "java is also good"
            # }
            cs_errors = cs.errors
            # print(cs_errors) #{'title': ['This field is required.']}
            return Response(cs_errors)
            # What we see on postman is this
            # {
            #     "title": [
            #         "This field is required."
            #     ]
            # }

Then add some data, okay, let's play with some related tables

class Author(models.Model): 
    nid = models.AutoField(primary_key=True)
    name=models.CharField( max_length=32)
    age=models.IntegerField()

class AuthorDetail(models.Model):

    nid = models.AutoField(primary_key=True)
    birthday=models.DateField()
    telephone=models.BigIntegerField()
    addr=models.CharField( max_length=64)

class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    name=models.CharField( max_length=32)
    city=models.CharField( max_length=32)
    email=models.EmailField()

class Book(models.Model):

    nid = models.AutoField(primary_key=True)
    title = models.CharField( max_length=32)
    publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2)
    publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE) #Many-to-one to Publish tables
    authors=models.ManyToManyField(to='Author',) #Many-to-many to Author table

See the serialization code:

from django.shortcuts import render,HttpResponse,redirect
from django.views import View
from app01 import models

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers


class BookSerializers(serializers.Serializer):
    #Let's first serialize the data for two fields, and don't forget that the field in this field has the same name as the field variable in the model table
    title = serializers.CharField(max_length=32)
    price = serializers.DecimalField(max_digits=5, decimal_places=2)

    #One-to-many processing
    # publish = serializers.CharField(max_length=32)  #Return Object
    publish_email = serializers.CharField(max_length=32, source='publish.email')  # source specifies the email data of the many-to-one publish object that is returned, and we now find the email of the book, so the field names in front of it don't correspond to your publish, just name it anyway
    publish_name = serializers.CharField(max_length=32, source='publish.name')  # Sorce specifies that the other field data of the many-to-one publish object returned can receive a write field, that is, the data of all associated fields can be written here for serialization.

    #Processing of multiple pairs
    # authors = serializers.CharField(max_length=32) #bookobj.authors gets something like models.Authors.object, which is a None when printing
    # authors = serializers.CharField(max_length=32,source="authors.all") #Writing this returns data of type queryset, which is certainly not possible for the front end, so write as follows
    authors = serializers.SerializerMethodField() #Serialize method fields, specifically for many-to-many fields, and then define a method below that is written in such a get_field name as the name must be
    def get_authors(self,obj): #The parameter writes an obj, which is a Book object, and then we return the corresponding data through the book object
        # author_list_values = obj.authors.all().values() #It's okay to return data of this type, so you need to communicate clearly with front-end personnel about the structure of the data you want to return, and then process the data here
        #If the data being processed is of this type [{}, {}], you can write it in the following logic. I write it simply, and there must be better logic for processing it
        author_list_values = []
        author_dict = {}
        author_list = obj.authors.all()
        for i in author_list:
            author_dict['name'] = i.name
            author_list_values.append(author_dict)
        return author_list_values


class BookView(APIView):
    def get(self,request):
        book_obj_list = models.Book.objects.all()
        s_books = BookSerializers(book_obj_list,many=True)
        return Response(s_books.data)

    def post(self,request):
        pass

serializer does this internally, and pseudocode is high.

    

urls.py is written as follows:

urlpatterns = [
    #url(r'^admin/', admin.site.urls),
    #Do some interfaces for book tables
    url(r'^books/', views.BookView.as_view(),),

]

Then look at the data Postman returned:

    

Then we'll be able to serialize all kinds of data, but you'll find that it's too tiring to write. It's just a table. If hundreds of tables are neat, there's a simpler way (similar to the difference between form and modelform).

Using ModelSerializer, look at the code:

#ModelSerializer
class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model=models.Book
        # fields=['title','price','publish','authors']
        fields = "__all__"
        # If you write all directly, you get data like this, but if someone else's front end and you want the author's id and name, do you want to deal with it?
        # [
        #     {
        #         "nid": 3,
        #         "title": "go",
        #         "publishDate": null,
        #         "price": "122.00",
        #         "publish": 2,
        #         "authors": [
        #             2,
        #             1
        #         ]
        #     }
        # ]
    #Then there's no way but to process it yourself, in the way it used to be
    authors = serializers.SerializerMethodField()
    def get_authors(self,obj):
        author_list_values = []
        author_dict = {}
        author_list = obj.authors.all()
        for i in author_list:
            author_dict['id'] = i.pk
            author_dict['name'] = i.name
            author_list_values.append(author_dict)
        return author_list_values #This data will overwrite the data in the serialized authors field above
    # So that's all the data the front end gets
    # [
    #     {
    #         "nid": 3,
    #         "authors": [
    #             {
    #                 "name": "chao",
    #                 "id": 1
    #             },
    #             {
    #                 "name": "chao",
    #                 "id": 1
    #             }
    #         ],
    #         "title": "go",
    #         "publishDate": null,
    #         "price": "122.00",
    #         "publish": 2
    #     }
    # ]
    # So what if the publish in a one-to-many relationship has the name of the data the front end wants?Or the old way,
    # publish_name = serializers.CharField(max_length=32, source='publish.name')#But you will find that the serialized data has a publish:1 corresponding to an id value, so if I don't want him to do anything, I can override it with the same variable name, such as the following
    publish = serializers.CharField(max_length=32, source='publish.name')

class BookView(APIView):
    def get(self,request):
        book_obj_list = models.Book.objects.all()
        s_books = BookSerializers(book_obj_list,many=True)
        return Response(s_books.data)

    def post(self,request):
        pass

Above we completed the get request to see all the book information. Next, let's play a post request to add a book data and go directly to the code:

class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model=models.Book
        fields = "__all__"
   # Be careful to comment out the following first, otherwise since get and post request us to use this serialization component, there will be many-to-many multivariable conflicts, so read and write operations are generally divided into two serialization components to write to
    # authors = serializers.SerializerMethodField() #It can also be used to handle one-to-many relationship fields
    # def get_authors(self,obj):
    #     author_list_values = []
    #     author_dict = {}
    #     author_list = obj.authors.all()
    #     for i in author_list:
    #         author_dict['id'] = i.pk
    #         author_dict['name'] = i.name
    #         author_list_values.append(author_dict)
    #     return author_list_values
    # publish = serializers.CharField(max_length=32, source='publish.name')

class BookView(APIView):
    def get(self,request):
        book_obj_list = models.Book.objects.all()
        s_books = BookSerializers(book_obj_list,many=True)
        return Response(s_books.data)


    def post(self,request):

        b_serializer = BookSerializers(data=request.data,many=False)
        if b_serializer.is_valid():
            print('xxxx')
            b_serializer.save() #Because this serializer uses ModelSerializer and we specify which table to serialize in the BookSerializers class, direct save tells us which table we want to save the data in, which is actually a create operation.
            return Response(b_serializer.data) #b_serializer.data This is a dictionary data

        else:
            return Response(b_serializer.errors)

Above we have completed the interface writing for GET and POST requests, and below we have completed several interfaces for PUT, DELETE, and GET to view individual data.

#A read serialization component and a write serialization component
class BookSerializers1(serializers.ModelSerializer):
    class Meta:
        model=models.Book
        fields = "__all__"
    def create(self, validated_data):
        print(validated_data)
        #{'publishDate': datetime.date (2012, 12, 12),'publish': <Publish: Publish object>,'authors': [<Author: Author object>, <Author: Author object>],'title':'Old wine 3','price': Decimal ('15.00')}
        authors = validated_data.pop('authors')
        obj = models.Book.objects.create(**validated_data)
        obj.authors.add(*authors)
        return obj

class BookSerializers2(serializers.ModelSerializer):
    class Meta:
        model=models.Book
        fields = "__all__"

    authors = serializers.SerializerMethodField()
    def get_authors(self,obj):
        print('sssss')
        author_list_values = []
        author_dict = {}
        author_list = obj.authors.all()
        for i in author_list:
            author_dict['id'] = i.pk
            author_dict['name'] = i.name
            author_list_values.append(author_dict)
        return author_list_values
    publish = serializers.CharField(max_length=32, source='publish.name')


class BookView(APIView):
    def get(self,request):
        book_obj_list = models.Book.objects.all()
        s_books = BookSerializers2(book_obj_list,many=True)
        return Response(s_books.data)

    def post(self,request):
        b_serializer = BookSerializers1(data=request.data,many=False)
        if b_serializer.is_valid():
            print('xxxx')
            b_serializer.save() 
            return Response(b_serializer.data) 

        else:
            return Response(b_serializer.errors)

urls.py is as follows:

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # url(r'^courses/', views.CourseView.as_view()),
    #Do some interfaces for book tables
    #url of GET and POST interfaces
    url(r'^books/$', views.BookView.as_view(),), #Don't forget the end of the $symbol

    #PUT, DELETE, GET request interface
    url(r'^books/(\d+)/', views.SBookView.as_view(),),

]

The views.py code is as follows:

from django.shortcuts import render,HttpResponse,redirect
from django.views import View
from app01 import models

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers

class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model=models.Book
        fields = "__all__"

class BookView(APIView):
    def get(self,request):
        '''
        //View all books
        :param request:
        :return:
        '''
        book_obj_list = models.Book.objects.all()
        s_books = BookSerializers(book_obj_list,many=True)
        return Response(s_books.data)

    def post(self,request):
        '''
        //Add a Data
        :param request:
        :return:
        '''
        b_serializer = BookSerializers(data=request.data,many=False)
        if b_serializer.is_valid():
            b_serializer.save()
            return Response(b_serializer.data)
        else:
            return Response(b_serializer.errors)

#Because updating a data, deleting a data, and getting a data all have separate parameters (getting a data, usually an id, so I wrote put, delete, get into a view class, that is, completing our interfaces with that BookView view class above)
class SBookView(APIView):
    def get(self,request,id):
        '''
        //Get Single Data
        :param request:
        :param id:
        :return:
        '''
        book_obj = models.Book.objects.get(pk=id)#Get this data object
        #Then serialize a single model object, serializing a single object returns a dictionary structure {}, serializing multiple objects returns a structure of [{}, {}]
        book_serializer = BookSerializers(book_obj,many=False)
        return Response(book_serializer.data)


    def put(self,request,id):
        '''
        //Update a Data
        :param request:request.data Update submitted data
        :param id:
        :return:
        '''
        book_obj = models.Book.objects.get(pk=id)
        b_s = BookSerializers(data=request.data,instance=book_obj,many=False) #Don't forget to write an instance, because we use ModelSerializer, the data submitted by the front end must be data from all fields, of course the id field is not used
        if b_s.is_valid():
            b_s.save() #The translation is the update operation
            return Response(b_s.data) #Interface specifications require us to return the updated data
        else:
            return Response(b_s.errors)

    def delete(self,request,id):
        '''
        //Delete a Data
        :param request:
        :param id:
        :return:
        '''
        book_obj = models.Book.objects.get(pk=id).delete()
        return Response("") #Don't forget that the interface specification says it's best to return an empty

Well, after five interfaces have been written, let's finish our serialization component, and don't forget to look at the last pit in this section.

  

Override save's create method

class BookSerializers(serializers.ModelSerializer):

      class Meta:
          model=Book
          fields="__all__"
          # exclude = ['authors',]
          # depth=1

      def create(self, validated_data):
        
          authors = validated_data.pop('authors')
          obj = Book.objects.create(**validated_data)
          obj.authors.add(*authors)
          return obj

Hyperlink API, Hyperlink

class BookSerializers(serializers.ModelSerializer):
      publish= serializers.HyperlinkedIdentityField(
                     view_name='publish_detail',
                     lookup_field="publish_id",
                     lookup_url_kwarg="pk")
      class Meta:
          model=Book
          fields="__all__"
          #depth=1

Properties and methods of serializer:

1.save()
When serializer.save() is called, a Model instance (created by calling create() or update() is created or updated, depending on the implementation of the serialized class, such as:

2.create(),update()
The create() and update() methods in Serializer are used to create and generate a Model instance. When using Serializer, one of the two methods must be implemented if the deserialized instance is to be saved to the database, and the resulting instance is returned as a save() return value.The method property validated_data represents the incoming data for the check and can be overridden in the serialized classes you define.

3. is_valid()
When deserializing, the is_valid() method must be used before calling Serializer.save(), and if the check returns True successfully, False is returned if it fails, and error information is saved in the serializer.errors property.

4.data
 The serializer.data holds the serialized data.

5.errors
 When serializer.is_valid() is checked, if the check fails, error information is saved to the serializer.errors property.

Field of serializer:

1.CharField
 Corresponds to models.CharField, and if a length is specified, it is also responsible for checking the text length.

max_length: Maximum length;
min_length: Minimum length;
allow_blank=True: indicates that an empty string is allowed as a valid value and defaults to False;

2.EmailField
 Corresponding to models.EmailField, verify that it is a valid email address.

3.IntegerField
 Corresponds to models.IntegerField, representing the integer type

4.FloatField
 Corresponding models.FloatField, representing floating point type

5.DateTimeField
 Corresponds to models.DateTimeField, which represents the time and date type.

format='YYYY-MM-DD hh:mm': Specifies the datetime output format, defaulting to the DATETIME_FORMAT value.
It is important to note that if models.DateTimeField has auto_now=True or auto_add_now=True in ModelSerializer and HyperlinkedModelSerializer, the property read_only=True will be used by default in the corresponding serializers.DateTimeField, and if you do not want to use this behavior, you need to show that the field is declared:

    class CommentSerializer(serializers.ModelSerializer):
        created = serializers.DateTimeField()
     
        class Meta:
            model = Comment
        
6.FileField
 Corresponds to models.FileField, which represents a file and is responsible for file validation.

max_length: Maximum file name length;
allow_empty_file: Allow empty files;

7.ImageField
 Corresponding to models.ImageField, which represents a picture, is responsible for verifying the correct format of the picture.

max_length: Maximum length of picture name;
allow_empty_file: Allow empty files;
For picture processing, it is recommended to install Pillow: pip install Pillow

8.HiddenField
 This is a unique Field in serializers that does not get values based on user submissions, but rather from default or callable values.A common usage scenario is when a user_id exists as a foreign key in the Model, and when a user submits, it is not allowed to submit a user_id, but the user_id must be a field when defining the Model, in which case HiddenField can be used to provide a default value:

    class LeavingMessageSerializer(serializers.Serializer):
        user = serializers.HiddenField(
            default=serializers.CurrentUserDefault()
        )

Common parameters for serializer:

A public parameter is one that is acceptable to all serializers. <FieldName>.Here are some common parameters.

1.read_only
    read_only=True means that the field is read-only, that is, the corresponding field is used only for serialization (output) and not for deserialization (object creation).The default value is False.

2.write_only
    write_only=True indicates that the field is a write-only segment, as opposed to read_only, where the corresponding field is only used to update or create a new Model and is not used in serialization, that is, it is not output to the user.The default value is False.

3.required
    required=False indicates that the corresponding field is not required for deserialization.Normally, if a field is missing during deserialization, an exception is thrown.The default value is True.

4.default
    Specify a default value for the field.It is important to note that if the field is default, it implicitly indicates that the field already contains required=False, and if both default and required are specified, an exception is thrown.

5.allow_null
    Allow_null=True indicates that None is allowed as a valid value for serialization.It is important to note that if the default parameter is not used explicitly, default=None will be defaulted during serialization when allow_null=True is specified, but not when deserializing.

6.validators
    A list of validation functions applied to incoming fields that will cause validation errors if validation fails or return directly to validate fields, such as:

    username = serializers.CharField(max_length=16, required=True, label='username',
                                    validators=[validators.UniqueValidator(queryset=User.objects.all(),message='User already exists')])

7.error_message
    A dict for error code and error information during validation, which specifies some error information for validation fields, such as:

    mobile= serializers.CharField(max_length=4, required=True, write_only=True, min_length=4,
                                 label='phone', error_messages={
                                    'blank':'Please enter the verification code',
                                    'required':'This field is required',
                                    'max_length':'Authentication code formatted incorrectly',
                                    'min_length':'Authentication code format error',
                                })
7.style
    A key-value pair that controls how fields are rendered, most commonly used for ciphertext entry for passwords, such as:

    password = serializers.CharField(max_length=16, min_length=6, required=True, label='password',
                                    error_messages={
                                        'blank':'Please enter a password',
                                        'required':'This field is required',
                                        'max_length':'Password length does not exceed 16',
                                        'min_length':'Password length is not less than 6',
 
                                    },
                                    style={'input_type': 'password'}, write_only=True) 
9.label
    A short text string describing the field.

10.help_text
    A text string that can be used to describe fields in HTML form fields or other descriptive elements.

11.allow_blank
    allow_blank=True can set False for null but cannot be null

12.source
    Source='user.email'(the value of the email field in the user table gives this value) Set a field value similar to default Usually this value has a foreign key association property that can be set with source

13.validators
    Verify that the field resembles a separate validate

    UniqueValidator is unique by itself

    validators=[UniqueValidator(queryset=UserProfile.objects.all())

    UniqueTogetherValidator: Multi-field union is unique, at which point you can't work on a single field, we set it in Meta.

    Validators = [UniqueTogetherValidator (queryset=UserFav.objects.all(), fields=('user','course', message='already in collection')]

14.error_messages
    Error message prompt

    error_messages={
        "min_value": "The quantity of goods must not be less than one".
        "required": "Please choose the quantity to purchase"
    })
7.ModelSerializers
    ModelSerializer s inherit from Serializer and automate the following three steps compared to their parent class:

    1. Automatically detect and generate serialized fields based on the specified Model without prior definition;
    2. Automatically generate checkers for serialization;
    3. The create() method and update() method are automatically implemented.
    Use ModelSerializer as follows:

    class StudentSerializer(serializers.ModelSerializer):
        class Meta:
            #Specify a Model to automatically detect serialized fields
            model = StudentSerializer
            fields = ('id', 'name', 'age', 'birthday')
It's a lot simpler than Serializer, but sometimes, depending on your project requirements, you may also see declaration fields in ModelSerializer, which are summarized later.

model
 This property specifies a Model class, and ModelSerializer automatically detects the fields that need to be serialized based on the provided Model class.By default, fields in all Model classes will be mapped to corresponding fields in the ModelSerializer class.

For some pits in getting and posting of the same serialization component, go directly to the code (until I dig deeper and give a better answer~):

class BookSerializers(serializers.ModelSerializer):

    class Meta:
        model=models.Book
        fields = "__all__"
        # The following extra_kwargs is temporarily ignored
        # extra_kwargs = {
        #     # 'publish': {'write_only': True},  #Let the data in the publish and authors fields be written only to the database, but they will not be displayed when the query is displayed because we have configured the data that publish will return below to be called publish_name
        #     # 'authors': {'write_only': True}
        # }    #The read_only attribute means that the data for this field name can only be viewed, not saved, and will be excluded if there is data for this field in the data submitted by the user.
    #The create and update methods can be overridden under our BookSerializers class, but validated_data is the data that the user submits and has been serialized. In addition to some basic checks such as required, the serialization checks also align fields according to the read_only=True property in the fields set in this serialization component we writeEliminate, which is why when we write many-to-many and one-to-many fields face to face, if the field names are the same as those of many-to-many or one-to-many fields in the model table, then the data submitted by the user named by this field will be excluded, then there will be no more-to-many and one-to-many fields in validated_data, then the Create method will be executed againValidated_data.pop ('authors') will have an error saying that the authors property cannot be found.
    # def create(self, validated_data):
    #     print(validated_data)
    #     authors = validated_data.pop('authors')
    #     for i in authors:
    #         print(i.pk)
    #     obj = models.Book.objects.create(**validated_data)
    #     obj.authors.add(*authors)
    #     return obj

    authors_list = serializers.SerializerMethodField() #Note that when you use this serialization component to both query and add data, the name of this field cannot be the same as the name of many-to-many fields in your models, nor can it be called authors here
    # authors = serializers.SerializerMethodField()
    # authors_list = A() #Error: {"authors_list": ["This field is required."]}, that is, if we change read_only in the SerializerMethodField to False, then the field is not excluded when field validation occurs, that is, data that must be passed to me with the name of authors_list, but if such a data author_list is added to the data we give to the front endList:[1,2], you will find that there is still an error,.is_valid() is wrong here, why, because, when serializing component checks, there is no field named authors_list in the model table, so there is still an error, so one way here is to rename this field in the serialized component, not the same name as authors, andUse default configuration (that is, read_only=true)
    # def get_authors_list(self,obj):
    def get_authors_list(self,obj):
        author_list_values = []
        author_list = obj.authors.all()
        for i in author_list:
            author_dict = {}
            author_dict['id'] = i.pk
            author_dict['name'] = i.name
            author_list_values.append(author_dict)
        return author_list_values
    # publish = serializers.CharField(max_length=32, source='publish.name',read_only=True) #If this field name is the same as the foreign key field name in the data table and the read_only=True property is set, when the user submits the data to the backend for saving, an error will be reported: NOT NULL constraint failed: app01_book.publish_id, 1.Either you change this name to another name, 2.Either you go to the database table and set this field to null=True, but the second oneThis is certainly not a good way to do that. Remember, when you get data, it's okay to use this serialization component even if the name of the field is the same as that of the field in the data table. It's only a problem when the user submits data for saving, so the best solution is to add the read_only attribute and change the field name instead of in the data tableThis field has the same name
    publish_name = serializers.CharField(max_length=32, source='publish.name',read_only=True)

Two View Components (Mixin Mixed Class)

Following the view of the serialized components above, and then writing, we just mentioned a few interface operations of a Book table above, but do we have any other tables? If we do some serialized interface operations on all four tables above, do we write them as follows?

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from django.shortcuts import HttpResponse
from django.core import serializers


from rest_framework import serializers


class BookSerializers(serializers.ModelSerializer):
      class Meta:
          model=Book
          fields="__all__"
          #depth=1


class PublshSerializers(serializers.ModelSerializer):

      class Meta:
          model=Publish
          fields="__all__"
          depth=1


class BookViewSet(APIView):

    def get(self,request,*args,**kwargs):
        book_list=Book.objects.all()
        bs=BookSerializers(book_list,many=True,context={'request': request})
        return Response(bs.data)


    def post(self,request,*args,**kwargs):
        print(request.data)
        bs=BookSerializers(data=request.data,many=False)
        if bs.is_valid():
            print(bs.validated_data)
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)


class BookDetailViewSet(APIView):

    def get(self,request,pk):
        book_obj=Book.objects.filter(pk=pk).first()
        bs=BookSerializers(book_obj,context={'request': request})
        return Response(bs.data)

    def put(self,request,pk):
        book_obj=Book.objects.filter(pk=pk).first()
        bs=BookSerializers(book_obj,data=request.data,context={'request': request})
        if bs.is_valid():
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)


class PublishViewSet(APIView):

    def get(self,request,*args,**kwargs):
        publish_list=Publish.objects.all()
        bs=PublshSerializers(publish_list,many=True,context={'request': request})
        return Response(bs.data)


    def post(self,request,*args,**kwargs):

        bs=PublshSerializers(data=request.data,many=False)
        if bs.is_valid():
            # print(bs.validated_data)
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)


class PublishDetailViewSet(APIView):

    def get(self,request,pk):

        publish_obj=Publish.objects.filter(pk=pk).first()
        bs=PublshSerializers(publish_obj,context={'request': request})
        return Response(bs.data)

    def put(self,request,pk):
        publish_obj=Publish.objects.filter(pk=pk).first()
        bs=PublshSerializers(publish_obj,data=request.data,context={'request': request})
        if bs.is_valid():
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)

Okay, so let's look at the use of multiple inheritance to objects:

class Animal:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def eat(self):
        print('eat')
    def drink(self):
        print('drink')
    #Eats and drink s are common to animals. The following three are not common, so it is not appropriate to write them directly. So look at the writing below, write some classes separately, some of the other animals are in one class, and inherit more.
    # def eatshit(self):
    #     print('eat s')
    # def zhiwang(self):
    #     print('woven')
    # def flying(self):
    #     print('fly')

class Eatshit:
    def eatshit(self):
        print('eat s')

class Zhiwang:
    def zhiwang(self):
        print('Woven web')

class Flying:
    def zhiwang(self):
        print('Woven web')

class Jumping:
    def zhiwang(self):
        print('jump')

class Dog(Animal,Eatshit):pass

class Spider(Animal,Zhiwang):pass

class Bird(Animal,Flying):pass

class Daishu(Animal,Flying,Jumping):pass

Well, based on this form of inheritance, do we have to consider whether the interface operations we face on each table have the same logic for processing data, and you will find that with so many tables, the GET, PUT, DELETE and POST operations of each table are actually similar, basically changing in two places, here we call them two variables.

publish_list=Publish.objects.all()  #Table All Data
bs=PublshSerializers(publish_list,many=True,context={'request': request})  #Serialization Component

Mixin Mixed Class

The drf has helped us encapsulate several Mixin classes for the operation of logical data processing. Let's just play around and see the code:

from django.shortcuts import render,HttpResponse,redirect
from django.views import View
from app01 import models

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers

#Put the serialization components in a separate file and bring them in
from app01.serializer import BookSerializers,PublishSerializers
from rest_framework import generics

from rest_framework.mixins import ListModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin
# ListModelMixin looks at all the data, corresponding to the interface where our get above looks at all the data
# CreateModelMixin adds data, encapsulating a create operation that corresponds to several ports of POST adds data above
# UpdateModelMixin Update
# DestroyModelMixin Destroyed (Deleted)
# RetrieveModelMixin Gets Single Data
# We extracted it by ourselves, saying that the operation of each table is basically the get, post, delete, put operation above, so we want to extract these methods and inherit them for other classes in the future, so drf encapsulates them for us, which are the Minin classes


class PublishView(ListModelMixin,CreateModelMixin,generics.GenericAPIView):
    '''
        GenericAPIView Must have inherited APIView,because APIView That's what we need, and that's what GenericAPIView It's for our connection. Put your APIView Functions and our Minin Functionally cohesive, dispatched of classes
    '''
    #After inheritance, we need to tell our class the two different variables extracted from the serialization of the previous tables. Note that the following two variable names are both of them, they cannot be changed, and they must be given
    queryset = models.Publish.objects.all()
    serializer_class = PublishSerializers
    def get(self,request):
        '''
        //Distribution finds the corresponding request method, our get method, and the logic to process the data is done by the list method in the inherited ListModelMixin class, so we just need to return self.list(request method, the logic to process the data does not have to be rewritten by ourselves)
        :param request:
        :return:
        '''

        return self.list(request) #The list method helped us serialize

    #The post method adds a piece of data and we just need to execute the create method in the CreateModelMixin class
    def post(self,request):
        return self.create(request)


class SPublishView(UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin,generics.GenericAPIView):
  #The following two variables and corresponding data are required
    queryset = models.Publish.objects.all()  
    serializer_class = PublishSerializers

    # def get(self,request,id):#IDS don't need to be passed, because pk parameters for named groupings added to URLs are automatically made
    def get(self,request, *args, **kwargs): #*args,**kwargs are meant to receive those parameters of the url. Let's write a pk parameter.
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

The classes of the serialized components are placed in a separate file called serializer.py, which reads as follows

from app01 import models
from rest_framework import serializers

class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model=models.Book
        fields = "__all__"

class PublishSerializers(serializers.ModelSerializer):
    class Meta:
        model=models.Publish
        fields = "__all__"

urls.py is as follows:

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [#Interface for publish table
    url(r'^publishs/$', views.PublishView.as_view(),),
    # url(r'^publishs/(\d+)/', views.SPublishView.as_view(),), 
    #When using Mixin classes such as UpdateModelMixin, DestroyModelMixin, RetrieveModelMixin, people ask for a named grouping parameter called pk. The name can be changed, but first use Ang like this.
    url(r'^publishs/(?P<pk>\d+)/', views.SPublishView.as_view(),),

]

After playing with these drf hybrid classes, you will find that the same logical part of processing data is omitted and the code is much simplified.

But you see, we just wrote a publish table operation on it. We have a lot of other tables. Are they also GET, POST, DELETE, PUT and so on? So you want to see if there is any optimization.

####################Author Table Operation##########################
ListCreateAPIView Class encapsulated for us get and create Method
class AuthorView(generics.ListCreateAPIView):

    queryset = models.Author.objects.all()
    serializer_class = AuthorSerializers

#RetrieveUpdateDestroyAPIView This class encapsulates put, get, patch, delete methods
class SAuthorView(generics.RetrieveUpdateDestroyAPIView):

    queryset = models.Author.objects.all()
    serializer_class = AuthorSerializers

Then you can see that there are also optimizations. These two classes have the same thing. Can you weigh them? Of course, one class does it and see how to write them?

#####################Reencapsulated Author Table Operation##########################
from rest_framework.viewsets import ModelViewSet #Inherit this module
class AuthorView(ModelViewSet):
    queryset = models.Author.objects.all()
    serializer_class = AuthorSerializers

However, to change the url, see how it is written:

#Both URLs use one of the above URLs (r'^authors/$', views.AuthorView.as_view ({"get": "list", "post": "create"}),
url(r'^authors/(?P<pk>\d+)/', views.AuthorView.as_view({
                'get': 'retrieve',
                'put': 'update',
                'patch': 'partial_update',
                'delete': 'destroy'
            }),),

Then you'll restart your program and postman will test it.

Well, what about this thing?Interesting, you can check the source code~~

The most critical point in its source code is this:

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            # We also store the mapping of request methods to actions,
            # so that we can later set the action attribute.
            # eg. `self.action = 'list'` on an incoming GET request.
            self.action_map = actions

            # Bind methods to actions
            # This is the bit that's different to a standard view       #These three sentences below are very clever
            for method, action in actions.items(): {'get':'list',}
                handler = getattr(self, action) #You can definitely find the corresponding method list handler = self.list
                setattr(self, method, handler)  #self.get = self.list        Execute later dispatch After the method, that handler = getattr(self,request.method.lower()) #Find the list method to execute, because self.get equals self.list, then execute the list method to return the corresponding content

All we do above are data interfaces, but there are also logical interfaces, such as login, such as this data interface to write a class Login(APIView):pass to do so, the simpler the encapsulation, the more complex the internal logic, the more complex the customization, so we will write our own about different logic.

Note 1:
#By using self to call variables between inherited classes, we now call variables in the Running class from the Animal class that we inherit from the Dog class. That is, if you can't find a corresponding property in one class, you might have placed it in another class
class Animal:
    x=10
    def foo(self):
        print(self.x)

class Running:
    x = 20

#Add a class variable x to the Animal class and change the order of the two classes inherited below to see what happens.
class Dog(Animal,Running):
# class Dog(Running, Animal):
    pass

d = Dog() 
d.foo()  #20

//Note 2: Pass in a dictionary data to the function, exactly which of the following parameters is given by this dictionary.
def f1(action,**kwargs):
    print(action) 
    print(kwargs) 

f1({'name':'chao'}) #Result: {'name':'chao'} {}
# f1(x=1) #Error: TypeError: f1() missing 1 required positional argument:'action'
f1(1,x=1) #Result: 1 {'x': 1}

Would you like to see how the pk in the url named the route in put\get\delete can find the original model object before the update by its pk value?

Posted by phil88 on Sat, 14 Dec 2019 19:42:07 -0800