序列化器的目的:
(1)数据的转换,转换分两种:序列化、反序列化
(2)数据的校验,只有反序列化时才会用到
1. 序列化流程图:
2. 反序列化流程图
选项参数用来约束反序列化时前端传递过来的参数是否合法
3. 序列化器中CharField和IntegerField选项参数
4. 序列化器中字段通用选项参数
5. models.py代码
from django.db import models
class BookInfo(models.Model):
btitle = models.CharField(max_length=50, verbose_name="标题")
bcontent = models.TextField(verbose_name="内容")
bread = models.IntegerField(verbose_name="阅读量", default=0)
bprice = models.DecimalField(verbose_name="价格", max_digits=6, decimal_places=2, default=20)
bpub_date = models.DateTimeField(verbose_name="发布日期", auto_now_add=True)
is_delete = models.BooleanField(verbose_name="删除标记", default=False)
image = models.ImageField(upload_to="books/%Y/%m", verbose_name="图片", max_length=100, null=True, blank=True)
def __str__(self):
return self.btitle
class Meta:
verbose_name = "图书信息"
verbose_name_plural = verbose_name
6. serializers.py代码
from rest_framework import serializers
from .models import BookInfo
# 反序列化有三种自定义校验方法:1. 字段参数校验 2. 单个字段校验 3. 多个字段联合校验
def about_python(value):
if "python" not in value.lower():
raise serializers.ValidationError("图书不是关于python的")
return value
class BookInfoSerializer(serializers.Serializer):
"""书籍的序列化器"""
# 序列化器中的字段可以比模型类多,也可以比模型类少
id = serializers.IntegerField(label="ID", read_only=True)
btitle = serializers.CharField(label="标题", max_length=50, validators=[about_python])
bcontent = serializers.CharField(label="内容", required=True)
bread = serializers.IntegerField(label="阅读量", required=False)
bprice = serializers.DecimalField(label="价格", max_digits=6, decimal_places=2, required=False)
bpub_date = serializers.DateTimeField(label="发布日期", required=False)
image = serializers.ImageField(label="图片", required=False)
def validate_btitle(self, value):
"""对序列化器中单个字段追加额外的校验规则
value: 当前要校验的单个字段的值
"""
if "django" not in value.lower():
raise serializers.ValidationError("图书不是关于django的")
return value
def validate(self, attrs):
"""对多个字段进行联合校验,也可以只校验单个字段,所以实际开发中用这个的比较多
attrs: 里面是前端传递过来的所有数据, 它是一个字典
"""
if attrs["btitle"] != attrs["bcontent"]:
raise serializers.ValidationError("标题和内容不一致") # 此处练习是模仿两次密码不一致
# 此处字典中还可以增加键值对,一般情况下不变
# 此处return 返回的 attrs 就是视图中serializer对象的属性validated_data对应的值
return attrs
def create(self, validated_data):
"""当调用序列化器对象的save方法时,如果当初创建序列化器对象时没有给instance参数,调用create方法"""
book = BookInfo.objects.create(**validated_data)
# 获取创建序列化器对象时传递的属性值--request请求对象
request = self.context
print(request.user)
return book
def update(self, instance, validated_data):
"""当调用序列化器的save方法时,如果当初创建序列化器对象时传递了instance参数,调用update方法"""
# instance是创建序列化器对象是传递进来的 BookInfoSerializer(instance=book, data=data)
# validated_data 是经过反序列化,校验后的,大字典数据
instance.btitle = validated_data["btitle"]
instance.bcontent = validated_data["bcontent"]
# 切记,修改了实例属性后不要忘记保存进数据库
instance.save()
return instance
# heroinfo_set = serializers.PrimaryKeyRelatedField(label="英雄", many=True, read_only=True)
# heroinfo_set = serializers.StringRelatedField(label="英雄", many=True, read_only=True)
# 注意: 关联属性进行序列化时,只能在一方写,两方都写会报错
# heroinfo_set = HeroInfoSerializer(many=True)
7. views.py代码
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .serializers import BookInfoSerializer
from .models import BookInfo
class BookInfoList(APIView):
"""图书列表类视图"""
def get(self, request):
books = BookInfo.objects.all()
serializers = BookInfoSerializer(instance=books, many=True)
return Response(data=serializers.data, status=status.HTTP_200_OK)
def post(self, request):
# 创建序列化器对象时可以把request请求对象传递给序列化器对象的属性
serializer = BookInfoSerializer(data=request.data, context=request)
if serializer.is_valid():
# 此处调用save方法时,会自动调用序列化器中的create方法
serializer.save()
return Response(data=serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(data=serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class BookInfoDetail(APIView):
"""图书详情类视图"""
@staticmethod
def get_book(pk):
# 查询图书
try:
book = BookInfo.objects.get(id=pk)
except BookInfo.DoesNotExist:
book = ""
return book
else:
return book
def get(self, request, pk):
book = self.get_book(pk)
if not book:
return Response({"err": "所查图书不存在"}, status=status.HTTP_404_NOT_FOUND)
# 序列化
serializer = BookInfoSerializer(book)
# 响应
return Response(data=serializer.data)
def put(self, request, pk):
# 查看图书是否存在
book = self.get_book(pk)
if not book:
return Response({"err": "图书不存在,无法更新"}, status=status.HTTP_404_NOT_FOUND)
# 接收数据并校验数据
serializer = BookInfoSerializer(instance=book, data=request.data)
if serializer.is_valid():
# 此处调用save方法时会自动调用序列化器中的update方法
serializer.save()
# 响应
return Response(serializer.data)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
book = self.get_book(pk)
if not book:
return Response({"err": "所查图书不存在, 无法删除"}, status=status.HTTP_404_NOT_FOUND)
book.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
8. urls.py代码
from django.urls import path, re_path
from django.views.decorators.csrf import csrf_exempt
from . import views
urlpatterns = [
# 图书类视图对应的路由
path("books/", csrf_exempt(views.BookInfoList.as_view()), name="booklist"),
re_path("^books/(?P<pk>\d+)/$", csrf_exempt(views.BookInfoDetail.as_view()), name="bookdetail"),
]
9. ModelSerializer类使用详解
ModelSerializer与常规的Serializer相同,但提供了:
(1)基于模型类自动生成一系列字段
(2)包含默认的create和update方法的实现
当然如果自动生成的字段或create、update方法不能满足我们的需求,我们就可以修改属性或重新定义create、update方法
from rest_framework import serializers
from .models import BookInfo, HeroInfo
class BookInfoSerializer(serializers.ModelSerializer):
"""图书序列化器"""
class Meta:
model = BookInfo
fields = "__all__" # 映射模型类中存在的所有字段
class HeroInfoSerializer(serializers.ModelSerializer):
"""英雄序列化器"""
hbook = BookInfoSerializer(read_only=True)
class Meta:
model = HeroInfo # 指定序列化器字段从哪个模型去映射
# fields类属性需要把hbook单独加上,而exclude不需要, __all__也不需要
fields = ["id", "hname", "hcommon", "hgender", "hbook"] # 映射指定的字段
# exclude = ["hgender"] # 排除的字段,其它字段都要
# 如果想对字段中的某些选项做修改,可以加个类属性extra_kwargs
# extra_kwargs = {
# "hname": {"max_length": 10, "write_only": True}
# }
# 序列化单独定义属性,反序列化要在extra_kwargs中设置
# read_only_fields属性设置只能序列化字段
read_only_fields = ["hgender"]
def create(self, validated_data):
"""新增一个英雄"""
# 创建序列化区对象时,context参数传递过来的前端数据hbook: int类型
book_id = self.context
hero = HeroInfo()
hero.hname = validated_data["hname"]
hero.hcommon = validated_data["hcommon"]
hero.hbook_id = book_id
hero.save()
return hero
def validate(self, attrs):
"""联合校验"""
if "蜀" not in attrs["hcommon"]:
raise serializers.ValidationError({"err": "不包含蜀字"})
return attrs
扫描二维码关注公众号,回复:
13136272 查看本文章