一、序列化

1.步骤

  • model s.py,定义表与字段,及表关系
  • serializes.py 中序列化与反序列化
  • views.py 中写 get,post 等操作
  • urls.py 定义路由

2.模拟场景

建立图书管理系统:
表Book: name, price, img, authors, publish, is_delete, create_time
表Publish: name, address, is_delete, create_time
表Author: name, age, is_delete, create_time
表AuthorDetail: mobile, author, is_delete, create_time

3.模型构建

modes.py

from django.db import models

# 基表
# 提供公用的字段,而不创建表
from django.contrib.auth.models import User


class BaseModel(models.Model):
is_delete = models.BooleanField(default=0)
create_time = models.DateTimeField(auto_now_add=True)

# 作为基表的 Model 不能在数据库中创建表
# 声明基表,设置 abstract = True
class Meta:
abstract = True


class Book(BaseModel):
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=5, decimal_places=2)
img = models.ImageField(upload_to='img', default='img/default.jpg')
publish = models.ForeignKey(
to='Publish',
db_constraint=False,
related_name='books',
on_delete=models.DO_NOTHING
)

# 需要注意 Author 这里设置 on_delete 不会生效
# 这是因为多对多,会在 book_author 的关系表来定义,级联关系 on_delete 在这定义
authors = models.ManyToManyField(
to='Author',
db_constraint=False,
related_name='books'
)

@property
def publish_name(self):
return self.publish.name

@property
def author_list(self):
return self.authors.values('name', 'age', 'detail__mobile').all()

class Meta:
db_table = 'book'
verbose_name = '书籍'
verbose_name_plural = verbose_name

def __str__(self):
return self.name


class Publish(BaseModel):
name = models.CharField(max_length=64)
address = models.CharField(max_length=64)

class Meta:
db_table = 'publish'
verbose_name = '出版社'
verbose_name_plural = verbose_name

def __str__(self):
return f'{self.name}'


class Author(BaseModel):
name = models.CharField(max_length=64)
age = models.IntegerField()

class Meta:
db_table = 'author'
verbose_name = '作者'
verbose_name_plural = verbose_name

def __str__(self):
return f'{self.name}'


class AuthorDetail(BaseModel):
mobile = models.CharField(max_length=11)
author = models.OneToOneField(
to='Author',
db_constraint=False, # 不断开表连接
related_name='detail', # 相当于别名,调用时候用 detail 替换 authordetail
on_delete = models.CASCADE, # 删除 AuthorDetail 不会影响 Author 表,但是如果删除 Author,AuthorDetail就会被一起删除
)

class Meta:
db_table = 'author_detail'
verbose_name = '作者详情'
verbose_name_plural = verbose_name

def __str__(self):
return f'from django.db import models

# 基表
# 提供公用的字段,而不创建表
from django.contrib.auth.models import User


class BaseModel(models.Model):
is_delete = models.BooleanField(default=0)
create_time = models.DateTimeField(auto_now_add=True)

# 作为基表的 Model 不能在数据库中创建表
# 声明基表,设置 abstract = True
class Meta:
abstract = True


class Book(BaseModel):
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=5, decimal_places=2)
img = models.ImageField(upload_to='img', default='img/default.jpg')
publish = models.ForeignKey(
to='Publish',
db_constraint=False,
related_name='books',
on_delete=models.DO_NOTHING
)

# 需要注意 Author 这里设置 on_delete 不会生效
# 这是因为多对多,会在 book_author 的关系表来定义,级联关系 on_delete 在这定义
authors = models.ManyToManyField(
to='Author',
db_constraint=False,
related_name='books'
)

@property
def publish_name(self):
return self.publish.name

@property
def author_list(self):
return self.authors.values('name', 'age', 'detail__mobile').all()

class Meta:
db_table = 'book'
verbose_name = '书籍'
verbose_name_plural = verbose_name

def __str__(self):
return self.name


class Publish(BaseModel):
name = models.CharField(max_length=64)
address = models.CharField(max_length=64)

class Meta:
db_table = 'publish'
verbose_name = '出版社'
verbose_name_plural = verbose_name

def __str__(self):
return f'{self.name}'


class Author(BaseModel):
name = models.CharField(max_length=64)
age = models.IntegerField()

class Meta:
db_table = 'author'
verbose_name = '作者'
verbose_name_plural = verbose_name

def __str__(self):
return f'{self.name}'


class AuthorDetail(BaseModel):
mobile = models.CharField(max_length=11)
author = models.OneToOneField(
to='Author',
db_constraint=False, # 不断开表连接
related_name='detail', # 相当于别名,调用时候用 detail 替换 authordetail
on_delete = models.CASCADE, # 删除 AuthorDetail 不会影响 Author 表,但是如果删除 Author,AuthorDetail就会被一起删除
)

