配置: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 pymysql pymysql.install_as_MySQLdb() """ LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_L10N = True USE_TZ = False MEDIA_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, include from django.contrib import admin from django.views.static import serve from django.conf import settings urlpatterns = [ 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 url from . import views urlpatterns = [ ] |
多表设计
1
2
3
4
5
6
7
8
9
10
11
12
13
|
""" Book表:name、price、img、authors、publish、is_delete、create_time Publish表:name、address、is_delete、create_time Author表:name、age、is_delete、create_time AuthorDetail表: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_time Publish表: name、address、is_delete、create_time Author表: name、age、is_delete、create_time AuthorDetail表: 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 = True class 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 .name class 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 .name class 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 .name class 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, SerializerMethodField from rest_framework.exceptions import ValidationError from . 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) 单整体改,说明前台要提供修改的数据,那么数据就需要校验,校验的数据应该在实例化“序列化类对象”时,赋值给data 2)修改,就必须明确被修改的模型类对象,并在实例化“序列化类对象”时,赋值给instance 3)整体修改,所有校验规则有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_class class 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 Response class 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) |