drf-序列化器serializer

先生發表於2021-07-04

一、序列化器-serializer

  1. 序列化,序列化器會把模型物件轉成字典,經過response以後變成JSON字串
  2. 反序列化:把客戶端傳送過來的資料,經過request以後變成字典,序列化器可以把字典轉成模型
  3. 反序列化:完成資料校驗功能

二、序列化器的使用

序列化器的使用分為兩個階段:

  1. 在客戶端請求時,使用序列化器可以完成對資料的反序列化。
  2. 在伺服器響應時,使用序列化器可以完成對資料的序列化。

簡單使用

1、建立一個表模型

from django.db import models

class Books(models.Model):
    title = models.CharField(verbose_name='書名', max_length=32)
    publish = models.CharField(verbose_name='出版社', max_length=32)
    price = models.DecimalField(verbose_name='價格', max_digits=5, decimal_places=2)

2、新建一個py檔案,寫一個序列化的類,繼承Serializer

3、在類中寫要序列化的欄位,想序列化那個欄位,就在類中寫那個欄位

from rest_framework import serializers

class BooksSerializer(serializers.Serializer):
    title = serializers.CharField()
    publish = serializers.CharField()
    price = serializers.DecimalField()

4、在檢視類中使用,匯入——》例項化得到序列化物件,把要序列化的物件傳入

5、序列化的物件.data——》是一個字典

6、把字典返回,如果不使用rest_framework提供的Response,就得使用JsonResponse

from rest_framework.views import APIView
from rest_framework.request import Request
from app01.models import Books
from app01.ser import BooksSerializer

class BookView(APIView):
    def get(self, request, pk):
        # 響應資訊
        response_msg = {'status': 200, 'message': '查詢成功'}
        # 獲取要序列化的物件
        book = Books.objects.filter(pk=pk).first()
        # 要序列化誰就把誰傳到序列化類去
        book_ser = BooksSerializer(book)
        # book_ser.data————》序列化物件.data————》就是序列化後的字典
        # 將查詢結果新增到響應資訊內
        response_msg['data'] = book_ser.data
        return Response(response_msg)
    
 # urls.py
re_path(r'^book/(?P<pk>\d+)/', views.BookView.as_view()),

7、如果要被序列化的是包含多條資料的查詢集queryset,可以通過新增many=True引數

from rest_framework.views import APIView
from rest_framework.response import Response
from app01.models import Books
from app01.ser import BooksSerializer


class BooksView(APIView):
    def get(self, request):
        # 響應資訊
        response_msg = {'status': 200, 'message': '查詢成功'}
        books = Books.objects.all()
        # 要序列化誰就把誰傳到序列化類去
        book_ser = BooksSerializer(books, many=True)
        # book_ser.data————》序列化物件.data————》就是序列化後的字典
        # 將查詢結果新增到響應資訊內
        response_msg['data'] = book_ser.data
        return Response(response_msg)

# urls.py
re_path(r'^books/', views.BookView.as_view()),

高階使用

source

1、可以修改欄位名字

class BooksSerializer(serializers.Serializer):
    xxx = serializers.CharField(source='title')	 # 相當於——》xxx = Books.title
   
    
# 響應
{
    "status": 200,
    "message": "查詢成功",
    "data": {
        "xxx": "魔道祖師"			————》響應的欄位名被修改了
    }
}

2、可以跨表查詢

class BookSerializer(serializers.Serializer):
    publish_email = serializers.CharField(source='publish.email')
    # 相當於——》publish_email = Book.publish.email	連表查詢publish表的email欄位
    
    
# 響應
{
    "status": 200,
    "message": "查詢成功",
    "data": {
        "publish_email": "modao@163.com"
    }
}

3、可以執行方法

# models.py
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.IntegerField()
    pub_date = models.DateTimeField()
    publish = models.ForeignKey("Publish", on_delete=models.CASCADE, null=True)
    authors = models.ManyToManyField("Author")

    def func(self):
        return '666666'

    
# ser.py
class BookSerializer(serializers.Serializer):
    msg = serializers.CharField(source='func')
    # msg = Book.func  ——》呼叫Book類中的func()方法的返回值
    
    
# 響應
{
    "status": 200,
    "message": "查詢成功",
    "data": {
        "msg": "666666"
    }
}

**SerializerMethodField( ) **

它需要有一個配套的方法,方法名叫做get_欄位名,返回值就是要顯示的東西

