3-4 序列化-DRF模型序列化验证
目录:
- 初始化操作(模型、视图、urls)
- 序列化验证:单个字段验证(validate_验证字段名)、组合字段验证(validate) =>都需要返回值
- choices 单选display显示 => to_representation 决定每个字段的具体返回的格式,source,这可以自定义ChioceDisplayField 让支持可读可写
- 额外字段(解决出参时不显示密码字段) => 可以实现字段不支持序列化,但是支持反序列化
- Validators验证器
一、初始化操作(模型、视图、urls)
1.1、创建app03
D:\drf_test>python manage.py startapp app03
settings.py注册
INSTALLED_APPS = [
...
'app03.apps.App03Config',
'rest_framework'
]
1.2、 模型类
说明:创建用户表
from django.db import models
# Create your models here.
class User(models.Model):
genders = ((1, '男'), (2, "女"))
name = models.CharField(max_length=10, verbose_name='名字')
phone = models.CharField(max_length=11, verbose_name='手机号')
gender = models.IntegerField(choices=genders, verbose_name='性别')
pwd = models.CharField(verbose_name="密码",max_length=64)
def __str__(self):
return self.name
1.3、序列化
说明:在app03下创建serializers.py文件,完成序列化
from rest_framework import serializers
from .models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
1.4、 视图
说明:编辑 views.py,新增 user_list和user_detail的函数。
from .serializers import UserSerializer from rest_framework.renderers import JSONRenderer from rest_framework.parsers import JSONParser from django.http import HttpResponse from .models import User from django.views.decorators.csrf import csrf_exempt class JSONResponse(HttpResponse): """重新封装一下HttpResponse避免重复代码""" def __init__(self, data, **kwargs): content = JSONRenderer().render(data) kwargs['content_type'] = 'application/json' super(JSONResponse, self).__init__(content, **kwargs) @csrf_exempt def user_list(request): """ 文章标题 :return: """ if request.method == "GET": arts = User.objects.all() ser = UserSerializer(instance=arts, many=True, context={'request':request}) return JSONResponse(ser.data, status=200) elif request.method == "POST": data = JSONParser().parse(request) ser = UserSerializer(data=data, context={'request':request}) if ser.is_valid(): ser.save() return JSONResponse(ser.data, status=201) return JSONResponse(ser.errors, status=401) @csrf_exempt def user_detail(request,id): """ 文章详情 :param request: :param id: 文章id :return: """ try: art = User.objects.get(id=id) except User.DoesNotExist as e: return HttpResponse(status=404) if request.method == "GET": ser = UserSerializer(instance=art, context={'request':request}) return JSONResponse(ser.data, status=200) elif request.method == "PUT": data = JSONParser().parse(request) ser = UserSerializer(instance=art, data=data, context={'request':request}) if ser.is_valid(): ser.save() return JSONResponse(ser.data, status=201) return JSONResponse(ser.errors, status=400) elif request.method == "PATCH": data = JSONParser().parse(request) ser = UserSerializer(instance=art, data=data, partial=True, context={'request': request}) if ser.is_valid(): ser.save() return JSONResponse(ser.data, status=201) return JSONResponse(ser.errors, status=400) elif request.method == "DELETE": art.delete() return HttpResponse(status=204)
1.5、路由设置
#根级路由
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('app03.urls'))
]
#子集路由
from django.urls import path
from app03 import views
urlpatterns = [
path("user/", views.user_list, name="user-list"),
path("user/<int:id>/", views.user_detail, name="user-detail")
]
postman运行结果:
1.请求:POST:http://127.0.0.1:8000/api/user/
入参:
{
"phone": "13641929921",
"gender": 1,
"name": "gaogao",
"pwd": "1234"
}
出参:
{
"id": 1,
"name": "gaogao",
"phone": "13641929921",
"gender": 1,
"pwd": "1234"
}
2.请求:GET: http://127.0.0.1:8000/api/user/1/
出参:
{
"id": 1,
"name": "gaogao",
"phone": "13641929921",
"gender": 1,
"pwd": "1234"
}
二、序列化验证
一般前后端分离的时候,我们都会校验前端的参数时候合法。如果我们ModelSerializer话,因为它本身已经帮我们写好create方法,所以我们基本不需要再写验证。但是一些特殊的我们就需要重写或者自己写验证方法。
2.1、验证单个字段(validate_验证字段名)
说明:其实model层已经帮我们验证了一些字段,但是不能验证全,比如说 phone手机号,是11位,而且是以 13、15、等开头的规则,那咋办。这个时候我们就要用到 单个字段的验证(phone规则验证)。用法如下:
from rest_framework import serializers
from .models import User
import re
class UserSerializer(serializers.ModelSerializer):
#因为phone没有办法支持11位,需重新定义phone规则。
phone = serializers.CharField(max_length=11,min_length=11,required=True)
class Meta:
model = User
fields = "__all__"
#自定义单个字段验证规则:validate_验证字段名(phone),参数:传入的是当前字段(phone)
#当我们序列化的时候,phone字段就会走自定义序列化函数
def validate_phone(self, phone):
#检测手机是否合法
if not re.match(r'1[3456789]\d{9}',phone):
raise serializers.ValidationError("手机号码不合法") #如果不匹配我抛出一个异常
#检测手机号是否存在
if User.objects.filter(phone=phone).all():
raise serializers.ValidationError("手机号已被注册")
return phone #一定要有返回值,意思就是把你验证后的字段返回给模型类
postman运行结果:
1.POST:http://127.0.0.1:8000/api/user/
入参:
{
"phone": "13641929921", #已注册过的手机号
"gender": 1,
"name": "gaogao",
"pwd": "1234"
}
出参:
{
"phone": [
"手机号已被注册"
]
}
2. POST:http://127.0.0.1:8000/api/user/
入参:
{
"phone": "01641929921", #手机号不合法
"gender": 1,
"name": "gaogao",
"pwd": "1234"
}
出参:
{
"phone": [
"手机号码不合法"
]
}
2.2、组合字段验证(validate)
说明:我们有的时候需要验证两个字段,比如说,在注册的时候,需要输入密码,和输入确认密码,但是我们只需要保存一次即可,那这种情况咋办呐。这个时候我们就需要用到validate这种组合验证方式。
from rest_framework import serializers
from .models import User
import re
class UserSerializer(serializers.ModelSerializer):
phone = serializers.CharField(max_length=11,min_length=11,required=True)
#序列化一个新字段pwd1,只是我们这个字段的值不保存数据库,后续验证完毕之后需要删除
pwd1 = serializers.CharField(max_length=64,write_only=True) #write_only表示必须要从前端传递过来
class Meta:
model = User
fields = "__all__"
def validate_phone(self, phone): #单个验证
....
def validate(self, attrs): #组合验证
#print(attrs) #attrs是:OrderedDict([('phone', '13641929925'), ('pwd1', '1234'), ('name', 'gaogao'), ('gender', 1), ('pwd', '1234')])
if attrs.get('pwd1') != attrs.get('pwd'):
raise serializers.ValidationError("两次密码不一样")
attrs.pop('pwd1') #验证完毕需要删除pwd1,因为pwd1在模型类User中没有这个字段
return attrs #一定要有返回值
postman运行:
POST:http://127.0.0.1:8000/api/user/
入参:
{
"phone": "13641929926",
"gender": 1,
"name": "gaogao",
"pwd": "1234", #两次密码不一样
"pwd1": "12345"
}
出参:
{
"non_field_errors": [
"两次密码不一样"
]
}
2.3、登录密码加密
说明:我们在注册密码的时候,需要进行用户密码的加密,这个时候就需要重写 create 方法。
from rest_framework import serializers
from .models import User
import re
class UserSerializer(serializers.ModelSerializer):
....
class Meta:
model = User
fields = "__all__"
def create(self, validated_data):
#重写create方法,对密码加密完之后,再插入到数据库中
return User.objects.create()
def validate_phone(self, phone): #单独验证函数
.....
def validate(self, attrs): #组合验证
...
三、choices 单选 显示
说明:我们在model类中,有的时候会存choice这样的选项。如下:
from django.db import models
# Create your models here.
class User(models.Model):
genders = ((1, '男'), (2, "女"))
...
gender = models.IntegerField(choices=genders, verbose_name='性别')
...
postman请求的结果如下:
GET: http://127.0.0.1:8000/api/user/1/
出参:
{
"id": 1,
"phone": "13641929921",
"name": "gaogao",
"gender": 1, #只显示1或者2,不显示 男 或者 女
"pwd": "1234"
}
很明显 只显示 1或者 2这样的数据,不是我们想要的,那我们咋办呐?三种办法:
3.1、序列化中重新定义gender字段
说明:根据source来源来定义字段,这种方式只能支持序列化,但是不支持反序列化,所以只能获取数据,但是不能创建数据。
from rest_framework import serializers
from .models import User
import re
class UserSerializer(serializers.ModelSerializer):
gender = serializers.CharField(source='get_gender_display') #根据source数据来源定义字段
class Meta:
model = User
fields = "__all__"
postman运行结果:
GET:http://127.0.0.1:8000/api/user/1/
出参:
{
"id": 1,
"phone": "13641929921",
"gender": "男", #显示男
"name": "gaogao",
"pwd": "1234"
}
3.2、重写 to_representation 方法
说明:前面我们也讲了,所有字段的显示,其实就是在 to_representation方法里面,那我们在 to_representation 方法重新定义 gender字段不就可以了。这样既支持序列化,也可以支持反序列化。
from rest_framework import serializers
from .models import User
import re
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
def to_representation(self, instance): #重写to_representation方法
representation = super(UserSerializer, self).to_representation(instance) #继承UserSerializer父类
representation['gender'] = instance.get_gender_display() #重新定义gender字段
return representation
postman运行结果:
1.GET:http://127.0.0.1:8000/api/user/1/
出参:
{
"id": 1,
"phone": "13641929921",
"name": "gaogao",
"gender": "男", #序列化没有问题
"pwd": "1234"
}
2.POST:http://127.0.0.1:8000/api/user/
入参:
{
"phone": "13641929927",
"gender": 2,
"name": "gaogao",
"pwd": "1234",
"pwd1": "1234"
}
出参:
{
"id": 3,
"phone": "13641929927",
"name": "gaogao",
"gender": "女", #反序列化也是可以的
"pwd": "1234"
}
3.3、重写genders 字段
说明:这种效果跟上面的效果差不多,但是代码量太大,我觉得没啥用。但是还是要学一下。
from rest_framework import serializers
from .models import User
from collections import OrderedDict
#重新定义字段,实现可读可写
class ChoiceDisplayField(serializers.Field):
"""Custom ChoiceField serializer field."""
def __init__(self, choices, **kwargs):
"""init."""
self._choices = OrderedDict(choices)
super(ChoiceDisplayField, self).__init__(**kwargs)
# 返回可读性良好的字符串而不是 1,-1 这样的数字
def to_representation(self, obj):
"""Used while retrieving value for the field."""
return self._choices[obj]
def to_internal_value(self, data):
"""Used while storing value for the field."""
for i in self._choices:
# 这样无论用户POST上来但是CHOICES的 Key 还是Value 都能被接受
if i == data or self._choices[i] == data:
return i
raise serializers.ValidationError("Acceptable values are {0}.".format(list(self._choices.values())))
class UserSerializer(serializers.ModelSerializer):
GENDERS = ((1,'男'),(2,'女'))
gender = ChoiceDisplayField(choices=GENDERS) #调用ChoiceDisplayField
class Meta:
model = User
fields = "__all__"
运行结果跟重写 重写 to_representation 方法 效果是一样的
四、额外字段(解决出参时不显示密码字段)
说明:我们在入参的时候可能想输入一些参数,但是在出参的时候,不想显示一些参数,比如说,密码,不可能我注册的时候,响应给我密码多少。那这个怎么解决呐?我们就用额外字段解决。
from rest_framework import serializers
from .models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
extra_kwargs = { #额外字段,就是说你必须传参,但是不需要出参。额外字段:改变我们字段的修饰
"pwd": {"write_only": True} #write_only: True 表示只允许入参,不允许出参
}
postman运行结果:
1.GET: http://127.0.0.1:8000/api/user/1/
出参:
{
"id": 1,
"phone": "13641929921",
"name": "gaogao",
"gender": 1
}
2.POST:http://127.0.0.1:8000/api/user/
入参:
{
"phone": "13641929929",
"gender": 2,
"name": "gaogao",
"pwd": "1234",
"pwd1": "1234"
}
出参:
{
"id": 5,
"phone": "13641929929",
"name": "gaogao",
"gender": 2
}
五、Validators验证器
5.1、UniqueValidator:对象是唯一
说明:其实这个我们在model模型类中用unique=True 也可以验证。
username = serializers.CharField(required=True, allow_blank=False, label="用户名", max_length=16, min_length=6,validators=[UniqueValidator(queryset=User.objects.all(),
message="用户已经存在")],error_messages={
"blank": "用户名不允许为空",
"required": "请输入用户名",
"max_length": "用户名长度最长为16位",
"min_length": "用户名长度至少为6位"
})
5.2、UniqueTogetherValidator:联合唯一
说明:两个组合联合唯一
class UserFav(models.Model):
user = models.ForeignKey(User,verbose_name="用户",on_delete=False)
goods = models.ForeignKey(Goods,verbose_name="商品",on_delete=False)
add_time = models.DateTimeField(default=datetime.now,verbose_name="用户收藏")
class Meta:
verbose_name = "用户收藏"
verbose_name_plural=verbose_name
unique_together = (('user','goods'),)
class UserFavSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = UserFav
fields = ('id','user', 'goods')
validators = [UniqueTogetherValidator(queryset=UserFav.objects.all(),fields=('user','goods'),message='您已收藏')]

浙公网安备 33010602011771号