Serialzers 序列化组件
前言
- 当做前后端分离的项目时,JSON是一个轻量级的数据交互格式。所有我们给前端数据的时候都要转成json格式,那就需要对我们从数据库拿到数据进行序列化
Django的序列化方法
- 使用django,json转数据进行传输,(了解即可)
class BooksView(View):
def get(self, request):
# 获取数据库中的queryset数据
book_list = Book.objects.values("id", "title", "chapter", "pub_time", "publisher")
# 强转成列表类型
book_list = list(book_list)
# 如果我们需要取外键关联的字段信息 需要循环获取外键 再去数据库查然后拼接成我们想要的
ret = []
for book in book_list:
pub_dict = {}
pub_obj = Publish.objects.filter(pk=book["publisher"]).first()
pub_dict["id"] = pub_obj.pk
pub_dict["title"] = pub_obj.title
book["publisher"] = pub_dict
ret.append(book)
ret = json.dumps(book_list, ensure_ascii=False, cls=MyJson)
return HttpResponse(ret)
# json.JSONEncoder.default()
# 解决json不能序列化时间字段的问题
class MyJson(json.JSONEncoder):
def default(self, field):
if isinstance(field, datetime.datetime):
return field.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(field, datetime.date):
return field.strftime('%Y-%m-%d')
else:
return json.JSONEncoder.default(self, field)
- 由于太过于麻烦,所有我们可以使用DRF的序列化
字段和选项
- 注意serializer不是只能为数据库模型类定义,也可以为非数据库模型定义。serializer是独立于数据库之外的存在
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=) |
2.选项参数
参数名称 | 作用 |
---|---|
max_length | 最大长度 |
min_lenght | 最小长度 |
allow_blank | 是否允许为空 |
trim_whitespace | 是否截断空白字符 |
max_value | 最小值 |
min_value | 最大值 |
3.通用参数
参数名称 | 说明 |
---|---|
read_only | 表明该字段仅用于序列化输出,默认False |
write_only | 表明该字段仅用于反序列化输入,默认False |
required | 表明该字段在反序列化时必须输入,默认True |
default | 反序列化时使用的默认值 |
allow_null | 表明该字段是否允许传入None,默认False |
validators | 该字段使用的验证器 |
error_messages | 包含错误编号与错误信息的字典 |
label | 用于HTML展示API页面时,显示的字段名称 |
help_text | 用于HTML展示API页面时,显示的字段帮助提示信息 |
DRF序列化的方法
1. 自定义序列化
需要自定义所有字段,以及方法create,updata方法
声明一个序列化类
# 根据model.py中的字段对应写要显示的字段 class BookSerializer(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField(max_length=32) CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python")) chapter = serializers.ChoiceField(choices=CHOICES, source="get_chapter_display") pub_time = serializers.DateField()
将数据库中的数据传入序列化对象中
from rest_framework.views import APIView from rest_framework.response import Response class BookView(APIView): def get(self, request): # 获取数据库中的数据 book_list = Book.objects.all() # 实例化序列化对象, many= True 表示数据有多个,如果是一个则不需要 ret = BookSerializer(book_list, many=True) # 给前端的数据 return Response(ret.data)
2. 外键关系的序列化
from rest_framework import serializers
from .models import Book
class PublisherSerializer(serializers.Serializer):
# read_only=True 表示的是给前端时显示,反序列时,不需要此字段
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=32)
class UserSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(max_length=32)
age = serializers.IntegerField()
class BookSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=32)
CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python"))
chapter = serializers.ChoiceField(choices=CHOICES, source="get_chapter_display", read_only=True)
pub_time = serializers.DateField()
publisher = PublisherSerializer(read_only=True)
# many= True 表示有多条数据
user = UserSerializer(many=True, read_only=True)
3. 反序列化
当前端给我们发post请求的时候,前端给后端传数据的,我们需要对数据进行一些校验然后保存到数据库,或者对数据库中的数据进行更改,DRF的serializer也提供了方法
Serializer提供了is_valid()和save方法
url.py
urlpatterns = [ url(r'^list$', BooksView.as_view()), ]
声明一个序列化类
# serializers.py 文件 class BookSerializer(serializers.Serializer): # read_only = True 表示前端不需要传该字段的数据,其他的都需要传 id = serializers.IntegerField(read_only=True) title = serializers.CharField(max_length=32) CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python")) chapter = serializers.ChoiceField(choices=CHOICES, source="get_chapter_display", read_only=True) # write_only = True 表示前端需要传这个字段的数据, w_chapter = serializers.IntegerField(write_only=True) pub_time = serializers.DateField() publisher = PublisherSerializer(read_only=True) user = UserSerializer(many=True, read_only=True) users = serializers.ListField(write_only=True) publisher_id = serializers.IntegerField(write_only=True) # post请求会执行该方法 def create(self, validated_data): book = Book.objects.create(title=validated_data["title"], chapter=validated_data["w_chapter"], pub_time=validated_data["pub_time"], publisher_id=validated_data["publisher_id"]) book.user.add(*validated_data["users"]) # 需要返回对象 # 如果数据简单, # book = Book.objects.create(**validated_data) return book
- 注意: 因为存在后端给前端显示的数据和前端给后端存储的数据有区别,所有要灵活运用read_only= True 和write_only=True ,俩个属性,
反序列化
class BookView(APIView): def get(self, request): book_list = Book.objects.all() ret = BookSerializer(book_list, many=True) return Response(ret.data) # 会执行序列化中的create方法 def post(self, request): # book_obj = request.data print(request.data) # 将前端传来的数据传入序列化对象中 serializer = BookSerializer(data=request.data) # 判断数据 if serializer.is_valid(): # 执行后端的create方法 serializer.save() return Response(serializer.validated_data) # 否则将错误信息发回给前端 return Response(serializer.errors)
对数据进行编辑
url.py
urlpatterns = [ url(r'book/(?P<pk>\d+)/', BookView.as_view(), name='book'), ]
声明一个序列化类
class BookSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) title = serializers.CharField(max_length=32) CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python")) chapter = serializers.ChoiceField(choices=CHOICES, source="get_chapter_display", read_only=True) w_chapter = serializers.IntegerField(write_only=True) pub_time = serializers.DateField() publisher = PublisherSerializer(read_only=True) user = UserSerializer(many=True, read_only=True) users = serializers.ListField(write_only=True) publisher_id = serializers.IntegerField(write_only=True) def create(self, validated_data): book = Book.objects.create(title=validated_data["title"], chapter=validated_data["w_chapter"], pub_time=validated_data["pub_time"], publisher_id=validated_data["publisher_id"]) book.user.add(*validated_data["users"]) return book # put或者patch或执行该方法 def update(self, instance, validated_data): instance.title = validated_data.get("title", instance.title) instance.chapter = validated_data.get("w_chapter", instance.chapter) instance.pub_time = validated_data.get("pub_time", instance.pub_time) instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id) if validated_data.get("users"): instance.user.set(validated_data.get("users")) instance.save() return instance
view.py
class BookView(APIView): def patch(self, request): print(request.data) book_id = request.data["id"] book_info = request.data["book_info"] book_obj = Book.objects.filter(pk=book_id).first() # partial= True 对部分数据进行修改 serializer = BookSerializer(book_obj, data=book_info, partial=True) if serializer.is_valid(): serializer.save() return Response(serializer.data) else: return Response(serializer.errors) # 这俩种方法是一样的只写其中一种 def put(self, request, id): book_obj = Book.objects.filter(id=id).first() ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True) if ser_obj.is_valid(): ser_obj.save() return Response(ser_obj.data) return Response(ser_obj.errors)
3.1 view四个方法的运用
-- get 请求
def get(self, request):
queryset = Book.objects.all()
ser_obj = BookSerializer(queryset, many=True)
return Response(ser_obj.data)
-- post请求
def post(self, request):
data = request.data
ser_obj = BookSerializer(data=request.data)
if ser_obj.is_valid():
ser_obj.save()
# ser_obj.validated_data
return Response(ser_obj.data)
else:
return Response(ser_obj.errors)
-- put/patch请求
def put(self, request, id):
data = request.data
book_obj = Book.objects.filter(id=id).first()
ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True) # partial表示局部更新
if ser_obj.is_valid():
ser_obj.save()
# ser_obj.validated_data
return Response(ser_obj.data)
else:
return Response(ser_obj.errors)
4.验证
优先级从高到低: 自定义>单独验证>多个字段验证
如果我们需要对某些字段进行自定义验证,DRF也给我们提供了钩子方法
一 对单个数据进行验证, 格式必须是validate_需要验证的字段名(self, value):
class BookSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) title = serializers.CharField(max_length=32) # 省略了一些字段 跟上面代码里一样的 # 。。。。。 # 格式必须是validate_需要验证的字段名(self, value) def validate_title(self, value): if "python" not in value.lower(): raise serializers.ValidationError("标题必须含有Python") return value
对多个数据进行验证
class BookSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) title = serializers.CharField(max_length=32) CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python")) chapter = serializers.ChoiceField(choices=CHOICES, source="get_chapter_display", read_only=True) w_chapter = serializers.IntegerField(write_only=True) pub_time = serializers.DateField() date_added = serializers.DateField(write_only=True) # 新增了一个上架时间字段 # 省略一些字段。。都是在原基础代码上增加的 # 。。。。。。 # 对多个字段进行验证 要求上架日期不能早于出版日期 上架日期要大 def validate(self, attrs): if attrs["pub_time"] > attrs["date_added"]: raise serializers.ValidationError("上架日期不能早于出版日期") return attrs
定义一个验证器
def my_validate(value): if "敏感词汇" in value.lower: raise serializers.ValidationError("包含敏感词汇,请重新提交") return value class BookSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) title = serializers.CharField(max_length=32, validators=[my_validate]) # 。。。。。。
ModelSerializer
现在我们已经清楚了Serializer的用法,会发现我们所有的序列化跟我们的模型都紧密相关~
那么,DRF也给我们提供了跟模型紧密相关的序列化器
ModelSerializer-- 它会根据模型自动生成一组字段
-- 它简单的默认实现了.update()以及.create()方法
1.定义一个ModelSerializer序列化器
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = "__all__"
# fields = ["id", "title", "pub_time"]
# exclude = ["user"]
# 分别是所有字段 包含某些字段 排除某些字段
2.外键关联的序列化
自动序列化连表操作,可以使用depth来进行快捷的跨表操作,官方建议是0~10层,但是最好用到3或者4层就可以了
注意:当序列化类MATE中定义了depth时,这个序列化类中引用字段(外键)则自动变为只读
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = "__all__"
# fields = ["id", "title", "pub_time"]
# exclude = ["user"]
# 分别是所有字段 包含某些字段 排除某些字段
depth = 1
# depth 代表找嵌套关系的第几层
3.自定义字段对覆盖默认字段
我们可以声明一些字段来覆盖默认字段,来进行自定制~
比如我们的选择字段,默认显示的是选择的key,我们要给用户展示的是value。
class BookSerializer(serializers.ModelSerializer):
chapter = serializers.CharField(source="get_chapter_display", read_only=True)
class Meta:
model = Book
fields = "__all__"
# fields = ["id", "title", "pub_time"]
# exclude = ["user"]
# 分别是所有字段 包含某些字段 排除某些字段
depth = 1
4.meta中的其它关键字参数
class BookSerializer(serializers.ModelSerializer):
chapter = serializers.CharField(source="get_chapter_display", read_only=True)
class Meta:
model = Book
fields = "__all__"
# fields = ["id", "title", "pub_time"]
# exclude = ["user"]
# 分别是所有字段 包含某些字段 排除某些字段
depth = 1
# read_only_fields 表示只读
read_only_fields = ["id"]
extra_kwargs = {"title": {"validators": [my_validate,]}}
read_only_fields 表示id为只读,
extra_kwargs
选项在字段上指定任意附加关键字参数。与read_only_fields
的情况一样,这意味着你不需要在序列化类中显式声明该字段。该选项是一个字典,将字段名称映射到关键字参数字典。例如:
class CreateUserSerializer(serializers.ModelSerializer): class Meta: model = User fields = ('email', 'username', 'password') extra_kwargs = {'password': {'write_only': True}}
5 post以及patch/put请求
- 由于depth会让我们外键变成只读,所以我们再定义一个序列化的类,其实只要去掉depth就可以了~~
class BookSerializer(serializers.ModelSerializer):
chapter = serializers.CharField(source="get_chapter_display", read_only=True)
class Meta:
model = Book
fields = "__all__"
# fields = ["id", "title", "pub_time"]
# exclude = ["user"]
# 分别是所有字段 包含某些字段 排除某些字段
read_only_fields = ["id"]
extra_kwargs = {"title": {"validators": [my_validate,]}}
6 SerializerMethodField
- 针对于多对多表数据的显示,可以使用SerializerMethodField,来自定义显示
外键关联的对象有很多字段我们是用不到的~都传给前端会有数据冗余~就需要我们自己去定制序列化外键对象的哪些字段~~
class BookSerializer(serializers.ModelSerializer):
chapter = serializers.CharField(source="get_chapter_display", read_only=True)
user = serializers.SerializerMethodField()
publisher = serializers.SerializerMethodField()
def get_user(self, obj):
# obj是当前序列化的book对象
users_query_set = obj.user.all()
return [{"id": user_obj.pk, "name": user_obj.name} for user_obj in users_query_set]
def get_publisher(self, obj):
publisher_obj = obj.publisher
return {"id": publisher_obj.pk, "title": publisher_obj.title}
class Meta:
model = Book
fields = "__all__"
# fields = ["id", "title", "pub_time"]
# exclude = ["user"]
# 分别是所有字段 包含某些字段 排除某些字段
read_only_fields = ["id"]
extra_kwargs = {"title": {"validators": [my_validate,]}}
7. 用ModelSerializer改进上面Serializer的完整版
class BookSerializer(serializers.ModelSerializer):
dis_chapter = serializers.SerializerMethodField(read_only=True)
users = serializers.SerializerMethodField(read_only=True)
publishers = serializers.SerializerMethodField(read_only=True)
def get_users(self, obj):
# obj是当前序列化的book对象
users_query_set = obj.user.all()
return [{"id": user_obj.pk, "name": user_obj.name} for user_obj in users_query_set]
def get_publishers(self, obj):
publisher_obj = obj.publisher
return {"id": publisher_obj.pk, "title": publisher_obj.title}
def get_dis_chapter(self, obj):
return obj.get_chapter_display()
class Meta:
model = Book
# fields = "__all__"
# 字段是有序的
fields = ["id", "title","dis_chapter", "pub_time", "publishers", "users","chapter", "user", "publisher"]
# exclude = ["user"]
# 分别是所有字段 包含某些字段 排除某些字段
read_only_fields = ["id", "dis_chapter", "users", "publishers"]
extra_kwargs = {"title": {"validators": [my_validate,]}, "user": {"write_only": True}, "publisher": {"write_only": True},
"chapter": {"write_only": True}}
返回URL的连接
详情见网址:https://www.cnblogs.com/wupeiqi/articles/7805382.html中的序列化中的url