from django.urls import path, re_path
from . import views
urlpatterns = [
re_path(r'^roles/$', views.RolesView.as_view()),
re_path(r'^userinfo/$', views.UserInfoView.as_view()),
re_path(r'^group/(?P\d+)$' , views.GroupView.as_view(), name='gp'), #注意这里的pk,默认系统里面这么写的,可以在view中改掉.,源码在: class HyperlinkedRelatedField(RelatedField): lookup_field = 'pk' 这个pk就是人家默认规定的就是这个pk,但是pk不对,因为pk是id,id是变化的,二分组是固定的.
re_path(r'^usergroup/$', views.UserGroupView.as_view())
]
from django.db import models
class UserInfo(models.Model):
user_type_choices = (
(1, '普通用户'),
(2, 'VIP客户'),
(3, 'SVIP')
)
user_type = models.IntegerField(choices=user_type_choices) #注意这里是choice的
username = models.CharField(max_length=32, unique=True)
password = models.CharField(max_length=64)
group = models.ForeignKey(to='UserGroup', on_delete=models.CASCADE) # 注意这个多对一,外键. 假设一个组对应多个人,一个人只有多个组
roles = models.ManyToManyField('Role') #注意这个多对多
class UserGroup(models.Model):
title = models.CharField(max_length=32)
class Role(models.Model):
title = models.CharField(max_length=32)
class UserToken(models.Model):
user = models.OneToOneField(to='UserInfo', null=True, on_delete=models.SET_NULL)
token = models.CharField(max_length=64)
from django.shortcuts import render, HttpResponse
from django.http import response, JsonResponse
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response
import json
from .models import Role, UserInfo, UserGroup
#######################################################################################################################
###########以下示例讲解的是用serializer来解析从数据库中取出来的数据的方法#########################################################
#例子1
# 方法1: 用values然后用list类型把获取出来的Queryset类型的某一列,或者几列,转换成list类型,然后用json,就可以输出.
#class RolesView(APIView):
# def get(self, request, *args, **kwargs):
# roles = Role.objects.all().values('pk', 'title')
# roles = list(roles)
# ret = json.dumps(roles, ensure_ascii=True)
# return Response(roles)
#例子2
# 方法2:用rest framework里面的serializer序列化处理.
# 首先定义一个能勾完成序列话的类,然后用这个类 去处理我们从数据库中拿出的数据,因为拿出来的数据是Queryset类型,所以需要序列化之后,输出
from rest_framework import serializers
#定义一个用来实例化的类
class RolesSerializer(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField() #注意这个title 要和数据库中的字段名称保持一致,当然也可以不用保持一致,需要指定source参数就可以,如下一行代码所示:
# xxx = serializers.CharField(source='title')
#定义View类
class RolesView(APIView):
def get(self, request, *args, **kwargs):
roles = Role.objects.all()
ser = RolesSerializer(instance=roles, many=True) #注意这就用到了上面写的那个序列化的类.传递两个参数,必写的.instance是我们上面从数据库中获得实例对象, many的值:True表示多条数据,False表示单个实力
#上一行代码就获得了序列化之后的实例对象, ser.data就是序列化之后的结果.
print(ser.data) #打印看下有没有值,有值,且类型是:[OrderedDict([('title', '医生')]), OrderedDict([('title', '学生')]), OrderedDict([('title', '元昊')])]
return Response(ser.data) #这个是采用rest framework组件里面的Response,本身可以解析json格式数据,如果用系统的httpResponse(),就必须在上一步用json.dumps()来先格式化一下,在输出.如下:
#采用httpResponse()来做
# ret = json.dumps(ser.data, ensure_ascii=False) # ensure_ascii=False 这个保证输出的中文,不是十六进制的字符
# return HttpResponse(ret)
#例子3
# 方法3: 用serializer序列化操作复杂的数据表,这里models.py里面有: choice选择, 外键ForeignKey, 多对多ManyToMany,三种类型
# 针对每种类型,注意下面的操作步骤.
#第1步.创建序列化的类
class UserInfoSerializer(serializers.Serializer):
#想要序列化什么字段,就写上什么字段
username = serializers.CharField()
password = serializers.CharField()
#第一个重点: 解析choice选择.
# user_type = serializers.IntegerField(source='user_type') #这个只会输出user_type就是数据库里面的纯数字.并不能显示出来choice对应的选项,如下解决choice.
# user_type = serializers.IntegerField(source='get_user_type_display') #直接一步到位的说,使用source 关键字,然后用get_xxx_display的方式指定,中间的xxx就是带有choice的field字段,当时注意不能用和原来的一样的字段,因为这里显示中文,所以需要换成Charfield(),不然报错
user_type = serializers.CharField(source='get_user_type_display') # 正确答案: 使用get_xxx_display的方式,同时用Charfield()类型,就可以搞出来对应的中文
# 第二个重点: 解析外键ForeignKey() 如何使用.
# group = serializers.CharField() #这个常规操作1,只会输出这个对象对应的外键的对象,注意是对象,是UserGroup object类型,并不是可以直观看到的内容.
# group = serializers.CharField(source='group') #这个常规操作2,是会报错的,因为最开始的group和source里面指定的group是重复的,系统会报错,如果想要用source=group指定,前面的group就要改名字,如下:
# gp = serializers.CharField(source='group') #这个就不会报错,但是在返回的变量名就是gp 不在是group了. 但是下面的一句就不会报错,因为source指向的是另外一个表的内容了..
group = serializers.CharField(source='group.title') #直接一步到位的说: 因为是外键,所以需要应用指向外键的那个表里面的字段,所以这里就用'点'操作了,直接用这个表里面的外键字段去'点'指向的那个外键表里面的字段,UserGroup表里面id, pk, title都可以,都行
# 第三个重点: 解析多对多ManyToManyField() 如何使用
#这里操作角色,用户和角色是多对多的,这里看操作
# roles = serializers.CharField(source='roles.title') #现根据之前的外键的经验,做出如此的写法,,但是是错误的.
# roles = serializers.CharField(source='roles.all') #直接一步到位的说: 用source指定roles.all可以获取到一个user 用户对应的所有的角色,注意是多对多的关系,仔细思考,一个角色对应另外一个表里面的所有的关联数据. 但是这个获取到的只是, , ]>,不是具体的数据,这种用source指定的做法,歇菜了.
# 上面两行都不行,不能用source来做,最多只能取出来对象,但是不能取出来对象里面具体的值,可以获取到值的做法,参考如下:
#正确的方法1: 这个方法也可以发扬光大.对于choice 和ForeignKey来说都可以用这种先获取对应表里面的所有数据,然后遍历.
roles = serializers.SerializerMethodField() #注意这里的Field已经变化了,还有roles
def get_roles(self, r): #注意这里的函数名字: get_roles() 和上面的field要对应,加上开头的get_
role_obj_list = r.roles.all() #注意这里程序取出来的是QuerySet对象,针对数据库里面的每一个user用户,这里取出来的就是那个用户所对应的多对多表里面的全部数据,这个全部数据是多个,所以用到遍历,然后添加到字典或者其它数据结构中..
# print(role_obj_list) #测试上面一句的输出内容.输出2个QuerySet对象,因为数据库中有2个user用户,下面的get视图中,也是取出全部的用户,所有是2个QuerySet对象.
ret = [] #先定义,给下面使用的.
for item in role_obj_list: #这个role_obj_list是QuerySet类型.针对每一个用户 在多对多表中的全部数据,遍历拿到里面的每一条数据,注意这个每一条数据的意思,就跟excel表格中的一行是一样的,所以可以在下面这一行代码中用'点'操作.
ret.append({'id':item.pk, 'title':item.title}) #这个点操作后面的名称,也不是瞎写的,是根据就数据库表中的字段来的.
return ret
# 例子4:用serializers.ModelSerializer()这个其实就是把字段进行省略写法,对于处理choice ForeignKey, ManyToMany来说还是一样要像serializer.Serializer()一样处理,个人感觉不太实用.
class UserInfoModelSerializer(serializers.ModelSerializer):
# 也支持混合自定义用
xxx = serializers.CharField(source='get_user_type_display') #自定义字段,然后用source来指定对应的字段
rls = serializers.SerializerMethodField() #自定义字段
class Meta:
model = UserInfo #指定要连接的表
# fields = "__all__" # 指定全部的字段都需要处理
fields = ['username', 'password', 'xxx', 'rls'] # 进行部分的字段处理
# exclude = ['username', 'password'] # 这个是排除的字段,除了这个的字段,其它的都要进行序列化.
def get_rls(self, r): #注意这里的函数名字: get_roles() 和上面的自定义的字段rls要对应,加上开头的get_
role_obj_list = r.roles.all() #注意这里程序取出来的是QuerySet对象,针对数据库里面的每一个user用户,这里取出来的就是那个用户所对应的多对多表里面的全部数据,这个全部数据是多个,所以用到遍历,然后添加到字典或者其它数据结构中..
# print(role_obj_list) #测试上面一句的输出内容.输出2个QuerySet对象,因为数据库中有2个user用户,下面的get视图中,也是取出全部的用户,所有是2个QuerySet对象.
ret = [] #先定义,给下面使用的.
for item in role_obj_list: #这个role_obj_list是QuerySet类型.针对每一个用户 在多对多表中的全部数据,遍历拿到里面的每一条数据,注意这个每一条数据的意思,就跟excel表格中的一行是一样的,所以可以在下面这一行代码中用'点'操作.
ret.append({'id':item.pk, 'title':item.title}) #这个点操作后面的名称,也不是瞎写的,是根据就数据库表中的字段来的.
return ret
#例子5:用depth=x来拿对应的嵌套数据nest ,用两种类都来验证一下
#1, 验证类serializer.Serializer()
class UserInfoDepthSerializer(serializers.Serializer):
user_type = serializers.CharField()
username = serializers.CharField()
password = serializers.CharField()
group = serializers.CharField()
roles = serializers.CharField()
class Meta:
depth=2 #没有起任何作用,输出的仍然是QuerySet类型,不是具体的数据库表中的值.
#2, 验证类serializer.ModelSerializer()
class UserInfoDepthModelSerializer(serializers.ModelSerializer):
class Meta:
model = UserInfo
fields = '__all__'
depth = 1 #只有在ModelSerializer 起作用, 但是对于那个choice 还是不能用,还是会输出对应的id,而不是类型.
#3, 验证类serializer.ModelSerializer() depth 对choice的改进, 完美写法!
class UserInfoDepthModelSerializer_1(serializers.ModelSerializer):
user_type = serializers.CharField(source='get_user_type_display') #在这里用source=get_xxx_display的方法,注意user_type我们这里是相当于覆盖掉UserInfo表里面的的那个user_info字段了,我们也可以自定义写成xxx但是这个xxx就要在fields里面体现了.
class Meta:
model = UserInfo
fields = "__all__"
depth = 1
#例子6: 反向生成URL,
# 就是在序列化的时候,在提供数据库信息的时候,同时针对某些字段,我们不仅要获取这个字段的值,还想要让系统反向帮我们生成针对这个字段的URL,比如:
# 在系统给我们提供用户信息的时候,我们不仅要知道用户属于那个组,还要求提供那个组的URL,这样我们点击那个组,就可以跳转到其它的页面, 思考:这里其实就是针对
# 那个需要反向的字段进行reverse...所以第一步我们需要给这个字段创建url,同时提供这个path的view name,才可以反转,所以我们在urls.py中添加一条针对
# group的path
# 这里用到group 所以还需要对 UserGroup这个表进行序列化,同时写上view,
#先处理 组 的视图和序列化类.groupserializer 和groupview
class GroupSerializer(serializers.ModelSerializer):
class Meta:
model = UserGroup
fields = '__all__'
class GroupView(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk') #先获取url中的pk,注意这个里面都是在kwargs里面了,没有放到args
#根据传入的pk到数据库中查找对象的pk,然后返回给get请求
obj = UserGroup.objects.filter(pk=pk).first()
ser = GroupSerializer(instance=obj, many=False) #因为这里就从数据库中拿到了一个数据,根据PK拿的.
ret = json.dumps(ser.data, ensure_ascii=False) #ensure_ascii=False 就可以显示中文.
return HttpResponse(ret) #这里HttpResponse()用的是系统原生的.
# 方案1: 下面处理用户的序列化,不仅序列化用户的数据,还顺便给我们提供针对那个字段的反向URL,这里需要注意的是因为是针对那个字段的URL,所以需要把那个字段的url, view, serializer都写好,要不然都没有view和url,无法反向生成url.
# 但是这种做法是错误的,得到的url不对! HyperlinkedIdentityField 源代码里面lookup_field = 'pk' 也就是说默认pk得到的是表的id来的,很显然不多我们想要的是用户所属组的id,而不是组的id
class UserInfoDepthModelSerializerUrl(serializers.ModelSerializer):
group = serializers.HyperlinkedIdentityField(view_name='gp')
class Meta:
model = UserInfo
fields = "__all__"
depth = 1
# 方案2: 下面的操作不仅要求得到用户的分组名称,还要求获得分组的反向URL.就可以结合前面说的方案,一个字段,得到两种结果.得到的东西更多,更完美.
# 但是这种做法是错误的,得到的url不对! HyperlinkedIdentityField 源代码里面lookup_field = 'pk' 也就是说默认pk得到的是表的id来的,很显然不多我们想要的是用户所属组的id,而不是组的id
class UserInfoDepthModelSerializerUrl_id(serializers.ModelSerializer):
group_id = serializers.CharField(source='group.title')
group = serializers.HyperlinkedIdentityField(view_name='gp')
class Meta:
model = UserInfo
# fields = "__all__"
fields = ['username', 'password', 'group_id', 'group'] # 这里可以写需要的,
depth = 1
#方案3: 下面是最终版,不仅可以修改url.py中的那个pk字符是其它的字符,也可以修正得到的url的路径是正确的,是真正的用户所属组的url.同时还得到了用户组的名称.
class UserInfoDepthModelSerializerUrl_Correct(serializers.ModelSerializer):
group_name = serializers.CharField(source='group.title') #获得group的名称,用点操作获取.
#下面一句代码,才可以解决两个问题,一个是url中写的那个pk的问题,这里有所体现,这里可以改成任何的,比如说xxx,但是同时在url和groupview中都要改成xxx.
#解决的第二个问题就是: 用户获得了对应的正确的group的的id,而不是group表中的pk,因为有很多用户对应的是同一个group,如果用group表里面的id很显然是错误的.
#第三个注意点: lookup_field='group_id' 这里不是点操作了,是用的下划线操作的.
group = serializers.HyperlinkedIdentityField(view_name='gp', lookup_field='group_id', lookup_url_kwarg='pk')
class Meta:
model = UserInfo
# fields = "__all__"
fields = ['username', 'password', 'group_name', 'group'] # 这里可以写需要的,
depth = 1
class UserInfoView(APIView):
def get(self, request, *args, **kwargs):
user = UserInfo.objects.all()
# ser = UserInfoSerializer(instance=user, many=True) #这个是针对serializer.Serializer()进行序列化
# ser = UserInfoModelSerializer(instance=user, many=True) # 这个是针对serializer.ModelSerializer()进行序列华
# ser = UserInfoDepthSerializer(instance=user, many=True) # 这个是测试depth 对serializer.Serializer()看能不能取到嵌套的值.事实证明,不行
# ser = UserInfoDepthModelSerializer(instance=user, many=True) # 事实证明depth 对serializer.ModelSerializer()才能用.但是choice还是不完美.
# ser = UserInfoDepthModelSerializer_1(instance=user, many=True) # 用depth的完美用法
# ser = UserInfoDepthModelSerializerUrl(instance=user, many=True, context={'request': request})
# ser = UserInfoDepthModelSerializerUrl_id(instance=user, many=True, context={'request': request})
ser = UserInfoDepthModelSerializerUrl_Correct(instance=user, many=True, context={'request': request})
#这里直接使用rest framework里面的Response,直接json格式输出,不用json.dumps()
return Response(ser.data)
# 用json.dumps(),用django自带的HttpResponse
# ret = json.dumps(ser.data, ensure_ascii=False)
# return HttpResponse(ret)
###########以上示例讲解的是用serializer来解析从数据库中取出来的数据的方法#########################################################
#######################################################################################################################
#######################################################################################################################
###########以下示例讲解的是用serializer来校验从前端post发送过来的数据#########################################################
#以下示例用usergroup数据表来操作
#倒序插入验证器的类,自定义一个验证器类:用作传入的title必须以 老男人 开头。
class XXValidator(object):
def __init__(self, base): #这里的base就是实例化传入的那个 老男人
self.base = base
def __call__(self, value): #注意这里的value就是你提交过来的值
if not value.startswith(self.base):
message = '传入的title %s 必须以老男人开头'%self.base
raise serializers.ValidationError(message)
def set_context(self, serializer_field):
#执行验证之前调用这个,serializer_field是当前字段对象,暂时用不到这个函数,但是必写
pass
#先创建序列化的类
class UserGroupSerializer(serializers.Serializer):
#需要验证什么字段,就从数据库中取什么字段进行验证.对什么字段验证,就写什么字段
#同时可以在CharField中写提示信息
# title = serializers.CharField() 这样也可以工作
# title = serializers.CharField(error_messages={'required':'标题不能为空'}) 只添加error_messages,只会在根本不传title才会触发。在里面写‘blank’就会吧英文翻译成这里写的中文。
title = serializers.CharField(error_messages={'required':'标题不能为空', 'blank': '标题不能为空blank'},validators = [XXValidator('老男人'),]) #这里也可以添加验证器类的实例,或者添加自定义的验证器类的实例,可以传参。
######也可以在这里用钩子函数做,什么是钩子函数,就是针对每一个字段写一个验证的函数,这样就不用写那个类了,比如说验证title就可以写:
#### 注意这个serializer 有两个验证规则,一个是上面的类XXValidator()一个就是下面的函数validate_title(),两个都起作用
def validate_title(self, value):#注意这里又两个注意点,第一个是函数必须以validate_开头,后面添加需要验证的字段。第二个是传入的参数value,这个就是你前端传入的值。
print('value is : %s'%value) #验证value是不是前端传入的值,确定value是前端传入的值
#同时根据源码可以看到这钩子函数是写到try中的,所以在这里如果验证不通过,可以触发源码中的try里面的异常,如下:
# from rest_framework import exceptions
# raise exceptions.ValidationError('看你不顺眼')
if value == "老男人【表情】" or None:
from rest_framework import exceptions #参考源码要触发下面的异常才引入的,可以放到最上面。
raise exceptions.ValidationError(detail='看你不顺眼', code='def函数验证改编') #注意这里改系统里面的code,但是一般只要改那个detail,显示原因就行了,不用改系统的code,这里可以参考源码
return value #如果触发异常就不会执行这个value了,但是这个函数是要有返回值的,人家吧需要验证的数据传给你函数,验证成功了就要返回这个数据,如果验证不成功,就触发异常,或者怎么样。
#定义视图
class UserGroupView(APIView):
def post(self, request, *args, **kwargs):
# print(request.data) #注意这里可以输出request.data,可以输出全部的从前台发送过来的值.所有的值
#方法1:这里可以用django的原生Form表单来做,
#方法2: 用上面创建的序列化类来校验
ser = UserGroupSerializer(data=request.data) #注意这里的data参数,因为这里的request是restframework的,所以可以调用request.data获取前端传递过来的data,然后放到serializer里面.
# print(ser.data) #写到这里是错误的,会提示:AssertionError: When a serializer is passed a `data` keyword argument you must call `.is_valid()` before attempting to access the serialized `.data` representation.
# You should either call `.is_valid()` first, or access `.initial_data` instead.
#下面对ser进行校验
if ser.is_valid():
# print(ser.validated_data)
print("ser.data == ",ser.data) # 注意这里!!! 经过is_valid()之后,ser.data有两种情况,第一:不合法的数据,第二:is_valid会把其它一起传入的数据清掉,只保留我们上面serializer里面想要验证的字段对应的数据,其余的都没有了..
print("ser.validated_data == ",ser.validated_data) # 注意这里,经过is_valid()之后,调用ser.validated_data就是OrderDict()类型的数据,里面包含了验证之后的合法数据.
print('ser.validated_data.title == ', ser.validated_data['title']) #注意这个是怎么获取orderDict()中的值,跟原生的dict获取键值的方式是一样,用健.只不过这个是改造过的orderDict()而已
# UserGroup.objects.create(title=ser.data.get("title")) #保存到数据库1
# UserGroup.objects.create(title=ser.validated_data['title']) #保存到数据库2
else:
print(ser.errors)
return Response('校验')