基於APIView&ModelSerializer寫介面

阿麗米熱發表於2023-02-10

基於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表錄入兩條
image

再在publish表錄入兩條
image

然後在book表錄入兩條
image

最後在關聯表新增資料
image

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()

image
image
第一個解決方案是在模型表中寫下面的方法
image
第二種解決方案在序列化類處理看下面程式碼框
image

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.在模型表中寫方法來定製

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

image

# 序列化類程式碼
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

image

三、繼承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

展示效果如下
image

相關文章