RESTful 规范
一, 前言
先建立数据库, 并添加相应的数据, 用来后面序列化使用
1, 建立数据库模型
为数据建立相应的数据库模型, 并且有一对一, 多对多, 外键关联.
- from django.db import models
- class UserGroup(models.Model):
- title = models.CharField(max_length=32)
- class UserInfo(models.Model):
- user_type_choices = (
- (1,'普通用户'),
- (2,'VIP'),
- (3,'SVIP'),
- )
- user_type = models.IntegerField(choices=user_type_choices)
- username = models.CharField(max_length=32,unique=True)
- password = models.CharField(max_length=64)
- group = models.ForeignKey("UserGroup", on_delete=models.CASCADE)
- roles = models.ManyToManyField("Role")
- class UserToken(models.Model):
- user = models.OneToOneField(to='UserInfo', on_delete=models.CASCADE)
- token = models.CharField(max_length=64)
- class Role(models.Model):
- title = models.CharField(max_length=32)
并执行数据库迁移操作
- python manage.py makemigrations
- python manage.py migrate
2, 添加少量数据
当数据迁移执行之后, 会在 SQLite 数据库中生活如下表
多对多关系的时候, django 自动生成第三张表维系表关系, 字段分别是 userinfo 和 role 的 id, 其中 api_userinfo_roles 为多对多关系生成的表.
在表中添加少量数据
<1>,UserInfo 表
<2>,UserGroup 表
3,UserInfo_roles 表
4,roles 表
二, 序列化的简单使用
1, 不使用序列化
- <1>, 路由
- from django.conf.urls import url
- from .views import RoleView
- urlpatterns = [
- url(r'^role/$', RoleView.as_view()),
- ]
- <2>, 视图
- from rest_framework.views import APIView
- from .models import Role
- import JSON
- class RoleView(APIView):
- def get(self, request, *args, **kwargs):
- roles = Role.objects.all().values('id' ,'title')
- print(roles, type(roles)) # roles 为一个 QuerySet 对象
- ret_roles = JSON.dumps(list(roles), ensure_ascii=False) # 多条数据
- return HttpResponse(ret_roles)
2, 简单使用 Serializer
- <1>, 定义序列化类
- from rest_framework import serializers
- class RoleSerializer(serializers.Serializer):
- id = serializers.IntegerField()
- title = serializers.CharField()
- <2>, 视图
- class RoleView(APIView):
- def get(self, request, *args, **kwargs):
- # 多条数据
- # 将序列化后的数据都存到 ser.data(OrderDict 有序字典中) 中
- # roles = Role.objects.all()
- # ser_roles = RoleSerializer(instance=roles, many=True)
- # print(ser_roles, type(ser_roles)) # ListSerializer 对象
- # ret_roles = JSON.dumps(ser_roles.data, ensure_ascii=False) # 多条数据
- # return HttpResponse(ret_roles)
- # 单条数据
- role = Role.objects.all().first()
- ser_role = RoleSerializer(instance=role, many=False) # RoleSerializer 对象
- print(ser_role, type(ser_role)) # 单条数据
- ret_roles = JSON.dumps(ser_role.data, ensure_ascii=False)
- return HttpResponse(ret_roles)
总结: 上面可以实现数据的简单序列化, 但是无法自定义字段, 也无法对数据进行处理, 不方便, 限制较大
三, 进一步使用 Serializer
1, 路由
- from django.conf.urls import url
- from .views import UserInfo
- urlpatterns = [
- url(r'^userinfo/$', UserInfo.as_view()),
- ]
2, 视图
- class UserInfoView(APIView):
- def get(self, request, *args, **kwargs):
- users = UserInfo.objects.all()
- users_ser = UserSerializer(instance=users, many=True)
- users_ret = JSON.dumps(users_ser.data, ensure_ascii=False)
- # print(users_ser.data, type(users_ser.data), type(users_ser.data[0]))
- return HttpResponse(users_ret)
3, 使用 serializer
- class UserSerializer(serializers.Serializer):
- type = serializers.IntegerField(source='user_type')
- user_type = serializers.CharField(source='get_user_type_display') # choices 字段显示
- username = serializers.CharField()
- pwd = serializers.CharField(source='password') # 自定义 serializer 中的 key 值
- group_title = serializers.CharField(source='group.title') # 关联对象属性
- roles = serializers.CharField(source='roles.all') # 多对多关系
- roles_info = serializers.SerializerMethodField() # 表示自定义方法, 显示 querytset 对象详情
- def get_roles_info(self, row):
- roles = row.roles.all()
- ret = []
- for item in roles:
- ret.append(
- {
- 'id': item.id,
- 'title': item.title
- }
- )
- return ret
注:
如果没有指定在 Filed 中没有定义 source 参数的时候, 就自动与数据库 modles 定义的字段进行匹配, 如上面的 userrname 字段. 在定义字段后, Serializer 类中可以自定义属性如 type
当 models 中是以 choice 定义时: 需要定义 source 参数定义
get_字段名_display
才能获取数据, 这与在模板语言中的用法一样, 如上面的 user_type
外键关联的时候, 直接 外键字段名. 属性 的方式定义传参给 source 参数即可, 如上面的 group.title
对于 roles 字段, 想直接获取所有的对象, 但是无法做到细粒度的将对象的所有属性展示出来, 只能获取到 QuerySet 对象
自定义字段, 处理数据, 如 roles_info 获取所有的 role 对象的属性, 处理数据可以定义方法, 方法名格式为 get_属性, 并 return 值最终返回值
执行结果:
另: 自定义字段也可以采取继承的方式, 如:
- class UsernameField(serializers.CharField):
- def to_representation(self, value):
- return 'username' + value
重写 to_representation 方法, value 为从数据库取出的值, 然后对 value 进行处理, 在返回即可
并将序列化类中的 username 改为
username = UsernameField()
四, 使用 ModelSerializer 组件
1, 包装 Serializer
- class UserSerializer(serializers.ModelSerializer):
- user_type = serializers.CharField(source='get_user_type_display')
- roles = serializers.CharField(source='roles.all') # 外键关联
- roles_info = serializers.SerializerMethodField() # 表示自定义方法, 显示外键关联详情
- group_title = serializers.CharField(source='group.title')
- def get_roles_info(self, row):
- roles = row.roles.all()
- ret = []
- for item in roles:
- ret.append(
- {
- 'id': item.id,
- 'title': item.title
- }
- )
- return ret
- class Meta:
- model = UserInfo
- # fields = '__all__' # 为全部的字段做匹配
- fields = ['user_type', 'username', 'password', 'group', 'group_title', 'roles', 'roles_info'] # 自定义需要展示的字段
- extra_kwargs = {'group': {'source': 'group_id'}}
ModelSerializer 与 Serializer 区别在于: ModelSerializer 支持了 Serializer 中所有的操作, 并且通过自动生成所有数据字段与序列化类的一一对应关系, 而不用自己手动添加.
即 Serializer 是 ModelSerializer 的父类, 所以 ModelSerializer 才会支持 Serializer 的所有操作
返回结果
2,ModelSerializer 深度控制
在上面, 看到在进行连表查询的时候, 只能获取到外键关联对象, 在当前表中存储的 id, 怎样拿到外键关联对象的具体信息.
- class UserSerializer(serializers.ModelSerializer):
- # 自动向内部进行深度查询 depth 表示查询层数
- class Meta:
- model = UserInfo
- # fields = "__all__"
- fields = ['id','username','password','group','roles']
- depth = 1 # 0 ~ 10 默认的 depth 为 0
- class UserInfoView(APIView):
- def get(self, request, *args, **kwargs):
- users = UserInfo.objects.all()
- users_ser = UserSerializer(instance=users, many=True)
- users_ret = JSON.dumps(users_ser.data, ensure_ascii=False)
- # print(users_ser.data, type(users_ser.data), type(users_ser.data[0]))
- return HttpResponse(users_ret)
注: 这里的 depth 就表示深度查询的层数, 默认的层数为 0, 层数越多查询效率越慢.
返回结果
3, 自动生成链接
在上面我们看到, 在返回组 group 的时候是返回该组的 id, 或者用 depth 深度控制, 返回组的详细信息. 在 restful 规范中, 规定应该给出相应的详情链接, 可以通过 url 拼接, 在 django REST framework 中也有相对应的实现.
首先改写一下用户信息序列化类, 使之能够提供用户组详情的有关 url
- class UserSerializer(serializers.ModelSerializer):
- group = serializers.HyperlinkedIdentityField(view_name='api:gp', lookup_field='group_id', lookup_url_kwarg='xxx')
- # view_name 参数 进行传参的时候是参考路由匹配中的 name 与 namespace 参数
- # lookeup_field 参数是根据在 UserInfo 表中的连表查询字段 group_id
- # look_url_kwarg 参数在做 url 反向解析的时候会用到
- class Meta:
- model = UserInfo
- fields = ['id','username','password','group','roles']
- depth = 1 # 0 ~ 10
- class UserInfoView(APIView):
- def get(self, request, *args, **kwargs):
- users = UserInfo.objects.all()
- users_ser = UserSerializer(instance=users, many=True, context={'request': request}) # 在做链接的时候需要添加 context 参数
- users_ret = JSON.dumps(users_ser.data, ensure_ascii=False)
- # print(users_ser.data, type(users_ser.data), type(users_ser.data[0]))
- return HttpResponse(users_ret)
- # 添加 group 序列化类
- class GroupSerializer(serializers.ModelSerializer):
- class Meta:
- model = UserGroup
- fields = "__all__"
- # 返回用户组的详细信息
- class GroupView(APIView):
- def get(self,request,*args,**kwargs):
- pk = kwargs.get('xxx')
- obj = UserGroup.objects.filter(pk=pk).first()
- ser = GroupSerializer(instance=obj,many=False)
- ret = JSON.dumps(ser.data,ensure_ascii=False)
- return HttpResponse(ret)
返回结果
当我们点解用户组详情链接后, 返回结果
4, 校验数据
序列化不仅可以做数据的返回, 也可以对前端提交的数据进行校验.
- <1>, 类方法检验
- class TitleValidator(object):
- def __init__(self, base):
- self.base = base
- def __call__(self, value):
- if not value.startswith(self.base):
- message = '标题必须以 %s 为开头.' % self.base
- raise serializers.ValidationError(message)
- def set_context(self, serializer_field):
- # 执行验证之前调用, serializer_fields 是当前字段对象
- pass
- class UserGroupSerializer(serializers.Serializer):
- title = serializers.CharField(error_messages={'required': '标题不能为空'}, validators=[TitleValidator('Django'),])
- class UserGroupView(APIView):
- def post(self,request,*args,**kwargs):
- print(request.data)
- ser = UserGroupSerializer(data=request.data)
- if ser.is_valid():
- print(ser.validated_data['title'])
- else:
- print(ser.errors)
- return HttpResponse('提交数据')
上面的 TitileValidator 类封装了对 request.data 前端传来的数据的校验, title 相对应的是数据中的 key 为 title 的值. TitileValidator 实现了 call() 特殊方法, 并把具体的验证逻辑封装到里边, 是一个可直接调用的对象. 而 self.base 则为具体的 title 对应的数据, 进行处理.
- <2>, 钩子方法
- class UserGroupSerializer(serializers.Serializer):
- title = serializers.CharField()
- def validate_title(self, value):
- from rest_framework import exceptions
- if not value:
- raise exceptions.ValidationError('不可为空')
- return value
- class UserGroupView(APIView):
- def post(self,request,*args,**kwargs):
- print(request.data)
- ser = UserGroupSerializer(data=request.data)
- if ser.is_valid():
- print(ser.validated_data['title'])
- else:
- print(ser.errors)
- return HttpResponse('提交数据')
在定义钩子方法的时候, 钩子函数是以 validate_字段名的方式进行命名的. 只有遵循这样的格式, 在 Serializer 内部会对钩子函数的名字进行拆分并识别出来. 在 validate_title 内部封装了对数据的校验操作, value 则为具体的值
来源: https://www.cnblogs.com/welan/p/10151714.html