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和valueraise ValidationError({'book':'该书已存在'}) return attrs
