序列化器-Serializer
阿新 • • 發佈:2022-12-12
目錄
序列化器-Serializer
作用:
- 序列化,序列化器會把模型物件轉換成字典,經過response以後變成json字串
- 反序列化,把客戶端傳送過來的資料,經過request以後變成字典,序列化器可以把字典轉成模型
- 反序列化,完成資料校驗功能
#先在models.py中寫建立表 from django.db import models class Book(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=32) price = models.DecimalField(max_digits=8, decimal_places=3) author = models.CharField(max_length=32) publish = models.CharField(max_length=32)
1、序列化器使用
寫一個序列化類,繼承Serializer
在類中寫要序列化的欄位,想要序列化哪個欄位,就在類中寫哪個欄位
在檢視類中使用,匯入自己寫的序列化類(ser.py)---》例項化得到序列化類的物件,把要序列化的物件傳入
序列化類的物件.data 是一個字典
把字典返回,如果不使用rest_framework提供的Resposne,就得使用JsonResponse
# 自己建立的ser.py檔案中寫序列化類 from rest_framework import serializers # 需要繼承Serializer class BookSeralizer(serializers.Serializer): #想要序列化哪個欄位,就在類中寫哪個欄位 id = serializers.CharField() name = serializers.CharField() price = serializers.CharField() author = serializers.CharField() publish = serializers.CharField()
#urls.py
re_path('books/(?P<pk>\d+)', views.BookView.as_view())
#views.py
from rest_framework.views import APIView
from App.models import Book
from App.ser import BookSeralizer
from rest_framework.response import Response # drf提供的響應物件
class BookView(APIView):
def get(self, request, pk):
book_obj = Book.objects.filter(pk=pk).first()
# 序列化誰,就把誰傳過來
book_ser = BookSeralizer(book_obj) # 呼叫類的__init__方法
# 序列化物件.data就是序列化後的字典
return Response(book_ser.data)
如果使用JsonResponse
返回資料,效果如下:
如果使用JsonResponse
返回資料,就不需要在settings.py
中註冊rest_framework
了
注意:
如果碰到下面的報錯,需要把rest_framework
在settings.py中的app中註冊
2、序列化類的欄位型別
有很多,
只需記住 CharField IntegerField,DateField......
欄位型別:
選項引數:
通用引數:
3、序列化元件修改儲存資料
1.寫一個序列化類,繼承Serializer
2.在類中寫要反序列化的欄位,想要反序列化哪個欄位,就在類中寫哪個欄位,欄位的屬性(max_length.....)
3.在檢視類中使用,匯入---》例項化得到序列化類的物件,把要修改的資料傳入
book_ser = BookSeralizer(book_obj, request.data)
book_ser = BookSeralizer(instance=book_obj, data=request.data)
#自己建立的ser.py檔案
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
# 需要繼承Serializer
class BookSeralizer(serializers.Serializer):
name = serializers.CharField(max_length=16, min_length=4)
price = serializers.CharField()
author = serializers.CharField()
publish = serializers.CharField()
def update(self, instance, validated_data):
# instance是Book這個物件
# validated_data是校驗後的資料
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.author = validated_data.get("author")
instance.publish = validated_data.get('publish')
instance.save() # book.save() 是django 的orm提供的
return instance
#urls.py
re_path('books/(?P<pk>\d+)', views.BookView.as_view())
#views.py
from rest_framework.views import APIView
from App.models import Book
from App.ser import BookSeralizer
from rest_framework.response import Response # drf提供的響應物件
class BookView(APIView):
def put(self, request, pk):
response_msg = {'code': 100, 'msg': ''}
# 找到要改的物件
book_obj = Book.objects.filter(pk=pk).first()
# 得到序列化類的物件
# book_ser = BookSeralizer(book_obj, request.data)
book_ser = BookSeralizer(instance=book_obj, data=request.data)
# 要驗證(和forms元件校驗一樣)
if book_ser.is_valid(): # 表示驗證通過
book_ser.save()
response_msg['msg'] = '資料校驗成功'
response_msg['data'] = book_ser.data
else:
response_msg['code'] = 101
response_msg['msg'] = '資料校驗失敗'
response_msg['data'] = book_ser.errors
return Response(response_msg)
4、序列化元件校驗資料
1.寫一個序列化類,繼承Serializer
2.在類中寫要反序列化的欄位,想要反序列化哪個欄位,就在類中寫哪個欄位,欄位的屬性(max_length.....)
3.在檢視類中使用,匯入---》例項化得到序列化類的物件,把要修改的資料傳入
book_ser = BookSeralizer(book_obj, request.data)
book_ser = BookSeralizer(instance=book_obj, data=request.data)
4.資料校驗 if book_ser.is_valid()
5.如果校驗通過就儲存,檢視中呼叫 序列化物件 ook_ser.save() 序列化物件.save()
6.如果不通過,邏輯自己寫
7.如果欄位的校驗規則不夠,可以寫鉤子函式(區域性和全域性)
# 區域性鉤子
def validate_price(self, data): # validate_欄位名,接受一個引數
# print(type(data))
# print(data)
if float(data) < 10:
# 校驗失敗,拋異常
raise ValidationError('價格太低')
return data
# 全域性鉤子
def validate(self, validated_data):
author = validated_data.get("author")
publish = validated_data.get("publish")
if author == publish:
raise ValidationError("作者跟出版社一樣")
return validated_data
8. 可以使用欄位的validators來校驗
author = serializers.CharField(validators=[check_author]), 來校驗
-寫一個函式
def check_author(data):
if data.startswith('sb'):
raise ValidationError('作者名不能以sb開頭')
-配置:author =validators=[check_author]
re_path('books/(?P<pk>\d+)', views.BookView.as_view())
區域性(全域性)鉤子校驗:
#自己建立的ser.py檔案
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
# 需要繼承Serializer
class BookSeralizer(serializers.Serializer):
name = serializers.CharField(max_length=16, min_length=4)
price = serializers.CharField()
author = serializers.CharField()
publish = serializers.CharField()
# 區域性鉤子
def validate_price(self, data): # validate_欄位名,接受一個引數
# print(type(data))
# print(data)
# 如果價格小於10,校驗不通過
if float(data) < 10:
# 校驗失敗,拋異常
raise ValidationError('價格太低')
return data
# 全域性鉤子
def validate(self, validated_data):
author = validated_data.get("author")
publish = validated_data.get("publish")
if author == publish:
raise ValidationError("作者跟出版社一樣")
return validated_data
def update(self, instance, validated_data):
# instance是Book這個物件
# validated_data是校驗後的資料
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.author = validated_data.get("author")
instance.publish = validated_data.get('publish')
instance.save() # book.save() 是django 的orm提供的
return instance
自己邏輯上的校驗:
#views.py
from rest_framework.views import APIView
from App.models import Book
from App.ser import BookSeralizer
from rest_framework.response import Response # drf提供的響應物件
class BookView(APIView):
def put(self, request, pk):
response_msg = {'code': 100, 'msg': ''}
# 找到要改的物件
book_obj = Book.objects.filter(pk=pk).first()
# 得到序列化類的物件
# book_ser = BookSeralizer(book_obj, request.data)
book_ser = BookSeralizer(instance=book_obj, data=request.data)
# 要驗證(和forms元件校驗一樣)
if book_ser.is_valid(): # 表示驗證通過
book_ser.save()
response_msg['msg'] = '資料校驗成功'
response_msg['data'] = book_ser.data
else:
response_msg['code'] = 101
response_msg['msg'] = '資料校驗失敗'
response_msg['data'] = book_ser.errors
return Response(response_msg)
校驗:
def check_author(data):
if data.startswith('sb'):
raise ValidationError('作者名不能以sb開頭')
# 需要繼承Serializer
class BookSeralizer(serializers.Serializer):
name = serializers.CharField(max_length=16, min_length=4)
price = serializers.CharField()
author = serializers.CharField(validators=[check_author]) # validators=[],列表中寫函式記憶體地址
publish = serializers.CharField()
全部程式碼:
- 1 表模型類
#models.py
from django.db import models
class Book(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8, decimal_places=3)
author = models.CharField(max_length=32)
publish = models.CharField(max_length=32)
- 2 序列化類
# -*- coding: UTF-8 -*-
# @Date :2022/12/2 15:29
#ser.py
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
def check_author(data):
if data.startswith('sb'):
raise ValidationError('作者名不能以sb開頭')
# 需要繼承Serializer
class BookSeralizer(serializers.Serializer):
name = serializers.CharField(max_length=16, min_length=4)
price = serializers.CharField()
author = serializers.CharField(validators=[check_author]) # validators=[],列表中寫函式記憶體地址
publish = serializers.CharField()
# 區域性鉤子
def validate_price(self, data): # validate_欄位名,接受一個引數
# print(type(data))
# print(data)
# 如果價格小於10,校驗不通過
if float(data) < 10:
# 校驗失敗,拋異常
raise ValidationError('價格太低')
return data
def validate(self, validated_data): # 全域性鉤子
author = validated_data.get("author")
publish = validated_data.get("publish")
if author == publish:
raise ValidationError("作者跟出版社一樣")
return validated_data
def update(self, instance, validated_data):
# instance是Book這個物件
# validated_data是校驗後的資料
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.author = validated_data.get("author")
instance.publish = validated_data.get('publish')
instance.save() # book.save() 是django 的orm提供的
return instance
- 3 路由配置
#urls.py
from django.contrib import admin
from django.urls import path, re_path
from App import views
urlpatterns = [
path('admin/', admin.site.urls),
re_path('books/(?P<pk>\d+)', views.BookView.as_view())
]
- 4 書寫檢視類
#views.py
from django.shortcuts import render
from rest_framework.views import APIView
from App.models import Book
from App.ser import BookSeralizer
from rest_framework.response import Response # drf提供的響應物件
from django.http import JsonResponse
class BookView(APIView):
def get(self, request, pk):
book_obj = Book.objects.filter(pk=pk).first()
# 序列化誰,就把誰傳過來
book_ser = BookSeralizer(book_obj) # 呼叫類的__init__方法
# 序列化物件.data就是序列化後的字典
return Response(book_ser.data)
# return JsonResponse(book_ser.data)
def put(self, request, pk):
response_msg = {'code': 100, 'msg': ''}
# 找到要改的物件
book_obj = Book.objects.filter(pk=pk).first()
# 得到序列化類的物件
# book_ser = BookSeralizer(book_obj, request.data)
book_ser = BookSeralizer(instance=book_obj, data=request.data)
# 要驗證(和forms元件校驗一樣)
if book_ser.is_valid(): # 表示驗證通過
book_ser.save()
response_msg['msg'] = '資料校驗成功'
response_msg['data'] = book_ser.data
else:
response_msg['code'] = 101
response_msg['msg'] = '資料校驗失敗'
response_msg['data'] = book_ser.errors
return Response(response_msg)
4、reand_only和write_only
- read_only
表明該欄位僅僅用於序列化輸出,預設False,
如果設定成True,postman中可以看到該欄位,修改時不需要傳該欄位
- write_only
表明該欄位僅僅使用者反序列化輸入,預設False,
如果設定成了True,postman看不到該欄位,但修改時,該欄位必須傳
5、檢視所有,刪除,新增
- 查詢所有資料
path('books/', views.BooksView.as_view())
#views.py
重新寫一個檢視類
class BooksView(APIView):
def get(self, request):
response_msg = {'code': 100, 'msg': ''}
books_obj = Book.objects.all()
book_ser = BookSeralizer(books_obj, many=True) # 序列化多條,如果序列化一條,不需要寫
response_msg['data'] = book_ser.data
response_msg['msg'] = '校驗成功'
return Response(response_msg)
- 新增資料
# urls.py
path('books/', views.BooksView.as_view())
#views.py
class BooksView(APIView):
# 新增資料
def post(self, request):
response_msg = {'code': 100, 'msg': '校驗成功'}
# 修改才有instance,新增沒有,只有data
book_ser = BookSeralizer(data=request.data)
# book_ser = BookSeralizer(request.data) #按位置傳引數request.data,會給instance,就報錯了
# 校驗資料
if book_ser.is_valid():
book_ser.save()
response_msg['data'] = book_ser.data
else:
response_msg['code'] = 102
response_msg['msg'] = '資料校驗失敗'
response_msg['data'] = book_ser.errors
return Response(response_msg)
#ser.py
#在序列化類中重寫create方法
def create(self, validated_data):
# Book.objects.create(name=validated_data.get('name'))
instance = Book.objects.create(**validated_data)
return instance
- 刪除資料
#urls.py
re_path('books/(?P<pk>\d+)', views.BookView.as_view()),
#views.py
class BookView(APIView):
def delete(self, request, pk):
book_obj = Book.objects.filter(pk=pk).delete()
response_msg = {'code': 100, 'msg': '刪除成功'}
return Response(response_msg)
6、自定義Response物件
# -*- coding: UTF-8 -*-
# @Date :2022/12/2 19:30
#建立一個utils.py檔案
class MyResponse():
def __init__(self):
self.code = 100
self.msg = '成功'
@property
def get_dict(self):
return self.__dict__
程式碼:
- models.py
from django.db import models
# Create your models here.
class Book(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8, decimal_places=3)
author = models.CharField(max_length=32)
publish = models.CharField(max_length=32)
- 序列化類
# -*- coding: UTF-8 -*-
# @Date :2022/12/2 19:53
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from App.models import Book
def check_author(data):
if data.startswith('sb'):
raise ValidationError('作者不能以sb開頭')
class BookSerializer(serializers.Serializer):
id = serializers.CharField(read_only=True)
name = serializers.CharField(max_length=16, min_length=4)
price = serializers.CharField(write_only=True)
author = serializers.CharField(validators=[check_author])
publish = serializers.CharField()
def validate_price(self, data):
if float(data) < 10:
raise ValidationError('價格太低了')
return data
def validate(self, validate_data):
author = validate_data.get('author')
publish = validate_data.get('publish')
if author == publish:
raise ValidationError('作者和出版社一樣')
return validate_data
def update(self, instance, validated_data):
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.author = validated_data.get('author')
instance.publish = validated_data.get('publish')
instance.save()
return instance
def create(self, validated_data):
instance = Book.objects.create(**validated_data)
return instance
- 自定義Response
# -*- coding: UTF-8 -*-
# @Date :2022/12/2 20:12
class MyResponse:
def __init__(self):
self.code = 100
self.msg = '檢驗成功'
@property
def get_dict(self):
return self.__dict__
if __name__ == '__main__':
res = MyResponse()
res.data = {'name': 'zhao'}
print(res.get_dict)
- urls.py
from django.contrib import admin
from django.urls import path, re_path
from App import views
urlpatterns = [
path('admin/', admin.site.urls),
re_path('book/(?P<pk>\d+)',views.BookView.as_view()),
path('books/',views.BooksView.as_view())
]
- 檢視類的書寫
from django.shortcuts import render
from rest_framework.views import APIView
from App.models import Book
from App.ser import BookSerializer
from rest_framework.response import Response
from App.utils import MyResponse
# Create your views here.
class BookView(APIView):
# 獲取單個數據
def get(self, request, pk):
book_obj = Book.objects.filter(pk=pk).first()
book_ser = BookSerializer(book_obj)
return Response(book_ser.data)
# 修改資料
def put(self, request, pk):
response = MyResponse()
book_obj = Book.objects.filter(pk=pk).first()
book_er = BookSerializer(instance=book_obj, data=request.data)
if book_er.is_valid():
book_er.save()
response.data = book_er.data
response.msg = '校驗成功'
else:
response.msg = '校驗失敗'
response.code = 101
response.data = book_er.errors
return Response(response.get_dict)
# 刪除資料
def delete(self, request, pk):
response = MyResponse()
Book.objects.filter(pk=pk).delete()
return Response(response.get_dict)
class BooksView(APIView):
# 獲取所有資料
def get(self, request):
response = MyResponse()
books_obj = Book.objects.all()
book_er = BookSerializer(books_obj, many=True)
response.data = book_er.data
return Response(response.get_dict)
# 新增資料
def post(self, request):
response = MyResponse()
book_ser = BookSerializer(data=request.data)
if book_ser.is_valid():
book_ser.save()
response.data = book_ser.data
else:
response.data = book_ser.errors
response.msg = '校驗失敗'
response.code = 101
return Response(response.get_dict)
7、模型類的序列化器
#ser.py 序列化類
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = Book # 對應models.py中的模型
fields = '__all__' # 表示所有欄位都序列化
# fields = ('name', 'price') # 序列化指定欄位
# exclude = ('name',) # 除了name欄位,其他都序列化
# 給authors和publish加write_only屬性
# name加max_len屬性
extra_kwargs = {
'name': {'max_length': 8},
'publish': {'write_only': True},
'authors': {'write_only': True},
}
#urls.py
path('books2/', views.BooksView2.as_view())
#views.py
from App.ser import BookModelSerializer
class BooksView2(APIView):
def get(self, request):
response = MyResponse()
book_obj = Book.objects.all()
book_ser = BookModelSerializer(book_obj, many=True)
response.data = book_ser.data
return Response(response.get_dict)
8、關鍵字many原始碼分析
# 序列化多條,需要傳many=True
path('many/',views.ManyView.as_view()),
class ManyView(APIView):
def get(self, request):
response = MyResponse()
book_obj = Book.objects.filter().first()
books_obj = Book.objects.all()
book_ser = BookModelSerializer(book_obj)#序列化單條
books_ser = BookModelSerializer(books_obj, many=True)#序列化多條
print(type(book_ser)) # <class 'App.ser.BookModelSerializer'>
print(type(books_ser))
# <class 'rest_framework.serializers.ListSerializer'>
response.data = books_ser.data
return Response(response.get_dict)
#物件的生成---》先呼叫類的__new__方法,生成空物件
#物件=類名(name=zhao),觸發類的__init__()
#類的__new__方法控制物件的生成
"""原始碼分析"""
class BaseSerializer(Field):
def __new__(cls, *args, **kwargs):
if kwargs.pop('many', False):
return cls.many_init(*args, **kwargs)
#沒有傳many=True,走下面,正常的物件例項化,
return super().__new__(cls, *args, **kwargs)
@classmethod
def many_init(cls, *args, **kwargs):
allow_empty = kwargs.pop('allow_empty', None)
max_length = kwargs.pop('max_length', None)
min_length = kwargs.pop('min_length', None)
child_serializer = cls(*args, **kwargs)
list_kwargs = {
'child': child_serializer,
}
if allow_empty is not None:
list_kwargs['allow_empty'] = allow_empty
if max_length is not None:
list_kwargs['max_length'] = max_length
if min_length is not None:
list_kwargs['min_length'] = min_length
list_kwargs.update({
key: value for key, value in kwargs.items()
if key in LIST_SERIALIZER_KWARGS
})
meta = getattr(cls, 'Meta', None)
list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
return list_serializer_class(*args, **list_kwargs)
9、Serializer高階用法
新建立一個應用,記得一定要去註冊!
# 路由分發
from App2 import urls
# path('App2/', include('App2.urls'))
path('App2/', include(urls)),
#App2.等models.py
from django.db import models
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 __str__(self):
return self.title
class Publish(models.Model):
name = models.CharField(max_length=32)
email = models.EmailField()
def __str__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
def __str__(self):
return self.name
#App2.views.py
from django.shortcuts import render
from App2.models import Book
from App2.ser import BookSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
class App2BookView(APIView):
def get(self, request, pk):
book_obj = Book.objects.filter(pk=pk).first()
book_ser = BookSerializer(book_obj)
return Response(book_ser.data)
# -*- coding: UTF-8 -*-
# @Date :2022/12/3 16:27
#App2.ser.py
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
title123 = serializers.CharField(source='title')
price = serializers.CharField()
authors = serializers.CharField()
publish = serializers.CharField(source='publish.email') # 相當於book.publish.email
pub_date = serializers.CharField()
# -*- coding: UTF-8 -*-
# @Date :2022/12/3 16:27
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
title123 = serializers.CharField(source='title')
price = serializers.CharField()
# authors = serializers.CharField()
authors = serializers.SerializerMethodField() # 必須配一個方法,方法名叫get_欄位名,返回值就是要顯示的內容
def get_authors(self, instance):
# book物件
authors = instance.authors.all() # 取出所有作者
l = []
for author in authors:
l.append({'name': author.name, 'age': author.age})
return l
publish = serializers.CharField(source='publish.email') # 相當於book.publish.email
pub_date = serializers.CharField()
source
# 可以改欄位名字
title123 = serializers.CharField(source='title')
# 可以.跨表
publish = serializers.CharField(source='publish.email') # 相當於book.publish.email
# 可以執行方法
pub_date = serializers.CharField(source='test') # test是Book表模型中
SerializerMethodField()
authors = serializers.SerializerMethodField() # 必須配一個方法,方法名叫get_欄位名,返回值就是要顯示的內容
def get_authors(self, instance):
# book物件
authors = instance.authors.all() # 取出所有作者
l = []
for author in authors:
l.append({'name': author.name, 'age': author.age})
return l
10、回顧
#1.Serializer類,需要序列化什麼,必須寫一個繼承類,想要序列化什麼欄位,就在裡面寫欄位,(source)
#2 序列化queryset(列表)物件和真正的物件,many=True的作用,instance=要序列化的物件
#3 反序列化 instance=要序列化的物件,data=request.data
#4 欄位驗證,序列化類中,給欄位加屬性,區域性和全域性鉤子函式,欄位屬性validators=[check_author]
#5 當在檢視中呼叫, 序列化物件.is_valid() booK_ser.is_valid(raise_exception=True) 只要驗證不通過,直接丟擲異常
#6 修改儲存---》呼叫序列化列物件.save(),重寫Serializer類 的update方法
def update(self, instance, validated_data):
# instance是Book這個物件
# validated_data是校驗後的資料
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.author = validated_data.get("author")
instance.publish = validated_data.get('publish')
instance.save() # book.save() 是django 的orm提供的
return instance
#7 序列化得到的欄位, 序列化物件.data
#8 自定義Response物件
class MyResponse():
def __init__(self):
self.code = 100
self.msg = '成功'
@property
def get_dict(self):
return self.__dict__
#9 反序列化的新增 序列化類(data=request.data),如果只傳了data,當呼叫序列化物件.save(),會觸發序列化類的create方法執行,當傳了instance和data時,呼叫 序列化物件.save(),會觸發序列化類的update方法執行
#10 重寫create方法,(可以很複雜)
def create(self, validated_data):
instance = Book.objects.create(**validated_data)
return instance
#11 ModelSerializer跟Model做了對應
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = Book # 對應models.py中的模型
fields = '__all__' # 表示所有欄位都序列化
# fields = ('name', 'price') # 序列化指定欄位
# exclude = ('name',) # 除了name欄位,其他都序列化,不能跟fields連用,寫誰,就排除誰
# 給authors和publish加write_only屬性
# name加max_len屬性
extra_kwargs = {
'name': {'max_length': 8},
'publish': {'write_only': True},
'authors': {'write_only': True,'max_lenth':6,'min_length':4},
}
#12 如果在ModelSerializer中寫一個區域性鉤子或全域性鉤子,跟之前一摸一樣
#13 many=True能夠序列化多條的原因-----》__new__是在__init__之前執行的,造出了一個空物件
#14 介面:為了統一子類的行為
、