class Meta:
db_table = 'author_detail'
verbose_name = '作者详情'
verbose_name_plural = verbose_name

def __str__(self):
return f'{self.author}的详情'

4.序列化

serializes.py

from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.fields import SerializerMethodField
from rest_framework.serializers import ModelSerializer

from api import models


class PublishModelSerializer(ModelSerializer):
class Meta:
model = models.Publish
fields = ('name', 'address')


class BookModelSerializers(ModelSerializer):
# 自定义连表深度,子序列化方式
# 注意:调用时有加载顺序,PublishModelSerializer >> PublishModelSerializer
# 这里通过 book 中 publish 的外键,提数据
publish = PublishModelSerializer()

class Meta:
# 序列化类关联的 model 类
model = models.Book
# 参与序列化的字段
fields = ('name', 'price', 'img','author_list', 'publish')

# 了解
# fields = '__all__' # 所有字段
# exclude = ('id', 'is_delete', 'create_time') # 排除字段,剩下的显示,不可与 fields 共存
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.fields import SerializerMethodField
from rest_framework.serializers import ModelSerializer

from api import models


class PublishModelSerializer(ModelSerializer):
class Meta:
model = models.Publish
fields = ('name', 'address')


class BookModelSerializers(ModelSerializer):
# 自定义连表深度,子序列化方式
# 注意:调用时有加载顺序,PublishModelSerializer >> PublishModelSerializer
# 这里通过 book 中 publish 的外键,提数据
publish = PublishModelSerializer()

class Meta:
# 序列化类关联的 model 类
model = models.Book
# 参与序列化的字段
fields = ('name', 'price', 'img','author_list', 'publish')

# 了解
# fields = '__all__' # 所有字段
# exclude = ('id', 'is_delete', 'create_time') # 排除字段,剩下的显示,不可与 fields 共存
# depth = 1 # 自动联表查询深度

5.视图

views.py

from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView

from api import models, serializers