class BookSerializer(serializers.Serializer):
    authors = serializers.SerializerMethodField()

    def get_authors(self, instance):
        # instance ——》 Book物件
        authors = instance.authors.all()    # 取出所有作者
        author_list = []
        for author in authors:
            author_list.append({'name': author.name, 'age': author.age})
        return author_list

通用引數

read_only:(只讀)表明該欄位僅用於序列化輸出,預設False,如果設定成True,響應中可以看到該欄位,修改時,不需要傳該欄位

write_only:(只寫)表明該欄位僅用於反序列化輸入,預設False,如果設定成True,響應中看不到該欄位,修改時,該欄位需要傳

from rest_framework import serializers

class BooksSerializer(serializers.Serializer):
    title = serializers.CharField(read_only = True)		# 響應中能看到改欄位,修改不需要傳值
    publish = serializers.CharField(write_only = True)	# 響應中看不到改欄位,修改需要傳值
    price = serializers.DecimalField()
    
    
# 還有引數如下:
required		表明該欄位在反序列化時必須輸入,預設True
default			反序列化時使用的預設值
allow_null		表明該欄位是否允許傳入None,預設False
validators		該欄位使用的驗證器
error_messages	包含錯誤編號與錯誤資訊的字典

三、反序列化資料校驗

當使用序列化器對資料進行反序列化時,就需要對資料進行校驗了,只有校驗成功的資料才能被儲存成模型類物件

將要校驗的資料傳入序列化器中並例項化:obj = BooksSerializer(data=request.data),呼叫is_valid()方法校驗,校驗成功返回True,失敗返回False。

失敗,可以通過序列化器物件的errors獲取錯誤資訊(字典)

成功,可以公共序列化物件的validated_data屬性獲取資料。

校驗方法有:區域性鉤子,全域性鉤子,validators,和序列化型別和欄位屬性也是

欄位屬性

max_length		最大長度
min_lenght		最小長度
allow_blank		是否允許為空
trim_whitespace	是否截斷空白字元
max_value		最小值
min_value		最大值

區域性鉤子

在序列化器類中建立區域性鉤子:validate_欄位名,並且接收一個引數

# ser.py
class BooksSerializer(serializers.Serializer):
    title = serializers.CharField()
    publish = serializers.CharField()
    price = serializers.DecimalField(max_digits=5, decimal_places=2)
    
    # 區域性鉤子對price欄位校驗
    def validate_price(self, data):
        if float(data) > 20:
            # 校驗成功就通過
            return data
        else:
            # 校驗失敗就拋異常
            raise ValidationError('價格太低')

全域性鉤子

全域性鉤子:validate( ), 接收一個引數,

同時對多個欄位進行比較驗證

# ser.py
class BooksSerializer(serializers.Serializer):
    title = serializers.CharField()
    publish = serializers.CharField()
    price = serializers.DecimalField(max_digits=5, decimal_places=2)
    
def validate(self, validate_data):
    title = validate_data.get('title')
    publish = validate_data.get('publish')
    if not title == publish:
        return validate_data
    else:
        raise ValidationError('書名和出版社不能一致')

validators

使用欄位的validators=[func],來校驗

# ser.py
# 校驗函式
def check_price(data):
    if float(data) > 10:
        return data
    else:
        raise ValidationError('價格太低')

class BooksSerializer(serializers.Serializer):
    title = serializers.CharField()
    publish = serializers.CharField()
    price = serializers.CharField(validators=[check_price])	# 配置

四、序列化器運算元據

查詢所有

# views.py
class BooksView(APIView):
    def get(self, request):
        # 響應資訊
        response_msg = {'status': 200, 'message': '查詢成功'}
        # 獲取所有資料
        books = Books.objects.all()
        # 把資料誰傳到序列化器中
        book_ser = BooksSerializer(instance=books, many=True)	# 序列化多條需要加 many=True
        # book_ser.data————》序列化物件.data————》就是序列化後的字典
        # 將查詢結果新增到響應資訊內
        response_msg['data'] = book_ser.data
        return Response(response_msg)
    
# urls.py
path('books/', views.BooksView.as_view()),

查詢單條

# views.py
class BookView(APIView):
    def get(self, request, pk):
        # 響應資訊
        response_msg = {'status': 200, 'message': '查詢成功'}
        # 獲取要序列化的物件
        book = Books.objects.filter(pk=pk).first()
        # 要序列化誰就把誰傳到序列化器中
        book_ser = BooksSerializer(instance=book)
        # book_ser.data————》序列化物件.data————》就是序列化後的字典
        # 將查詢結果新增到響應資訊內
        response_msg['data'] = book_ser.data
        return Response(response_msg)
    
