0x00 前言
接上次代码, 完成了数据的获取和提交, 本次将会用到Serializer对数据进行定义, 从而大幅减少代码量, 提高代码复用率
0x01 编写get的Serializer
在同目录下建立一个serializers.py
, 效果如图
在里面填上如下代码
from rest_framework import serializers
from .models import Config
class ConfigListSerializer(serializers.ModelSerializer): # ModelSerializer和model相对应
class Meta:
model = Config # 设置对于model
fields = ('nickname', # 设置需要序列化的字段
'title',
'desc',
'config',
'user_like',
'last_download')
serializers.ModelSerializer
说明这个serializers关联一个Model, 在Meta
的model
里和model进行绑定, 然后再fields
内定义需要被序列化的字段
然后在view.py
内改写函数get
class ConfigView(APIView):
def get(self, request):
config_list = Config.objects.all() # 取所有数据
serializer = ConfigListSerializer(config_list, many=True) # 建立serializer实例
return Response(serializer.data, status=status.HTTP_200_OK)
成功了
但是我们也发现一件事, user_like
返回的是一个列表, 但是我们只想返回一个具体的数量, 解决办法就是覆盖掉user_like
字段, 如代码
from rest_framework import serializers
from .models import Config
class ConfigListSerializer(serializers.ModelSerializer): # ModelSerializer和model相对应
user_like = serializers.SerializerMethodField() # 点赞数
def get_user_like(self, obj):
return obj.user_like.all().count()
class Meta:
model = Config # 设置对于model
fields = ('nickname', # 设置需要序列化的字段
'title',
'desc',
'config',
'user_like',
'last_download')
再次打开发现已经被覆盖了
0x03 编写Post的Serializer
首先看看我们post需要哪些数据
Config
里面只有uid
user
nickname
title
desc
config
user_like
create_time
last_download
get请求里我们差不多要把这些数据全部返回回来, 但是在请求post的时候, 因为create_time
last_download
user_like
等是系统自动添加的, 不需要表单提供, 而且post需要请求Config里没有的username
sign
数据, 于是我们对Serializer进行改写
from rest_framework import serializers
from .models import Config, User
from .utils import md5
class ConfigListSerializer(serializers.ModelSerializer): # ModelSerializer和model相对应
# 需要魔改的字段, 配合get_user_like食用
user_like = serializers.SerializerMethodField(read_only=True)
# 新增的数据库里没有的, 自定义的字段
username = serializers.CharField(write_only=True)
sign = serializers.CharField(write_only=True)
"""
此函数用于配合SerializerMethodField, 具体就是xxx前面加上一个get_, 这样就可以对字段进行个性化设置, 如+1什么的
"""
def get_user_like(self, obj):
return obj.user_like.all().count()
"""
此函数用于验证表单是否有问题, 没问题返回attrs, 否则raise serializers.ValidationError()
"""
def validate(self, attrs):
if md5(attrs['username'] + "123456") != attrs["sign"]: # 验证签名
raise serializers.ValidationError("用户签名错误")
return attrs
"""
此函数用于重载save, 建议在此处写上保存入数据库操作, 后面有用
"""
def save(self, **kwargs):
user = User.objects.get_or_create(username=self.validated_data["username"], sign=self.validated_data["sign"])[0]
Config.objects.get_or_create( # 存储配置
user=user,
nickname=self.validated_data["nickname"],
title=self.validated_data["title"],
desc=self.validated_data["desc"],
config=self.validated_data["config"],
)
class Meta:
# 设置对于model
model = Config
# 用于表示所有的出现的字段
fields = ('uid', 'nickname', 'title', 'desc', 'config', 'user_like', 'last_download', 'username', 'sign')
# 此字段用于设置fields的权限 read_only: 只有get里有 write_only: 只有post里有
extra_kwargs = {
"uid": {"read_only": True},
"user_like": {"read_only": True},
"last_download": {"read_only": True},
}
首先在serializers
里补齐post里需要的字段username
和sign
, 并在Meta.fields
里添加, 由于他们是只在post里有用, get并不需要, 于是加上write_only=True
属性. 然后在Meta
里新增字段extra_kwargs
, 用于声明所有只读属性(只有get里出现的)
新增函数validate
用于验证表单是否错误, 在这里你甚至可以对参数进行一些调整, 比如交换, 别忘了最后要return attrs
, 如果出错则抛出serializers.ValidationError
然后新增函数save
, 可以发现我们把view里的保存到数据库搬到这里做了, 这样可以使view于database分离, 而且咋后面的mixin中有更大的好处
编写post视图
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Config
from .serializers import ConfigListSerializer
class ConfigView(APIView):
def get(self, request):
config_list = Config.objects.all() # 取所有数据
serializer = ConfigListSerializer(config_list, many=True) # 建立serializer实例
return Response(serializer.data, status=status.HTTP_200_OK) # 返回序列化内容
def post(self, request):
serializer = ConfigListSerializer(request.data) # 从表单里取出内容
if serializer.is_valid(): # 验证表单是否有问题, 就是调用了validate函数
serializer.save() # 没问题的话则保存对象
return Response({"data": "创建完成"}, status=status.HTTP_201_CREATED)
return Response({"error": "表单错误"}, status=status.HTTP_400_BAD_REQUEST)
可以看到视图也没有多少内容了, 然而这还可以进一步精简, 还能只用几行代码添加许多预设功能, 比如过滤/分页, 这些都在后面的Mixin
和viewsets
, 这里才能体会到drf强大的实力