class Book(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
try:
book_obj = models.Book.objects.get(pk=pk)
book_data = serializers.BookModelSerializers(book_obj).data
except:
return Response({
'status': 1,
'msg': 'Books does not exist'
})
else:
book_objs = models.Book.objects.all()
book_data = serializers.BookModelSerializers(book_objs, many=True).data
return Response({
'status': 0,
'msg': 'ok',
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView

from api import models, serializers


class Book(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
try:
book_obj = models.Book.objects.get(pk=pk)
book_data = serializers.BookModelSerializers(book_obj).data
except:
return Response({
'status': 1,
'msg': 'Books does not exist'
})
else:
book_objs = models.Book.objects.all()
book_data = serializers.BookModelSerializers(book_objs, many=True).data
return Response({
'status': 0,
'msg': 'ok',
'results': book_data
})

6.路由

urls.py

from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
from django.views.static import serve
from django.conf import settings

from api import views

urlpatterns = [
url(r'^books/$', views.Book.as_view()),
url(from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
from django.views.static import serve
from django.conf import settings

from api import views

urlpatterns = [
url(r'^books/$', views.Book.as_view()),
url(r'^books/(?P.*)/$', views.Book.as_view()),
]

7.测试接口

使用 GET 查询结果

mark

二、反序列化与验证

1.反序列化

serializers.py

# 反序列化
class BookModelDeserializers(ModelSerializer):
class Meta:
model = models.Book
# 字段里只有设置了默认值,那么就不会报 This field is required.
fields = ('name', 'price','publish', 'authors')
# extra_kwargs 用来完成反序列化字段的 系统校验规则
extra_kwargs = {
'name': {
'required': True,
'max_length': 5,
'error_messages': {
'required': 'name field is required.',
'max_length': 'too long'
}
},
}

# 局部钩子
def validate_name(self, attrs):
# 书名不重复
# if models.Book.objects.filter(name=attrs): # 已存在
# raise ValidationError('Book existed')
# return attrs

# 书名不能包含 tmd 敏感字符
if 'tmd' in attrs.lower():
raise ValidationError('tmd error')
return attrs

# 全局钩子
def validate(self, attrs):
# 同一出版社不能出版同一本书
publish = attrs.get('publish') # 这里的外键已经变成对象
name = attrs.get('name')

if models.Book.objects.filter(name=name, publish=publish):
raise ValidationError({'book': 'Book exist.'})
# 反序列化
class BookModelDeserializers(ModelSerializer):
class Meta:
model = models.Book
# 字段里只有设置了默认值,那么就不会报 This field is required.
fields = ('name', 'price','publish', 'authors')
# extra_kwargs 用来完成反序列化字段的 系统校验规则
extra_kwargs = {
'name': {
'required': True,
'max_length': 5,
'error_messages': {
'required': 'name field is required.',
'max_length': 'too long'
}
},
}

# 局部钩子
def validate_name(self, attrs):
# 书名不重复
# if models.Book.objects.filter(name=attrs): # 已存在
# raise ValidationError('Book existed')
# return attrs

# 书名不能包含 tmd 敏感字符
if 'tmd' in attrs.lower():
raise ValidationError('tmd error')
return attrs

# 全局钩子
def validate(self, attrs):
# 同一出版社不能出版同一本书
publish = attrs.get('publish') # 这里的外键已经变成对象
name = attrs.get('name')

if models.Book.objects.filter(name=name, publish=publish):
raise ValidationError({'book': 'Book exist.'})
return attrs

2.视图

views.py

from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView

from api import models, serializers


class Book(APIView):
def get(self, request, *args, **kwargs):...

def post(self, request, *args, **kwargs):
request_data = request.data
book_ser = serializers.BookModelDeserializers(data=request_data)
# 当校验失败,马上终止当前视图方法,抛出异常返回给前端
book_ser.is_valid(raise_exception=True)
book_obj = book_ser.save()
return Response({
'status': 0,
'msg': 'ok',
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView

from api import models, serializers


class Book(APIView):
def get(self, request, *args, **kwargs):...

def post(self, request, *args, **kwargs):
request_data = request.data
book_ser = serializers.BookModelDeserializers(data=request_data)
# 当校验失败,马上终止当前视图方法,抛出异常返回给前端
book_ser.is_valid(raise_exception=True)
book_obj = book_ser.save()
return Response({
'status': 0,
'msg': 'ok',
'results': serializers.BookModelSerializers(book_obj).data
})

3.测试接口

使用 POST 第一次正常入库

mark

敏感字验证

mark

书名重复验证

mark

三、序列化与反序列的整合

从数据的安全性和健壮性来考虑,所有的自定义字段不能与 model 的原字段相同

因为序列化是将外键字段作为 Dict,而反序列化会将外键字段作为 Object

1.视图

views.py

...
class BookV2(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
try:
book_obj = models.Book.objects.get(pk=pk)
book_data = serializers.BookV2ModelSerializer(book_obj).data
except:
return Response({
'status': 0,
'msg': 'Books do not existed'
})
else:
book_obj = models.Book.objects.all()
book_data = serializers.BookV2ModelSerializer(book_obj, many=True).data
return Response({
'status': 0,
'msg': 'ok',
'results': book_data
})

# 单增,传的数据是与 model 对应的字典
# 群增,传的数据是装多个 model 对应字典的列表
def post(self, request, *args, **kwargs):
request_data = request.data
book_ser = serializers.BookV2ModelSerializer(data=request_data)

book_ser.is_valid(raise_exception=True)
book_obj = book_ser.save()
return Response({
'status': 100,
'msg': 'ok',
...
class BookV2(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
try:
book_obj = models.Book.objects.get(pk=pk)
book_data = serializers.BookV2ModelSerializer(book_obj).data
except:
return Response({
'status': 0,
'msg': 'Books do not existed'
})
else:
book_obj = models.Book.objects.all()
book_data = serializers.BookV2ModelSerializer(book_obj, many=True).data
return Response({
'status': 0,
'msg': 'ok',
'results': book_data
})

# 单增,传的数据是与 model 对应的字典
# 群增,传的数据是装多个 model 对应字典的列表
def post(self, request, *args, **kwargs):
request_data = request.data
book_ser = serializers.BookV2ModelSerializer(data=request_data)

book_ser.is_valid(raise_exception=True)
book_obj = book_ser.save()
return Response({
'status': 100,
'msg': 'ok',
'results': serializers.BookV2ModelSerializer(book_obj).data

})

2.路由

urls.py

from django.conf.urls import url, include

from api import views

urlpatterns = [
url(r'^books/$', views.Book.as_view()),
url(r'^books/(?P<pk>.*)/$', views.Book.as_view()),
url(r'^v2/books/$', views.BookV2.as_view()),
url(from django.conf.urls import url, include

from api import views

urlpatterns = [
url(r'^books/$', views.Book.as_view()),
url(r'^books/(?P<pk>.*)/$', views.Book.as_view()),
url(r'^v2/books/$', views.BookV2.as_view()),
url(r'^v2/books/(?P.*)/$', views.BookV2.as_view()),
]

3.接口测试

序列化演示

mark

反序列化演示

mark