day05 多表关联的序列化和反序列化


day05 多表关联的序列化和反序列化

今日内容

  • 多表关联的序列化和反序列化
  • 请求与响应
  • 视图组件(重点)

1、多表关联的序列化和反序列化

1.1 创建模型表

'''
    on_delete:
    models.CASCADE  # 级联删除
    models.DO_NOTHING # 什么都不做
    models.SET_DEFAULT #设置为默认值
    models.SET_NULL    # 设置为空
    models.SET         #可以写一个函数内存地址,删除的时候,触发这个函数执行
'''

from django.db import models

# 图书表
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish = models.ForeignKey(to='Publish', on_delete=models.SET_NULL, null=True)
    author = models.ManyToManyField(to='Author')


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)


class Author(models.Model):
    name = models.CharField(max_length=32)
    sex = models.IntegerField(choices=[(0, '男'), (1, '女')], default=0)
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)  # 级联删除


class AuthorDetail(models.Model):
    phone = models.IntegerField()
    
# 可能出现的错误:
	找到错误代码(line146):query = query.encode(errors='replace')
    解决方法:把decode改为encode即可。

1.2 创建序列化类

from app01.models import Book, Publish, Author, AuthorDetail
from rest_framework import serializers


class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        # 这里面字段可以写属性,也可以写方法,想要显示哪些,添加哪些字段
        fields = ['id', 'title', 'price', 'publish', 'author', 'publish_detail', 'author_detail']
        # depth = 1  # 深度查一层,官方建议不大于10,正常不超过3,不建议用

        # 限制一下
        extra_kwargs = {
            'publish': {'write_only': True},
            'author': {'write_only': True},
            'publish_detail': {'read_only': True},
            'author_detail': {'read_only': True},

        }

    # 把publish显示出版社的名字和地址
    # 方式一:在表模型中写方法,在序列化类的fields声明一下就可以


class PublishModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Publish
        fields = '__all__'


class AuthorModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = '__all__'


class AuthorDetailModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = AuthorDetail
        fields = '__all__'

1.2.1 多表序列化方法一:在表模型中写方法

# 方式一:在表模型中写方法,在序列化类的fields声明一下就可以

# serializer.py
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        # 这里面字段可以写属性,也可以写方法,想要显示哪些,添加哪些字段
        fields = ['id', 'title', 'price', 'publish', 'author', 'publish_detail', 'author_detail']
        # depth = 1  # 深度查一层,官方建议不大于10,正常不超过3,不建议用

        # 限制一下,把publish显示出版社的名字和地址
        extra_kwargs = {
            'publish': {'write_only': True},
            'author': {'write_only': True},
            'publish_detail': {'read_only': True},
            'author_detail': {'read_only': True},

        }
        
# models.py书写
from django.db import models
# 图书表
class Book(models.Model):

    # book的多表查询,方法一
    @property  # 把方法包装成数据属性
    def publish_detail(self):
        return {'name': self.publish.name, 'addr': self.publish.addr}

    # 作者详情
    @property
    def author_detail(self):
        # 获取所有作者
        author_list = self.author.all()
        author_dict_list = []
        for author in author_list:
            author_dict_list.append({'name': author.name, 'sex': author.get_sex_display()})
        return author_dict_list
'''
总结:
	1.先去models.py中书写方法,并把出版社详情和作者详情返回
	2.在serializer.py序列化类的fields声明一下就可以
	3.限制哪些字段只写,哪些字段只读
'''

1.2.2多表序列化方法二:在序列化类中写

class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        # 这里面字段可以写属性,也可以写方法,想要显示哪些,添加哪些字段
        fields = ['id', 'title', 'price', 'publish', 'author', 'publish_detail', 'author_detail']
        # 限制一下
        extra_kwargs = {
            'publish': {'write_only': True},
            'author': {'write_only': True},
        }
        
# 方法二:在序列化类中写
    publish_detail = serializers.SerializerMethodField(read_only=True)  
    # 这个字段需要配合一个方法,方法必须叫get_字段名,方法返回什么,publish_detail就是什么

    def get_publish_detail(self, obj):  # 需要给一个参数
        # obj是当前系列化到的对象,就是book对象
        return {'name': obj.publish.name, 'addr': obj.publish.addr}
    
    author_detail = serializers.SerializerMethodField(read_only=True)

    def get_author_detail(self, obj):  # 前面必须加上get_字段名
        # 获取所有作者
        author_list = obj.author.all()
        author_dict_list = []
        for author in author_list:
            author_dict_list.append({'name': author.name, 'sex': author.get_sex_display()})
        return author_dict_list
    
