表断关系,和modlesserializers序列化,反序列化
配置:settings.py
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
INSTALLED_APPS = [ # ... 'rest_framework',]DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'dg_proj', 'USER': 'root', 'PASSWORD': '123', }}"""任何__init__文件import pymysqlpymysql.install_as_MySQLdb()"""LANGUAGE_CODE = 'zh-hans'TIME_ZONE = 'Asia/Shanghai'USE_I18N = TrueUSE_L10N = TrueUSE_TZ = FalseMEDIA_URL = '/media/'MEDIA_ROOT = os.path.join(BASE_DIR, 'media') |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 主from django.conf.urls import url, includefrom django.contrib import adminfrom django.views.static import servefrom django.conf import settingsurlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^api/', include('api.urls')), url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),]# 子from django.conf.urls import urlfrom . import viewsurlpatterns = [ ] |
多表设计
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
"""Book表:name、price、img、authors、publish、is_delete、create_timePublish表:name、address、is_delete、create_time Author表:name、age、is_delete、create_timeAuthorDetail表:mobile, author、is_delete、create_time BaseModel基表 is_delete、create_time上面四表继承基表,可以继承两个字段""" |
基表
|
1
2
3
4
5
6
7
|
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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
"""1、外键位置: 一对多 - 外键放多的一方 一对一 - 从逻辑正反向考虑,如作者表与作者详情表,作者删除级联删除详情,详情删除作者依旧存在,所以建议外键在详情表中 多对多 - 外键在关系表中 2、ORM正向方向连表查找: 正向:通过外键字段 eg: author_detial_obj.author 反向:通过related_name的值 eg:author_obj.detail 注:依赖代码见下方 3、连表操作关系: 1)作者删除,详情级联 - on_delete=models.CASCADE 2)作者删除,详情置空 - null=True, on_delete=models.SET_NULL 3)作者删除,详情重置 - default=0, on_delete=models.SET_DEFAULT 4)作者删除,详情不动 - on_delete=models.DO_NOTHING 注:拿作者与作者详情表举例 4、外键关联字段的参数 - 如何实现 断关联、目前表间操作关系、方向查询字段 i)作者详情表中的 author = models.OneToOneField( to='Author', related_name='detail', db_constraint=False, on_delete=models.CASCADE ) ii)图书表中的 publish = models.ForeignKey( to='Publish', related_name='books', db_constraint=False, on_delete=models.DO_NOTHING, ) authors = models.ManyToManyField( to='Author' related_name='books', db_constraint=False, ) 注:ManyToManyField不能设置on_delete,OneToOneField、ForeignKey必须设置on_delete(django1.x系统默认级联,但是django2.x必须手动明确)""" |
model类
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
from django.db import models# 图书管理系统:Book、Author、AuthorDetail、Publish"""Book表: name、price、img、authors、publish、is_delete、create_timePublish表: name、address、is_delete、create_timeAuthor表: name、age、is_delete、create_timeAuthorDetail表: mobile, author、is_delete、create_time"""# 1) 基表class BaseModel(models.Model): is_delete = models.BooleanField(default=False) create_time = models.DateTimeField(auto_now_add=True) # 作为基表的Model不能在数据库中形成对应的表,设置 abstract = True class Meta: abstract = Trueclass Book(BaseModel): """name、price、img、authors、publish、is_delete、create_time""" 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', # 反向查询字段:publish_obj.books 就能访问所有出版的书 on_delete=models.DO_NOTHING, # 设置连表操作关系 ) 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.nameclass Publish(BaseModel): """name、address、is_delete、create_time""" 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 self.nameclass Author(BaseModel): """name、age、is_delete、create_time""" 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 self.nameclass AuthorDetail(BaseModel): """mobile, author、is_delete、create_time""" mobile = models.CharField(max_length=11) author = models.OneToOneField( to='Author', db_constraint=False, related_name='detail', on_delete=models.CASCADE, ) class Meta: db_table = 'author_detail' verbose_name = '作者详情' verbose_name_plural = verbose_name def __str__(self): return '%s的详情' % self.author.name |
序列化
序列化层:api/serializers.py
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
from rest_framework.serializers import ModelSerializer, SerializerMethodFieldfrom rest_framework.exceptions import ValidationErrorfrom . import models# 可以单独作为Publish接口的序列化类,也可以作为Book序列化外键publish辅助的序列化组件class PublishModelSerializer(ModelSerializer): class Meta: model = models.Publish fields = ('name', 'address')class BookModelSerializer(ModelSerializer): # 了解: 该方式设置的序列化字段,必须在fields中声明 # publish_address = SerializerMethodField() # def get_publish_address(self, obj): # return obj.publish.address # 自定义连表深度 - 子序列化方式 - 该方式不能参与反序列化,使用在序列化反序列化共存时,不能书写 publish = PublishModelSerializer() class Meta: # 序列化类关联的model类 model = models.Book # 参与序列化的字段 fields = ('name', 'price', 'img', 'author_list', 'publish') # 了解知识点 # 所有字段 # fields = '__all__' # 与fields不共存,exclude排除哪些字段 # exclude = ('id', 'is_delete', 'create_time') # 自动连表深度 # depth = 1 |
视图层:api/views.py
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class Book(APIView): def get(self, request, *args, **kwargs): pk = kwargs.get('pk') if pk: try: book_obj = models.Book.objects.get(pk=pk, is_delete=False) book_data = serializers.BookModelSerializer(book_obj).data except: return Response({ 'status': 1, 'msg': '书籍不存在' }) else: book_query = models.Book.objects.filter(is_delete=False).all() book_data = serializers.BookModelSerializer(book_query, many=True).data return Response({ 'status': 0, 'msg': 'ok', 'results': book_data }) |
路由层:api/urls.py
|
1
2
3
4
|
urlpatterns = [ url(r'^books/$', views.Book.as_view()), url(r'^books/(?P<pk>.*)/$', views.Book.as_view()),] |
反序列化
序列化层:api/serializers.py
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
class BookModelDeserializer(ModelSerializer): class Meta: model = models.Book fields = ('name', 'price', 'publish', 'authors') # extra_kwargs 用来完成反序列化字段的 系统校验规则 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书不能出版') return value # 全局钩子 def validate(self, attrs): publish = attrs.get('publish') name = attrs.get('name') if models.Book.objects.filter(name=name, publish=publish): raise ValidationError({'book': '该书已存在'}) return attrs # ModelSerializer类已经帮我们实现了 create 与 update 方法 |
视图层:api/views.py
|
1
2
3
4
5
6
7
8
9
10
11
12
|
class Book(APIView): def post(self, request, *args, **kwargs): request_data = request.data book_ser = serializers.BookModelDeserializer(data=request_data) # raise_exception=True:当校验失败,马上终止当前视图方法,抛异常返回给前台 book_ser.is_valid(raise_exception=True) book_obj = book_ser.save() return Response({ 'status': 0, 'msg': 'ok', 'results': serializers.BookModelSerializer(book_obj).data }) |
路由层:api/urls.py
|
1
2
3
4
|
urlpatterns = [ url(r'^books/$', views.Book.as_view()), url(r'^books/(?P<pk>.*)/$', views.Book.as_view()),] |
序列化与反序列化整合(重点)
序列化层:api/serializers.py
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
"""1) fields中设置所有序列化与反序列化字段2) extra_kwargs划分只序列化或只反序列化字段 write_only:只反序列化 read_only:只序列化 自定义字段默认只序列化(read_only)3) 设置反序列化所需的 系统、局部钩子、全局钩子 等校验规则"""class V2BookModelSerializer(ModelSerializer): class Meta: model = models.Book fields = ('name', 'price', 'img', 'author_list', 'publish_name', 'publish', 'authors') extra_kwargs = { 'name': { 'required': True, 'min_length': 1, 'error_messages': { 'required': '必填项', 'min_length': '太短', } }, 'publish': { 'write_only': True }, 'authors': { 'write_only': True }, 'img': { 'read_only': True, }, 'author_list': { 'read_only': True, }, 'publish_name': { 'read_only': True, } } def validate_name(self, value): # 书名不能包含 g 字符 if 'g' in value.lower(): raise ValidationError('该g书不能出版') return value def validate(self, attrs): publish = attrs.get('publish') name = attrs.get('name') if models.Book.objects.filter(name=name, publish=publish): raise ValidationError({'book': '该书已存在'}) |
return attrs
视图层:api/views.py
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
class V2Book(APIView): # 单查:有pk # 群查:无pk def get(self, request, *args, **kwargs): pk = kwargs.get('pk') if pk: try: book_obj = models.Book.objects.get(pk=pk, is_delete=False) book_data = serializers.V2BookModelSerializer(book_obj).data except: return Response({ 'status': 1, 'msg': '书籍不存在' }) else: book_query = models.Book.objects.filter(is_delete=False).all() book_data = serializers.V2BookModelSerializer(book_query, many=True).data return Response({ 'status': 0, 'msg': 'ok', 'results': book_data }) # 单增:传的数据是与model对应的字典 # 群增:传的数据是 装多个 model对应字典 的列表 def post(self, request, *args, **kwargs): request_data = request.data if isinstance(request_data, dict): many = False elif isinstance(request_data, list): many = True else: return Response({ 'status': 1, 'msg': '数据有误', }) book_ser = serializers.V2BookModelSerializer(data=request_data, many=many) # 当校验失败,马上终止当前视图方法,抛异常返回给前台 book_ser.is_valid(raise_exception=True) book_result = book_ser.save() return Response({ 'status': 0, 'msg': 'ok', 'results': serializers.V2BookModelSerializer(book_result, many=many).data }) # 单删:有pk # 群删:有pks | {"pks": [1, 2, 3]} def delete(self, request, *args, **kwargs): pk = kwargs.get('pk') if pk: pks = [pk] else: pks = request.data.get('pks') if models.Book.objects.filter(pk__in=pks, is_delete=False).update(is_delete=True): return Response({ 'status': 0, 'msg': '删除成功', }) return Response({ 'status': 1, 'msg': '删除失败', }) |
路由层:api/urls.py
|
1
2
3
4
|
urlpatterns = [ url(r'^v2/books/$', views.V2Book.as_view()), url(r'^v2/books/(?P<pk>.*)/$', views.V2Book.as_view()),] |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
"""1) 单整体改,说明前台要提供修改的数据,那么数据就需要校验,校验的数据应该在实例化“序列化类对象”时,赋值给data2)修改,就必须明确被修改的模型类对象,并在实例化“序列化类对象”时,赋值给instance3)整体修改,所有校验规则有required=True的字段,都必须提供,因为在实例化“序列化类对象”时,参数partial默认为False注:如果partial值设置为True,就是可以局部改1)单整体修改,一般用put请求:V2BookModelSerializer( instance=要被更新的对象, data=用来更新的数据, partial=默认False,必须的字段全部参与校验)2)单局部修改,一般用patch请求:V2BookModelSerializer( instance=要被更新的对象, data=用来更新的数据, partial=设置True,必须的字段都变为选填字段) 注:partial设置True的本质就是使字段 required=True 校验规则失效""" |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class V2Book(APIView): # 单整体改: 对 v2/books/(pk)/ 传的数据是与model对应的字典{name|price|publish|authors} def put(self, request, *args, **kwargs): request_data = request.data pk = kwargs.get('pk') old_book_obj = models.Book.objects.filter(pk=pk).first() # 目的:将众多数据的校验交给序列化类来处理 - 让序列化类扮演反序列化角色,校验成功后,序列化类来帮你入库 book_ser = serializers.V2BookModelSerializer(instance=old_book_obj, data=request_data, partial=False) book_ser.is_valid(raise_exception=True) # 校验通过,完成数据的更新:要更新的目标,用来更新的新数据 book_obj = book_ser.save() return Response({ 'status': 0, 'msg': 'ok', 'results': serializers.V2BookModelSerializer(book_obj).data }) |
单与整体局部修改
序列化层:serializers.py
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# 重点:ListSerializer与ModelSerializer建立关联的是:# ModelSerializer的Meta类的 - list_serializer_classclass V2BookListSerializer(ListSerializer): def update(self, instance, validated_data): # print(instance) # 要更新的对象们 # print(validated_data) # 更新的对象对应的数据们 # print(self.child) # 服务的模型序列化类 - V2BookModelSerializer for index, obj in enumerate(instance): self.child.update(obj, validated_data[index]) return instance # 原模型序列化类变化class V2BookModelSerializer(ModelSerializer): class Meta: # ... # 群改,需要设置 自定义ListSerializer,重写群改的 update 方法 list_serializer_class = V2BookListSerializer # ... |
视图层:views.py
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
class V2Book(APIView): # 单局部改:对 v2/books/(pk)/ 传的数据,数据字段key都是选填 # 群局部改:对 v2/books/ # 请求数据 - [{pk:1, name:123}, {pk:3, price:7}, {pk:7, publish:2}] def patch(self, request, *args, **kwargs): request_data = request.data pk = kwargs.get('pk') # 将单改,群改的数据都格式化成 pks=[要需要的对象主键标识] | request_data=[每个要修改的对象对应的修改数据] if pk and isinstance(request_data, dict): # 单改 pks = [pk, ] request_data = [request_data, ] elif not pk and isinstance(request_data, list): # 群改 pks = [] for dic in request_data: # 遍历前台数据[{pk:1, name:123}, {pk:3, price:7}, {pk:7, publish:2}],拿一个个字典 pk = dic.pop('pk', None) if pk: pks.append(pk) else: return Response({ 'status': 1, 'msg': '数据有误', }) else: return Response({ 'status': 1, 'msg': '数据有误', }) # pks与request_data数据筛选, # 1)将pks中的没有对应数据的pk与数据已删除的pk移除,request_data对应索引位上的数据也移除 # 2)将合理的pks转换为 objs objs = [] new_request_data = [] for index,pk in enumerate(pks): book_obj = models.Book.objects.filter(pk=pk).first() if book_obj: objs.append(book_obj) new_request_data.append(request_data[index]) book_ser = serializers.V2BookModelSerializer(instance=objs, data=new_request_data, partial=True, many=True) book_ser.is_valid(raise_exception=True) book_objs = book_ser.save() return Response({ 'status': 0, 'msg': 'ok', 'results': serializers.V2BookModelSerializer(book_objs, many=True).data }) |
视图类传递参数给序列化类
|
1
2
3
4
5
6
7
8
|
# 1)在视图类中实例化序列化对象时,可以设置context内容# 2)在序列化类中的局部钩子、全局钩子、create、update方法中,都可以用self.context访问视图类传递过来的内容# 需求:# 1) 在视图类中,可以通过request得到登陆用户request.user# 2) 在序列化类中,要完成数据库数据的校验与入库操作,可能会需要知道当前的登陆用户,但序列化类无法访问request# 3) 在视图类中实例化序列化对象时,将request对象传递进去 |
视图层:views.py
|
1
2
3
4
5
6
7
8
9
10
|
class Book(APIView): def post(self, request, *args, **kwargs): book_ser = serializers.BookModelSerializer(data=request_data,context={'request':request}) book_ser.is_valid(raise_exception=True) book_result = book_ser.save() return Response({ 'status': 0, 'msg': 'ok', 'results': serializers.BookModelSerializer(book_result).data }) |
序列化层:serializers.py
|
1
2
3
4
5
6
7
|
class BookModelSerializer(ModelSerializer): class Meta: model = models.Book fields = ('name', 'price') def validate_name(self, value): print(self.context.get('request').method) return value |
二次封装Response类
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
"""Response({ 'status': 0, 'msg': 'ok', 'results': [], 'token': '' # 有这样的额外的key-value数据结果},status=http_status,headers=headers,exception=True|False)APIResponse() => Response({'status': 0,'msg': 'ok'})"""from rest_framework.response import Responseclass APIResponse(Response): def __init__(self, data_status=0, data_msg='ok', results=None, http_status=None, headers=None, exception=False, **kwargs): # data的初始状态:状态码与状态信息 data = { 'status': data_status, 'msg': data_msg, } # data的响应数据体 # results可能是False、0等数据,这些数据某些情况下也会作为合法数据返回 if results is not None: data['results'] = results # data响应的其他内容 # if kwargs is not None: # for k, v in kwargs.items(): # setattr(data, k, v) data.update(kwargs) super().__init__(data=data, status=http_status, headers=headers, exception=exception) |

浙公网安备 33010602011771号