# urls.py
re_path(r'^book/(?P<pk>\d+)/', views.BookView.as_view()),

新增資料

新增資料需要在序列化器中重寫create( ) 方法:

注意沒有傳遞instance例項,則呼叫save()方法的時候,create()被呼叫,相反,如果傳遞了instance例項,呼叫save()方法的時候,update()被呼叫。

# views.py
class BookView(APIView):
    def post(self, request):
        # 響應資訊
        response_msg = {'status': 201, 'message': '增加成功'}
        # 修改才有instance,新增沒有instance,只有data
        book_ser = BooksSerializer(data=request.data)

        # 校驗欄位
        if book_ser.is_valid():
            book_ser.save()  # 需要在序列化器中重寫create()方法
            # 儲存成功把原資料返回
            response_msg['data'] = book_ser.data
        else:
            response_msg['status'] = 202
            response_msg['message'] = '資料校驗失敗'
            response_msg['data'] = book_ser.error_messages
        return Response(response_msg)
    
    
# ser.py
class BooksSerializer(serializers.Serializer):
    title = serializers.CharField()
    publish = serializers.CharField()
    price = serializers.DecimalField(max_digits=5, decimal_places=2)

    # 重寫create
    def create(self, validated_data):   # validated_data——>傳入的新增資料
        instance = Books.objects.create(**validated_data)
        # instance——> 新增的欄位物件,需要返回
        return instance
    
    
# urls.py
path('book/', views.BookView.as_view()),

修改資料

修改資料需要在序列化器中重寫update( ) 方法:

# views.py
class BookView(APIView):
    def put(self, request, pk):
        # 響應資訊
        response_msg = {'status': 200, 'message': '修改成功'}
        # 獲取需要修改的欄位物件
        book = Books.objects.filter(pk=pk).first()
        # 將欄位物件和修改資料新增到序列化器中
        book_ser = BooksSerializer(instance=book, data=request.data)

        # 校驗資料
        if book_ser.is_valid():
            book_ser.save()     # 需要在序列化器中重寫update()方法
            response_msg['data'] = book_ser.data
        else:
            response_msg['status'] = 202
            response_msg['message'] = '資料校驗失敗'
            response_msg['data'] = book_ser.error_messages
        return Response(response_msg)
    
# urls.py
re_path('book/(?P<pk>\d+)', views.BookView.as_view()),

刪除資料

# views.py
class BooksView(APIView):
    def delete(self, request, pk):
    # 響應資訊
    response_msg = {'status': 200, 'message': '刪除成功'}
    # 刪除資料
    Books.objects.filter(pk=pk).delete()
    return Response(response_msg)
    
# urls.py
re_path('book/(?P<pk>\d+)', views.BooksView.as_view()),

五、模型類序列化器

DRF提供了ModelSerializer模型類序列化器來幫助我們快速建立一個Serializer類。

ModelSerializer與常規的Serializer相同,但是提供了:

  • 基於模型類自動生成一系列欄位
  • 基於模型類自動為Serializer生成validators,比如unique_together
  • 包含預設的create( ) 和update( )。

例項:

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book        # 指明參照那個模型類
        fields = '__all__'  # 為模型類的那些欄位生成

欄位操作

1、可以使用fields來明確欄位,__all__表示包含所以欄位,具體那些欄位->fields = ('title','price')

2、exclude表示排除那些欄位,不能和fields一起寫——>exclude = ('price',)

3、額外引數extra_kwargs,給欄位新增額外的引數

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book        # 指明參照那個模型類
        fields = '__all__'  # 為模型類的那些欄位生成
		
        # 類似於  title = serializers.CharField(read_only = True)
        extra_kwargs = {
            'title': {'read_only': True},
        }

六、原始碼分析many=True

當我們需要查詢多條資料時就需要在例項化序列化器的時候傳many=True

book_ser = BooksSerializer(instance=books, many=True)	# 查詢多條
book_one_ser = BooksSerializer(instance=book)	# 查詢單條

print(type(book_ser))
#<class 'rest_framework.serializers.ListSerializer'>

print(type(book_one_ser))
#<class 'app01.ser.BookModelSerializer'>

# 物件的生成-->先呼叫類的__new__方法,生成空物件,如果many=True,生成ListSerializer物件,反之生成Serializer物件

# 類的__new__方法控制物件的生成
def __new__(cls, *args, **kwargs):
    # 如果many=True,就會自動建立ListSerializer類
    if kwargs.pop('many', False):
        return cls.many_init(*args, **kwargs)
    return super().__new__(cls, *args, **kwargs)

相關文章