'''
总结:
	可以在序列化类重写字段,SerializerMethodField需要借助一个方法
	方法必须叫get_字段名,方法返回什么,publish_detail就是什么
'''

1.2.2多表序列化方法三

class PublishModelSerializer(serializers.ModelSerializer):
    model = Publish
    fields = ['id']  # 显示内容
    
class AuthorModelSerializer(serializers.ModelSerializer):
    
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        #     # 这里面字段可以写属性,也可以写方法,想要显示哪些,添加哪些字段
        fields = ['id', 'title', 'price', 'publish', 'author']
        # 限制一下
        extra_kwargs = {
            'publish': {'write_only': True},
            'author': {'write_only': True},
        }
        
# 方式三:通过子序列化,前提是序列化类要在上面才能使用
    publish = PublishModelSerializer()
    author = AuthorModelSerializer(many=True)  # 多天加上many=True

'''
总结:
	通过子序列化来映射内容,前提必须在本类的上面才能使用
	也可以设置显示的内容
'''

1.3 views.py

from django.shortcuts import render
from app01.models import Book, Publish, Author
from rest_framework.views import APIView
from rest_framework.response import Response

'''图书的多表关联的5个接口'''
class BookView(APIView):
    def get(self, request, *args, **kwargs):
        # 查询所有的图书
        book_obj = Book.objects.all()
        ser = BookModelSerializer(instance=book_obj, many=True)
        return Response(data=ser.data)

    # 新增接口
    def post(self, request, *args, **kwargs):
        ser = BookModelSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)


class BookDetailView(APIView):
    # 查询单本图书
    def get(self, request, pk):
        book_obj = Book.objects.filter(pk=pk).first()
        ser = BookModelSerializer(instance=book_obj)
        return Response(ser.data)

    # 删除接口
    def delete(self, request, pk):
        res = Book.objects.filter(pk=pk).delete()  # 因为返回的是一个元祖,取第一个值
        if res[0] >= 1:  # 影响了几行
            return Response()
        else:
            return Response('要删除的数据不存在')

    # 修改接口
    def put(self, request, pk):
        book_obj = Book.objects.filter(pk=pk).first()
        ser = BookModelSerializer(instance=book_obj, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)

'''出版社的多表关联的5个接口'''
class PublishView(APIView):
    def get(self, request, *args, **kwargs):
        # 查询所有的图书
        publish_obj = Publish.objects.all()
        ser = PublishModelSerializer(instance=publish_obj, many=True)
        return Response(data=ser.data)

    # 新增接口
    def post(self, request, *args, **kwargs):
        ser = PublishModelSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)


class PublishDetailView(APIView):
    # 查询单本图书
    def get(self, request, pk):
        publish_obj = Publish.objects.filter(pk=pk).first()
        ser = PublishModelSerializer(instance=publish_obj)
        return Response(ser.data)

    # 删除接口
    def delete(self, request, pk):
        res = Publish.objects.filter(pk=pk).delete()  # 因为返回的是一个元祖,取第一个值
        if res[0] >= 1:  # 影响了几行
            return Response()
        else:
            return Response('要删除的数据不存在')

    # 修改接口
    def put(self, request, pk):
        publish_obj = Publish.objects.filter(pk=pk).first()
        ser = PublishModelSerializer(instance=publish_obj, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)

1.4 urls.py

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/', views.BookDetailView.as_view()),
    path('publish/', views.PublishView.as_view()),
    path('publish/', views.PublishDetailView.as_view()),
]

2、请求与相应

2.1 请求Request

2.1.1常用属性

1).data
    request.data 返回解析之后的请求体数据。类似于Django中标准的request.POST和 request.FILES属性,但提供如下特性:
    1.包含了解析之后的文件和非文件数据
    2.包含了对POST、PUT、PATCH请求方式解析后的数据
    3.利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据
    
2).query_params
    request.query_params与Django标准的request.GET相同,只是更换了更正确的名称而已。
    
3).method
	request.method就是使用了原来的request的method  通过重写 __getattr__魔法方法实现的

2.1.2 基本使用

# 默认情况下post提交数据,可以三种方式(form-data,urlencoded,json),都能处理
# 我们只允许接口接收json格式,其他格式不支持

