目錄
基於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
展示效果如下