Django REST 框架详解 04 | 序列化与反序列化及二者整合

2021-01-12 09:57:23 浏览数 (1)

文章目录

    • 一、序列化
        • 1.步骤
        • 2.模拟场景
        • 3.模型构建
        • 4.序列化
        • 5.视图
        • 6.路由
        • 7.测试接口
    • 二、反序列化与验证
        • 1.反序列化
        • 2.视图
        • 3.测试接口
    • 三、序列化与反序列的整合
        • 1.视图
        • 2.路由
        • 3.接口测试

一、序列化

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

代码语言:javascript复制
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

代码语言:javascript复制
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

代码语言:javascript复制
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

代码语言:javascript复制
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<pk>.*)/$', views.Book.as_view()),
]
7.测试接口

使用 GET 查询结果

二、反序列化与验证

1.反序列化

serializers.py

代码语言:javascript复制
# 反序列化
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

代码语言:javascript复制
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 第一次正常入库

敏感字验证

书名重复验证

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

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

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

1.视图

views.py

代码语言:javascript复制
...
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

代码语言:javascript复制
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<pk>.*)/$', views.BookV2.as_view()),
]
3.接口测试

序列化演示

反序列化演示

0 人点赞