1.多表与基表概念
2.多表断关联关系分析
3.多表序列化组件
4.自定义序列化深度连表查询
5.多表反序列化组件
6.序列化反序列化整合(重点)
7.群增接口实现
8.单删群删接口实现
课程准备:配置settings.py
1 INSTALLED_APPS = [ 2 # ... 3 'rest_framework', 4 ] 5 6 DATABASES = { 7 'default': { 8 'ENGINE': 'django.db.backends.mysql', 9 'NAME': 'dg_proj', 10 'USER': 'root', 11 'PASSWORD': '123', 12 } 13 } 14 """ 15 任何__init__文件 16 import pymysql 17 pymysql.install_as_MySQLdb() 18 """ 19 20 LANGUAGE_CODE = 'zh-hans' 21 TIME_ZONE = 'Asia/Shanghai' 22 USE_I18N = True 23 USE_L10N = True 24 USE_TZ = False 25 26 MEDIA_URL = '/media/' 27 MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
路由:
1 # 主 2 from django.conf.urls import url, include 3 from django.contrib import admin 4 from django.views.static import serve 5 from django.conf import settings 6 urlpatterns = [ 7 url(r'^admin/', admin.site.urls), 8 url(r'^api/', include('api.urls')), 9 url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}), 10 ] 11 12 # 子 13 from django.conf.urls import url 14 from . import views 15 urlpatterns = [ 16 17 ]
一.多表与基表概念
多表设计(有哪些表):
1 """ 2 Book表:name、price、img、authors、publish、is_delete、create_time 3 4 Publish表:name、address、is_delete、create_time 5 6 Author表:name、age、is_delete、create_time 7 8 AuthorDetail表:mobile, author、is_delete、create_time 9 10 BaseModel基表 11 is_delete、create_time 12 上面四表继承基表,可以继承两个字段 13 """
什么是基表:
# 基表,其他的表需要用到的就继承 class BaseModel(models.Model): is_delete = models.BooleanField(default=False) create_time = models.DateTimeField(auto_now_add=True) # 设置 abstract = True 来声明基表,作为基表的Model不能再数据库中形成对应的表 class Meta: abstract = True
二.多表断关联关系分析
多表断关联关系所有知识点:
1 """ 2 1、外键位置: 3 一对多 - 外键放多的一方 4 一对一 - 从逻辑正反向考虑,如作者表与作者详情表,作者删除级联删除详情,详情删除作者依旧存在,所以建议外键在详情表中 5 多对多 - 外键在关系表中 6 7 2、ORM正向方向连表查找: 8 正向:通过外键字段 eg: author_detial_obj.author 9 反向:通过related_name的值 eg:author_obj.detail 10 注:依赖代码见下方 11 12 3、连表操作关系: 13 1)作者删除,详情级联 - on_delete=models.CASCADE 14 2)作者删除,详情置空 - null=True, on_delete=models.SET_NULL 15 3)作者删除,详情重置 - default=0, on_delete=models.SET_DEFAULT 16 4)作者删除,详情不动 - on_delete=models.DO_NOTHING 17 注:拿作者与作者详情表举例 18 19 4、外键关联字段的参数 - 如何实现 断关联、目前表间操作关系、方向查询字段 20 i)作者详情表中的 21 author = models.OneToOneField( 22 to='Author', 23 related_name='detail', 反向查询的时候就不用表名小写了,可以写你自定义的 24 db_constraint=False, 断关联 25 on_delete=models.CASCADE 级联 26 ) 27 28 ii)图书表中的 29 publish = models.ForeignKey( 30 to='Publish', 31 related_name='books', 32 db_constraint=False, 33 on_delete=models.DO_NOTHING, 删除一方,另一方不动 34 ) 35 authors = models.ManyToManyField( 36 to='Author' 37 related_name='books', 38 db_constraint=False, 39 ) 40 注:ManyToManyField不能设置on_delete,OneToOneField、ForeignKey必须设置on_delete(django1.x系统默认级联,但是django2.x必须手动明确) 41 """
model类:
1 from django.db import models 2 3 # Create your models here. 4 5 6 # 基表,其他的表需要用到的就继承 7 class BaseModel(models.Model): 8 is_delete = models.BooleanField(default=False) 9 create_time = models.DateTimeField(auto_now_add=True) 10 11 # 设置 abstract = True 来声明基表,作为基表的Model不能再数据库中形成对应的表 12 class Meta: 13 abstract = True 14 15 # 图书管理系统表:Book,Author,AuthorDetail,Publish 16 ''' 17 Book表: name、price、img、authors、publish、is_delete、create_time 18 Publish表: name、address、is_delete、create_time 19 Author表: name、age、is_delete、create_time 20 AuthorDetail表: mobile, author、is_delete、create_time 21 ''' 22 class Book(BaseModel): 23 name = models.CharField(max_length=64) 24 price = models.DecimalField(max_digits=5,decimal_places=2) 25 img = models.ImageField(upload_to='img',default='img/default.jpg') 26 # 书籍表一对多出版社 27 publish = models.ForeignKey( 28 to='Publish', 29 db_constraint=False, # 断关联 30 related_name='books', # 反向查询字段:publish_obj.books 就能访问所有出版的书 31 on_delete=models.DO_NOTHING,# 设置连表操作关系 32 ) 33 # 就是一个方法属性,可以利用插拔式给前台返回数据 34 # 序列化插拔式属性 - 完成自定义字段名完成连表查询 35 @property 36 def publish_name(self): 37 return self.publish.name 38 39 @property 40 def author_list(self): 41 return self.authors.values('name','age','detail__mobile').all() 42 43 44 45 46 # 数据表对多对作者表 47 authors = models.ManyToManyField( 48 to='Author', 49 db_constraint=False, 50 related_name='books', 51 52 ) 53 class Meta: 54 db_table = 'book' 55 verbose_name = '书籍' 56 verbose_name_plural = verbose_name 57 def __str__(self): 58 return self.name 59 60 61 class Publish(BaseModel): 62 name = models.CharField(max_length=64) 63 address = models.CharField(max_length=64) 64 65 class Meta: 66 db_table = 'publish' 67 verbose_name = '出版社' 68 verbose_name_plural = verbose_name 69 def __str__(self): 70 return self.name 71 72 class Author(BaseModel): 73 name = models.CharField(max_length=64) 74 age = models.IntegerField() 75 76 class Meta: 77 db_table = 'author' 78 verbose_name = '作者' 79 verbose_name_plural = verbose_name 80 81 def __str__(self): 82 return self.name 83 84 85 class AuthorDetail(BaseModel): 86 mobile = models.CharField(max_length=11) 87 # 一多一作者表 88 author = models.OneToOneField( 89 to='Author', 90 db_constraint=False, # 断关联,数据库没有了联系,但是逻辑上还有 91 # 现在反向查可以直接通过自定义的这个名字 92 related_name='detail', 93 on_delete=models.DO_NOTHING, 94 95 96 ) 97 98 class Meta: 99 db_table = 'author_detail' 100 verbose_name = '作者详情' 101 verbose_name_plural = verbose_name 102 103 def __str__(self): 104 return '%s的详情'% self.author.name
三.多表序列化组件
序列化层:api/serializers.py
1 from rest_framework.serializers import ModelSerializer 2 from . import models 3 class BookModelSerializer(ModelSerializer): 4 class Meta: 5 # 需要序列化的model类, 6 model = models.Book 7 # 我要返回给前台哪些字段 也可以称为插拔式,不想反回了,就不写 8 fields = ('name','price','publish_name','author_list')
1 from rest_framework.serializers import ModelSerializer, SerializerMethodField 2 from rest_framework.exceptions import ValidationError 3 from . import models 4 5 # 可以单独作为Publish接口的序列化类,也可以作为Book序列化外键publish辅助的序列化组件 6 class PublishModelSerializer(ModelSerializer): 7 class Meta: 8 # 序列化类关联的model类 9 model = models.Publish 10 # 参与序列化的字段 11 fields = ('name', 'address') 12 13 class BookModelSerializer(ModelSerializer): 14 # 了解: 该方式设置的序列化字段,必须在fields中声明 15 # publish_address = SerializerMethodField() 16 # def get_publish_address(self, obj): 17 # return obj.publish.address 18 19 # 自定义连表深度 - 子序列化方式 - 该方式不能参与反序列化,使用在序列化反序列化共存时,不能书写 20 publish = PublishModelSerializer() 21 22 class Meta: 23 # 序列化类关联的model类 24 model = models.Book 25 # 参与序列化的字段 26 fields = ('name', 'price', 'img', 'author_list', 'publish') 27 28 # 了解知识点 29 # 所有字段 30 # fields = '__all__' 31 # 与fields不共存,exclude排除哪些字段 32 # exclude = ('id', 'is_delete', 'create_time') 33 # 自动连表深度 34 # depth = 1
视图层:api/views.py
1 # 负责请求过来的 2 from rest_framework.views import APIView 3 # 负责响应的 4 from rest_framework.response import Response 5 # 负责模型与序列化的 6 from . import models,serializers 7 8 class Book(APIView): 9 def get(self,request,*args,**kwargs): 10 pk = kwargs.get('pk') 11 if pk: 12 try: 13 book_obj = models.Book.objects.get(pk=pk) 14 book_data = serializers.BookModelSerializer(book_obj).data 15 except: 16 return Response({ 17 'status':1, 18 'msg':'书籍不存在', 19 }) 20 else: 21 book_query = models.Book.objects.all() # 因为是一个列表,有多个值 22 book_data = serializers.BookModelSerializer(book_query,many=True).data 23 return Response({ 24 'status': 0, 25 'msg': 'ok', 26 'results':book_data 27 })
路由层:api/urls.py
1 子路由: 2 from django.conf.urls import url 3 from . import views 4 5 # 子路由 6 urlpatterns = [ 7 url(r'^books/$',views.Book.as_view()), 8 url(r'^books/(?P<pk>.*)/$',views.Book.as_view()), 9 10 ] 11 12 主路由: 13 # 路由分发先导入include 14 from django.conf.urls import url,include 15 from django.contrib import admin 16 from django.conf import settings 17 from django.views.static import serve 18 19 urlpatterns = [ 20 url(r'^admin/', admin.site.urls), 21 # 路由分发 22 url(r'^api/', include('api.urls')), 23 # 把放置图片的文件接口开放 24 url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}), 25 ]
五.多表反序列化组件
序列化层:api/serializers.py
注意点: ModelSerializer类已经帮我们实现了 create 与 update 方法
extra_kwargs 用来完成反序列化字段的 系统校验规则
1 class BookModelDeserializer(ModelSerializer): 2 class Meta: 3 # 需要序列化的model类, 4 model = models.Book 5 # 我要返回给前台哪些字段 也可以称为插拔式,不想反回了,就不写 6 fields = ('name', 'price','publish','authors') 7 8 # extra_kwargs 用来完成反序列化字段的系统校验规则 9 # required=True 就是代表这个字段也要校验,error_messages自定义报错信息 10 extra_kwargs = { 11 'name':{ 12 'required':True, 13 'min_length':1, 14 'error_messages':{ 15 'required':'必填项', 16 'min_length':'太短', 17 } 18 } 19 } 20 21 #局部钩子 22 def validate_name(self,value): 23 # 书名不能包含g字符 24 if 'g' in value.lower(): 25 raise ValidationError('书名不能包含g') 26 # 如果不包含g就可以出版 27 return value 28 29 #全局钩子 同一个出版社不能出名字相同的书 30 def validate(self, attrs): 31 publish = attrs.get('publish') 32 name = attrs.get('name') 33 # 如果判断成立就抛异常 34 if models.Book.objects.filter(name=name,publish=publish): 35 # 全局的可以规定一下key和value 36 raise ValidationError({'book':'该书已存在'}) 37 return attrs
视图层:api/views.py
1 class Book(APIView): 2 def post(self, request, *args, **kwargs): 3 request_data = request.data 4 book_ser = serializers.BookModelDeserializer(data=request_data) 5 # raise_exception=True:当校验失败,马上终止当前视图方法,抛异常返回给前台 6 book_ser.is_valid(raise_exception=True) 7 book_obj = book_ser.save() 8 return Response({ 9 'status': 0, 10 'msg': 'ok', 11 'results': serializers.BookModelSerializer(book_obj).data 12 })
路由层:api/urls.py
1 主路由: 2 # 路由分发先导入include 3 from django.conf.urls import url,include 4 from django.contrib import admin 5 from django.conf import settings 6 from django.views.static import serve 7 8 urlpatterns = [ 9 url(r'^admin/', admin.site.urls), 10 # 路由分发 11 url(r'^api/', include('api.urls')), 12 # 把放置图片的文件接口开放 13 url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}), 14 ] 15 16 子路由: 17 from django.conf.urls import url 18 19 from . import views 20 21 urlpatterns = [ 22 url(r'^books/$', views.Book.as_view()), 23 url(r'^books/(?P<pk>.*)/$', views.Book.as_view()), 24 25 ]
六.序列化反序列化整合(重点)里面包含了群增接口实现,单删群删接口实现
序列化层:api/serializers.py
注意点:
""" 1) fields中设置所有序列化与反序列化字段 2) extra_kwargs划分只序列化或只反序列化字段 write_only:只反序列化 read_only:只序列化 自定义字段默认只序列化(read_only) 自定义字段也可以不需要写,因为自定义的字段是只读,数据库不需要,默认就是read_only 3) 设置反序列化所需的 系统、局部钩子、全局钩子 等校验规则 """
1 # 序列化与反序列化整合版本 2 class V2BookModelSerializer(ModelSerializer): 3 class Meta: 4 # 需要序列化的model类, 5 model = models.Book 6 # 我要返回给前台哪些字段 也可以称为插拔式,不想反回了,就不写 7 # 因为现在序列化和反序列化整合在一起了,所以不管序列化数据还是反序列化数据都要写上 8 # 但是也出现了一些问题,就是序列化的时候,反序列化的字段也出现了,这个时候就需要用到了extra_kwargs 9 fields = ('name', 'price','img','author_list','publish_name','publish','authors') 10 extra_kwargs = { 11 'name': { 12 'required': True, 13 'min_length': 1, 14 'error_messages': { 15 'required': '必填项', 16 'min_length': '太短', 17 } 18 }, 19 'publish':{ 20 # 只参加反序列化的字段 21 'write_only':True 22 }, 23 'author':{ 24 'write_only':True 25 }, 26 # 只参加序列化的字段 27 'img':{ 28 'read_only':True 29 }, 30 # 下面俩个字段也可以不需要,因为自定义的字段是只读,数据库不需要,默认就是read_only 31 'author_list': { 32 'read_only': True 33 }, 34 'publish_name': { 35 'read_only': True 36 } 37 38 } 39 # 局部钩子 40 41 def validate_name(self, value): 42 # 书名不能包含g字符 43 if 'g' in value.lower(): 44 raise ValidationError('书名不能包含g') 45 # 如果不包含g就可以出版 46 return value 47 48 # 全局钩子 同一个出版社不能出名字相同的书 49 50 def validate(self, attrs): 51 publish = attrs.get('publish') 52 name = attrs.get('name') 53 # 如果判断成立就抛异常 54 if models.Book.objects.filter(name=name, publish=publish): 55 # 全局的可以规定一下key和value 56 raise ValidationError({'book': '该书已存在'}) 57 return attrs
视图层:api/views.py
注意点:
单查:有pk 群查:无pk # 单增:传的数据是与model对应的字典 # 群增:传的数据是 装多个model对应字典的列表 # 单删:有pk # 群删:有pks 群删的形式:{"pks":[1,2,3]} # 单删和群删不需要序列化的处理,因为它就是改一下字段 如果是群增和群删需要指定一个参数:many=True
1 # 负责请求过来的 2 from rest_framework.views import APIView 3 # 负责响应的 4 from rest_framework.response import Response 5 # 负责模型与序列化的 6 from . import models,serializers 7 8 # 序列化与反序列化整合版本 9 class V2Book(APIView): 10 # 单查:有pk 11 # 群查:无pk 12 def get(self,request,*args,**kwargs): 13 pk = kwargs.get('pk') 14 if pk: 15 try: 16 book_obj = models.Book.objects.get(pk=pk) 17 book_data = serializers.V2BookModelSerializer(book_obj).data 18 except: 19 return Response({ 20 'status':1, 21 'msg':'书籍不存在', 22 }) 23 else: 24 book_query = models.Book.objects.all() # 因为是一个列表,有多个值 25 book_data = serializers.V2BookModelSerializer(book_query,many=True).data 26 return Response({ 27 'status': 0, 28 'msg': 'ok', 29 'results':book_data 30 }) 31 32 # 单增:传的数据是与model对应的字典 33 # 群增:传的数据是 装多个model对应字典的列表 34 def post(self, request, *args, **kwargs): 35 request_data = request.data 36 # 如何完成群增,可以把问题想的简单一点,就字典就是单增,列表就是群增 37 # 判断如果request_data就走单增的逻辑 38 if isinstance(request_data,dict): 39 many = False # 单增就是many=False ,也就是说不需要many 40 elif isinstance(request_data, list): 41 # 如果是list就走群增的逻辑 ,只要写了many=True就可以序列化和反序列化多个 42 many = True 43 else: 44 # 如果都不成立就是数据有问题 45 return Response({ 46 'status': 1, 47 'msg': '数据有误', 48 }) 49 book_ser = serializers.V2BookModelSerializer(data=request_data, many=many) 50 # 当校验失败,马上终止当前视图方法,抛异常返回给前台 51 book_ser.is_valid(raise_exception=True) 52 book_result = book_ser.save() # 序列化的结果调用保存方法 53 return Response({ 54 'status': 0, 55 'msg': 'ok', # 如果要是多增,返回给前台数据也需要many=True 56 'results': serializers.V2BookModelSerializer(book_result,many=many).data 57 }) 58 59 # 单删:有pk 60 # 群删:有pks 群删的形式:{"pks":[1,2,3]} 61 # 单删和群删不需要序列化的处理,因为它就是改一下字段 62 def delete(self, request, *args, **kwargs): 63 pk = kwargs.get('pk') 64 if pk: 65 # 把单删和群删统一了 66 pks = [pk] 67 else: 68 pks = request.data.get('pks') # 不能重复删除 69 if models.Book.objects.filter(pk__in=pks,is_delete=False).update(is_delete=True): 70 return Response({ 71 'status':0, 72 'msg':'删除成功', 73 }) 74 else: 75 return Response({ 76 'status': 1, 77 'msg': '删除失败', 78 })
路由层:api/urls.py
urlpatterns = [ url(r'^v2/books/$', views.V2Book.as_view()), url(r'^v2/books/(?P<pk>.*)/$', views.V2Book.as_view()), ]
class BookModelDeserializer(ModelSerializer):
class Meta:
# 需要序列化的model类,
model = models.Book
# 我要返回给前台哪些字段 也可以称为插拔式,不想反回了,就不写
fields = ('name', 'price','publish','authors')
# extra_kwargs 用来完成反序列化字段的系统校验规则
# required=True 就是代表这个字段也要校验,error_messages自定义报错信息
extra_kwargs = {
'name':{
'required':True,
'min_length':1,
'error_messages':{
'required':'必填项',
'min_length':'太短',
}
}
}
#局部钩子
def validate_name(self,value):
# 书名不能包含g字符
if 'g' in value.lower():
raise ValidationError('书名不能包含g')
# 如果不包含g就可以出版
return value
#全局钩子 同一个出版社不能出名字相同的书
def validate(self, attrs):
publish = attrs.get('publish')
name = attrs.get('name')
# 如果判断成立就抛异常
if models.Book.objects.filter(name=name,publish=publish):
# 全局的可以规定一下key和value
raise ValidationError({'book':'该书已存在'})
return attrs