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.书写类中方法
'''