一个小作业
自己写代码,让原生的request能实现request.data传值
思路:写装饰器,装饰request.data,做到2+1都是request.data
def MyRequest(func):
def inner(request, *args, **kwargs):
try:
print(request.body)
request.data = json.loads(request.body)
except Exception as e:
request.data = request.POST
res = func(request, *args, **kwargs)
return res
return inner
@MyRequest # 等于MyRequest(TestView)(request) 就是inner(request)
def TestView(request):
print(request.POST)
print(request.data)
return HttpResponse('ok')
一、ModelSerializer的使用
前话:序列化组件在写字段时,可以做约束,但是不是约束序列化,而是约束反序列化,
类似form组件的字段约束
ModelSerializer可以不用写字段,不用重写create、update方法
class Authorserializers(serializers.ModelSerializer):
class Meta:
model = Author
fields = '__all__' #这是把所有字段全都序列化了
#可以选择只序列化某几个字段
fields = [要序列化、反序列化的字段]
上面Meta这一堆,就相当于Serializer的正常的写字段,映射出来的而已
此外,还可以在这里面(是外面大类不是Meta里面!)重写字段,后执行的是真正生效的(这个简单一些好理解)
重点————也可以不重写字段,但是可以往里面加属性
model = Author
fields = '__all__' #这是把所有字段全都序列化了
#可以选择只序列化某几个字段
fields = [要序列化、反序列化的字段]
extra_kwargs = {'字段':{要加的属性字典}}
二、序列化组件中常用字段和字段参数
1.常用字段类(熟小部分,了解大部分)
BooleanField BooleanField()
NullBooleanField NullBooleanField()
CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugField SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
URLField URLField(max_length=200, min_length=None, allow_blank=False)
UUIDField UUIDField(format=’hex_verbose’) format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
IPAddressField IPAddressField(protocol=’both’, unpack_ipv4=False, **options)
IntegerField IntegerField(max_value=None, min_value=None)
FloatField FloatField(max_value=None, min_value=None)
DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationField DurationField()
ChoiceField ChoiceField(choices) choices与Django的用法相同
MultipleChoiceField MultipleChoiceField(choices)
FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListField ListField(child=, min_length=None, max_length=None)
DictField DictField(child=)
————————————下面两个重要
#ListField:{name:'kevin',age:19,hobby:['唱','跳']}
#DictField:{name:'kevin',age:19,city:{'name':'上海','name':'北京'}}
重点记住
CharField
BooleanField
IntergerField
DecimalField
2.常用字段参数
选项参数:
# CharField及其子类的(EmailField) ---》反序列化的校验,字段自己的规则
max_length 最大长度
min_lenght 最小长度
allow_blank 是否允许为空
trim_whitespace 是否截断空白字符
# IntegerField
max_value 最小值
min_value 最大值
# 所有字段类都有的
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
validators 该字段使用的验证器
error_messages 包含错误编号与错误信息的字典
----不常用-----
label 用于HTML展示API页面时,显示的字段名称
help_text 用于HTML展示API页面时,显示的字段帮助提示信息
# 重点:
read_only 表明该字段仅用于序列化输出,默认False,常用
write_only 表明该字段仅用于反序列化输入,默认False,常用
## 反序列化校验执行流程,其实是有四步的,第二个一般可以忽略
-1 先执行字段自己的校验规则----》最大长度,最小长度,是否为空,是否必填,最小数字。。。。
-2 validators=[方法,] ----》单独给这个字段加校验规则
好处是可以给很多个字段都用这个方法
name=serializers.CharField(validators=[方法,])
-3 局部钩子校验规则
-4 全局钩子校验规则
三、字段参数source的使用
几句话,在序列化字段时,后端数据库中表的字段可以变为另外的名字传给前端拿,新字段属性中 + source='老字段名'
另外,source后面也可以跟方法,用于重命名,方法可以封在模型层的表中
#模型层
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE) # 留住,还有很多
authors = models.ManyToManyField(to='Author')
def new_name(self):
return f'此书叫做{self.name}'
#序列化类
class BookSerializers(serializers.Serializer):
name_book = serializers.CharField(source='new_name')
price = serializers.CharField()
还可以做跨表查询(要用外键对象点字段),一对多直接点,多对多搞不了
注意:因为在序列化类过程中,视图函数中序列化了对象——绑定着一张表,如果基于这个表找东西可以省略表名,比如book.publish.name 可以省略book,这就是source='啥啥啥'是在序列化模型表中找属性的根本原因
接下来要学习重要的东西,准备全新的表,注意,因为我们已经创建过Book表,复制下面的表代码进去做数据库迁移会出问题,建议直接把项目停了,然后去删除sqlite3,然后迁移就没问题了
如果还是各种报错,重新开一个项目吧2333
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
publish_date = models.DateTimeField()
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
authors = models.ManyToManyField(to='Author')
def __str__(self):
return self.name
def new_name(self):
return f'这本书名叫{self.name}'
class Author(models.Model):
name = models.CharField(max_length=8)
age = models.IntegerField()
author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
class AuthorDetail(models.Model):
telephone = models.BigIntegerField()
addr = models.CharField(max_length=64)
class Publish(models.Model):
name = models.CharField(max_length=16)
addr = models.CharField(max_length=8)
email = models.EmailField(null=True)
由于表关联太多,录入数据不好录入,建议去admin后台录入,具体流程就不说了,创建一个admin超级用户就可以去admin后台
四、序列化高级用法之定制字段的两种方式(重要)
首先,我们想拿一下一对多、多对多的字段序列化给前端看看,记得哦,因为是多对多外键,source='authors.all'才是能拿到所有作者对象的QS
1.关联字段的显示形式SerializerMethodField
一对多的显示字典
序列化类中新增条这个
publish=serializers.SerializerMethodField()
然后!一定要跟上一个方法一定要是get_字段名,return啥,前端就显示啥,obj就是后端查到的对象
def get_publish(self,obj):
return {'name':obj.publish.name,'city':obj.publish.city}
多对多的显示列表套字典
authors_detail = serializers.SerializerMethodField()
def get_authors_detail(self, obj):
author_list = []
for i in obj.authors.all():
author_list.append({'name': i.name, 'age': i.age, 'detail':
{'addr': i.author_detail.addr, 'phone': i.author_detail.telephone}})
return author_list
2.在表模型中写方法——很有趣,推荐
因为序列化类就是在模型表中点属性,所以我们就在模型表中封方法,当这个方法被调用就返回前面写过的那种
一对多的:
@proprey
def publish_detail(self):
return {'name':self.publish.name,'city':self.publish.city}
然后序列化类中:
publish_detail=serializers.DictField()
多对多的:
@proprey
def author_list(self):
list = []
for i in self.authors.all():
list.append({'name':i.author.name,'phone':i.author.phone})
return list
# 也可以用列表生成式直接传出去
# return [{'name': i.name, 'age': i.age, 'detail':
# {'addr': i.author_detail.addr, 'phone': i.author_detail.telephone}} for i in self.authors.all()]
然后序列化类中:
author_list=serializers.ListField()
五、多表关联的反序列化
用到了read_only=True write_only=True属性
让我们的序列化类,能根据读写请求方式的不同选择不同的字段
1.反序列化之新增一个表数据
视图层的其实基本一致
def post(self, request):
ser = BookSerializers(data=request.data)
if ser.is_valid():
ser.save()
return Response ({'code': 1000, 'msg': '新增成功'})
else:
return Response ({'code': 1001, 'msg':ser.errors })
序列化类层的
然后重写create方法
def create(self, validated_data):
print(validated_data) #这里打一下看看
#注意,接下来干的是图方便,所以必须把
#publish= serializers.CharField(write_only=True)改为
#publish_id = serializers.CharField(write_only=True)
#因为要让前端传过来的publish_id和book表的字段对应上,不然就报错
authors = validated_data.pop('authors')
#这里是先移除掉authros,剩下的kv键值对一次丢进去创建
book = Book.objects.create(**validated_data)
book.authors.add(*authors) #这个方法很久没用了,是用于有关联关系的第三张表的数据添加,用于多对多
return book
#前端传数据时一定要注意K要对的上啊!!!!
2.反序列化之新增一个表数据
视图层的其实基本一致
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first()
#这里可以加点判断,我就不写了
ser = BookSerializers(data=request.data)
if ser.is_valid():
ser.save()
return Response({'code': 1000, 'msg': '新增成功'})
else:
return Response({'code': 1001, 'msg': ser.errors})
序列化类层:
def update(self, instance, validated_data):
#先弹出不能for循环一次写入的K
authors = validated_data.pop('authors')
print(validated_data)
#这里用了反射,就非常简单了
for i in validated_data:
setattr(instance, i, validated_data[i])
# instance.name = validated_data.get('name')
# instance.price = validated_data.get('price')
# instance.publish_id = validated_data.get('publish_id')
instance.authors.clear() # 先清空书和作者的关联关系再添加
instance.authors.add(*authors)
instance.save()
return instance
六、今日犯错
-
1.一对多ORM跨表查询,多的那张表根据外键字段自动产生外键字段_id的一个字段
跨表查询是那自己写模型表时写的外键字段,而不是外键字段_id 点半天报错!!!
-
2.写序列化跨表查询一对多字段时,字段和get_字段函数名字不统一,一直在报错
-
ORM 之多对多关系表 的 add()那四个方法忘记怎么用了
-
写新增接口时,因为图方便,直接pop一个数据后全**validated_data创建新的表数据,然后因为前端自己传的publish作为K想当 外键publish_id 来创建,当然创建不了啊!!!book表中都没这个publish 字段,因为被外键搞成了publish_id了!