基於APIView&ModelSerializer寫介面

2023-02-10 18:00:17

基於APIView&ModelSerializer寫介面

​ 引言,首先路由寫法還是不變、檢視層的檢視類寫法不變,在序列化類要改變寫法、慢慢的靠近序列化器元件;而且需要建立關聯表,因為現實生活當中不可能僅僅建單表,會使用大量的多表關聯的表資料。好吧!咱們上乾貨把,首先準備一下路由吧,畢竟就兩條程式碼而且配好就邏輯寫完可以馬上測試介面了。本篇文章重點介紹了序列化客製化欄位的多種方法,也演示了序列化重要欄位DictField()、ListField()的用法和反序列化重要引數read_only=True和write_only=True

一、首先準備前提工作

1.模型程式碼

from django.db import models


class Book(models.Model):
    name = models.CharField(verbose_name='書名', max_length=32)
    price = models.CharField(verbose_name='價格', max_length=32)

    # 外來鍵 書跟出版社是一對多
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    # 外來鍵 書跟作者是多對多
    authors = models.ManyToManyField(to='Author')


class Publish(models.Model):
    name = models.CharField(verbose_name='出版社名稱', max_length=32)
    address = models.CharField(verbose_name='出版社地址', max_length=32)


class Author(models.Model):
    name = models.CharField(verbose_name='作者姓名', max_length=32)
    phone = models.CharField(verbose_name='電話號碼', max_length=11)

錄入資料的順序不能亂來,因為有外來鍵關係、那麼小編在這裡詳細的列出錄入資料的過程
先在Author表錄入兩條

再在publish表錄入兩條

然後在book表錄入兩條

最後在關聯表新增資料

2.路由程式碼

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookView.as_view()),
    path('books/<int:pk>/', views.BookDetailView.as_view()),
]

3.檢視程式碼

from django.shortcuts import render
from rest_framework.views import APIView
from .models import Book
from .serializer import BookSerializer
from rest_framework.response import Response


class BookView(APIView):
    def get(self, request):  # 查詢所有
        # 查詢所有資料
        books = Book.objects.all()
        # 序列化資料
        ser = BookSerializer(instance=books, many=True)
        return Response(ser.data)

    def post(self, request):
        ser = BookSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
        else:
            return Response({'code': 101, 'msg': ser.errors})


class BookDetailView(APIView):
    def put(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(data=request.data, instance=book)
        if ser.is_valid():
            ser.save()
            return Response({'code':100, 'msg':'修改成功'})
        else:
            return Response({'code':101, 'msg':ser.errors})

    def get(self, request, pk):
        book = Book.objects.filter(pk=pk)
        ser = BookSerializer(instance=book)
        return Response(ser.data)

    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        return Response('刪除成功')

二、繼承Serializer序列化客製化欄位的三種方法

1.通過source關鍵詞客製化

# 用source關鍵字客製化欄位的程式碼

from rest_framework import serializers

class BookSerializer(serializers.Serializer):
    real_name = serializers.CharField(source='name')
    real_price = serializers.CharField(source='price')
    publish = serializers.CharField(source='publish.name')
    authors = serializers.CharField()



第一個解決方案是在模型表中寫下面的方法

第二種解決方案在序列化類處理看下面程式碼框

2.SerializerMethodField客製化

該方法能夠序列化客製化所有的任何的欄位先想好客製化成什麼樣子,之後新欄位名跟get_後面即可,記得客製化一個就一定要配合一個get_方法,可以自定義返回格式,就說明個性化能力強。


"""關鍵欄位SerializerMethodField客製化"""

class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.CharField()

    publish_detail = serializers.SerializerMethodField()

    def get_publish_detail(self, obj):
        return {'name':obj.publish.name, 'address':obj.publish.address}

    author_list = serializers.SerializerMethodField()

    def get_author_list(self, obj):
        list =[]
        for author in obj.authors.all():
            list.append({'name':author.name, 'phone':author.phone})
            return list

3.在模型表中寫方法來客製化

這個方法其實算不上方法,因為邏輯是一樣的,只不過把方法寫到模型表裡面,而序列化類裡面只需要寫新客製化欄位,所以相當於做了所謂的解耦合吧,但是我感覺完全沒這個必要,畢竟要序列化,那麼屬性和方法統一寫到一個位置比較省心。當然這是我的個人想法,僅供參考。

# 序列化類程式碼
class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.CharField()
    publish_detail = serializers.DictField()
    author_list = serializers.ListField()
# 模型表寫客製化方法程式碼
def publish_detail(self):
    return {'name': self.publish.name, 'address': self.publish.address}

def author_list(self):
    list = []
    for author in self.authors.all():
        list.append({'name': author.name, 'phone': author.phone})
        return list

三、繼承Serializer反序列化

當然客製化欄位的方法也要寫,不管在序列化類裡寫還是模型表裡寫,但凡涉及到客製化欄位就要寫客製化方法,因為涉及到反序列化所以要重寫create方法和update方法,而且也要寫資料校驗,畢竟不能前端傳什麼就錄入,一定要有校驗機制的

class BookSerializer(serializers.Serializer):
    # 如果一個欄位既用來序列化又用來反序列化就不用寫引數read_only或write_only
    name = serializers.CharField(max_length=8, error_messages={'max_length':'太長了'})
    price = serializers.CharField()

    # 只用來序列化 寫引數read_only=True
    publish_detail = serializers.DictField(read_only=True)
    author_list = serializers.ListField(read_only=True)

    # 只用來反序列化 寫引數write_only=True
    publish = serializers.CharField(write_only=True)
    author = serializers.ListField(write_only=True)

    # 反序列化要重寫create方法和update方法
    def create(self, validated_data):
        # 新增一本書
        book = Book.objects.create(name=validated_data.get('name'),
                                   price=validated_data.get('price'),
                                   publish_id=validated_data.get('publish'))
        # 關聯作者
        book.authors.add(*validated_data.get('author'))
        # 返回book
        return book

    def update(self, instance, validated_data):
        # 序列出資料
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish_id = validated_data.get('publish')
        # 先清空資料在add
        authors = validated_data.get('author')
        print(validated_data)
        instance.authors.clear()
        instance.authors.add(*authors)
        # 修改完儲存
        instance.save()
        # 返回資料
        return instance


    # 修改要重寫update
    def update(self, instance, validated_data):
        # validated_data 校驗過後的資料,{name:紅樓夢,price:19,publish:1,authors:[1,2]}
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish_id = validated_data.get('publish')

        # 先清空,再add
        authors = validated_data.get('authors')
        instance.authors.clear()
        instance.authors.add((*authors)

        instance.save()

        return instance

四、用ModelSerializer進行序列化與反序列化

"""ModelSerializer的用法"""


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        # 跟book表關聯
        model = Book
        # fields = ['寫需要序列化的欄位名',[]···]
        # 如果fields = '__all__'這樣寫就表明序列化所有欄位
        fields = '__all__'
        # extra_kwargs = {'欄位名': {'約束條件': 約束引數},是反序列化欄位
        extra_kwargs = {'name': {'max_length': 8},
                        'publish_detail': {'read_only': True},
                        'authors_list': {'read_only': True},
                        'publish': {'write_only': True},
                        'authors': {'write_only': True}
                        }

    def validate_name(self, name):
        if name.startswith('sb'):
            raise ValidationError('書名不能以sb開頭')
        else:
            return name

展示效果如下