# 方式一:全局配置,在配置文件中(settion.py)
# 所有drf的配置,写在这个字典中REST_FRAMEWORK
REST_FRAMEWORK = {
    # 默认能够解析的编码方式
    'DEFAULT_PARSER_CLASSES': (
        'rest_framework.parsers.JSONParser',  # 解析json的
        'rest_framework.parsers.FormParser', # 解析urlencoded
        'rest_framework.parsers.MultiPartParser' # 解析form-data
    )
}
'''
from rest_framework.serializers --->在源码中找到settings.py中找到定义的方法
'''

# 方式二:局部配置(views.py)
from rest_framework.parsers import JSONParser,FormParser,MultiPartParser
class PublishView(APIView):
    parser_classes = [FormParser]  # 优先级最高,只能urlencoded操作
    # 优先级:先用视图类自己的,再用配置文件---》drf的默认配置
    
'''
总结:
	设置post提交数据用哪种格式,有两种方法。
	一种是全局配置,需要去配置文件配置参数
	一种是局部配置,去视图函数中给某个类中设置用什么格式
'''

2.2 响应 Response

2.2.1 常用属性

1).data:
	返回给前端的数据,可以是字典,列表,字符串
    
2).status:
	响应状态码,1xx 2xx 3xx 4xx 5xx
    from rest_framework.status import HTTP_100_CONTINUE  # 这里是python解释器定义的状态码
    
3)emplate_name : 
    不用,替换模板,浏览器中显示模板
    
4)headers=None  :响应头
    obj = HttpResponse()  # 向响应头放入值
    obj['name'] = meng

2.2.2 基本使用

# 默认用浏览器可以看到页面,用postman可以看到json
# 设置只能显示json格式数据


# 方式一:全局配置,在配置文件中(settion.py)
REST_FRAMEWORK = {
    # 使用的是渲染类,作用是:浏览器显示JSON格式
    'DEFAULT_RENDERER_CLASSES': (   # 默认响应渲染类
        'rest_framework.renderers.JSONRenderer',  # json渲染器,必用的
        'rest_framework.renderers.BrowsableAPIRenderer',   # 浏览器显示json
    )
}

# 方式二:局部配置(views.py)
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
class PublishView(APIView):
    renderer_classes = [JSONRenderer] # 优先级最高
    # 优先级:先用视图类自己的,再用配置文件---》drf的默认配置
    
'''
总结:
	和上边的用法一样
'''

3 视图组件(重点)

3.1 GenericAPIView[通用视图类]

from rest_framework.generics import GenericAPIView  # 继承APIView,内部写了几个方法

3.2 类属性和三个方法

# 类属性
    queryset = None  # 所有数据
    serializer_class = None # 序列化的类
    lookup_field = 'pk'  # 查询单条转换器的字段

queryset = Publish.objects.all()  # 只要配置这两行
serializer_class = PublishModelSerializer

# 三个方法
    self.get_queryset()   # 获取所有数据,指明使用的数据查询集
    self.get_serializer   # 获取序列化类,指明视图使用的序列化器
    self.get_object()    # 获取单条

3.3 基本使用

# 通过继承GenericAPIView 写5个接口

from rest_framework.generics import GenericAPIView  # 继承APIView,内部写了几个方法

class PublishView(GenericAPIView):
    queryset = Publish.objects.all()  # 只要配置这两行
    serializer_class = PublishModelSerializer

    def get(self, request, *args, **kwargs):
        # 查询所有的图书
        obj = self.get_queryset()  # 内部做了questset.all()
        ser = self.get_serializer(instance=obj, many=True)
        return Response(data=ser.data)

    # 新增接口
    def post(self, request, *args, **kwargs):
        ser = self.get_serializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)


class PublishDetailView(GenericAPIView):
    # 查询单本图书
    def get(self, request, pk):
        # lookup_field = 'pk' 根据pk参数,类中定义好了
        obj = self.get_object()
        ser = self.get_serializer(instance=obj)
        return Response(ser.data)

    # 删除接口
    def delete(self, request, pk):
        res = self.get_object().delete()  # 因为返回的是一个元祖,取第一个值
        if res[0] >= 1:  # 影响了几行
            return Response()
        else:
            return Response('要删除的数据不存在')

    # 修改接口
    def put(self, request, pk):
        obj = self.get_object()
        ser = self.get_serializer(instance=obj, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)
        
'''
总结:
	1.先要继承GenericAPIView类
	2.配置这两个参数:queryset、serializer_class
	3.书写类中方法
'''
drf