前言

DRF-Serialization这篇文章中简单介绍了Serialization的使用,更多详细内容请参看官方文档

本文主要对实际开发过程遇到的问题及处理进行总结。

字段处理

只对指定的字段进行数据处理

比如django自带的User models有许多字段,但我们只想对其中的username, email, password进行处理。

这个很简单,通过filed指定即可:

class UserSerializer(serializers.ModelSerializer):
    '''
    user类数据解析
    '''
    class Meta:
        model = User  # 定义关联的 Model
        fields = ('username', 'email', 'password')

如果设置fields = '__all__',代表处理User model的全部字段。

fields相对的是exclude,exclude代表这里面的字段我都不处理。比如我不想处理Useremail字段,而除它之外的其他字段都要进行处理:

class UserSerializer(serializers.ModelSerializer):
    '''
    user类数据解析
    '''
    class Meta:
        model = User  # 定义关联的 Model
        exclude = ('email')

字段只读/只写

只读

我们知道ModelSerializerViewSet结合使用,能够迅速地实现一个表数据的增删查改。

但,有时我们希望修改表数据实例时不能对某些指定字段进行修改,可设置字段为只读。

比如,我们希望修改用户表实例数据时,不能修改用户的密码。

class UserSerializer(serializers.ModelSerializer):
    '''
    user类数据解析
    '''
    class Meta:
        model = User  # 定义关联的 Model
        fields = ('username', 'email', 'password')
        extra_kwargs = {'password': {'read_only': True},}

这样的话,当发送PUT请求`http//127.0.0.1/user//时,携带的body参数:

{ 
    "username": "test",
    "password": "test_password",
    "email": "[email protected]"
}

,我们的UserSerializer并不会对password字段进行数据处理,所以并不会去修改用户的密码。

当然当我们使用GET请求查询数据时,返回的数据仍然会携带password字段。

只写

read_only对应的就是write_only了。 设置了'write_only': True,代表该字段只可写,而不可读。 同样以password为例,为了安全,当返回用户列表的时候,我们并不想返回用户的password字段。

class UserSerializer(serializers.ModelSerializer):
    '''
    user类数据解析
    '''
    class Meta:
        model = User  # 定义关联的 Model
        fields = ('username', 'email', 'password')
        extra_kwargs = {'password': {'write_only': True},}

字段必需

又比如某些字段在model定义为不能为空,对应到serializer中即required=True,但在实际生活中,可能不希望修改此字段。

比如一些系统要求用户名固定,那么用户修改界面,不希望用户能够修改自己的用户名,是不是使用上面介绍的只读就可以了呢?

class UserSerializer(serializers.ModelSerializer):
    '''
    user类数据解析
    '''
    class Meta:
        model = User  # 定义关联的 Model
        fields = ('username', 'email', 'password')
        extra_kwargs = {'username': {'read_only': True},}

当我们用body参数

{
    "email": "[email protected]"
}

向我们的viewset发送PUT请求后,会收到系统返回类似field username is required的错误返回。也就是希望我们在body中携带"username"参数,但实际上因为限制了read_only: True,就算携带了username也毫无意义,那么怎么规避掉这个错误返回呢?这个时候就可以设置required: False

class UserSerializer(serializers.ModelSerializer):
    '''
    user类数据解析
    '''
    class Meta:
        model = User  # 定义关联的 Model
        fields = ('username', 'email', 'password')
        extra_kwargs = {
            'username': {
                'read_only': True,
                'required': False
            }
        }

自定义字段验证

通过在searializer类中自定义.validate_<field_name>方法或者指定字段的Validators

from rest_framework import serializers

class BlogPostSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=100)
    content = serializers.CharField()

    def validate_title(self, value):
        """
        Check that the blog post is about Django.
        """
        if 'django' not in value.lower():
            raise serializers.ValidationError("Blog post is not about Django")
        return value

或者

def multiple_of_ten(value):
    if value % 10 != 0:
        raise serializers.ValidationError('Not a multiple of ten')

class GameRecord(serializers.Serializer):
    score = IntegerField(validators=[multiple_of_ten])
    ...

字段间的验证或者说对象级别的验证

通过在searializer类中自定义.validate()方法

from rest_framework import serializers

class EventSerializer(serializers.Serializer):
    description = serializers.CharField(max_length=100)
    start = serializers.DateTimeField()
    finish = serializers.DateTimeField()

    def validate(self, data):
        """
        Check that start is before finish.
        """
        if data['start'] > data['finish']:
            raise serializers.ValidationError("finish must occur after start")
        return data

context

我们定义serializer是用来做数据的序列化和反序列化的,定义的serializer类通常在view视图中调用,如果我们需要serializer调用view视图中的某些属性应该怎么做呢?

比如我们需要在serializer中调用view视图中的request请求?这时候context就派上用场了

class UserSerializer(serializers.ModelSerializer):
    ...

    def create(self, validated_data):
        request = self.context['request']

那么content中有哪些参数呢?这些参数又是怎么传进去的呢?

让我们来看看源码:

首先在APIView或者ViewSet中是通过request.data生成具体的serializer对象的呢?找到rest_framework/generics.py通用的基类文件

# generics.py

class GenericAPIView(views.APIView):
    ...

    def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs)

    ...

    def get_serializer_context(self):
        """
        Extra context provided to the serializer class.
        """
        return {
            'request': self.request,
            'format': self.format_kwarg,
            'view': self
        }

可以看到在封装好的get_serializer_context函数中将request等参数传给了content参数中,而在get_serializer函数中构造serializer类对象时又传入content了,使得我们能在serializer类中调用self.context['request']而不报错。

看到这里,我们很容易也就知道通过在我们定义的的view中重载get_serializer_context,可以自定义我们想要传到serializer中的参数。

Serializer relations

关系字段用于表示模型关系。它们可以应用于ForeignKey,ManyToManyField和OneToOneField关系,以及反向关系和自定义关系,例如GenericForeignKey。

from django.contrib.auth.models import User
from django.db import models

class UserProfile(models.Model):
    name = models.CharField(null=True, max_length=30)
    gender = models.IntegerField(null=True, verbose_name="性别")
    user = models.OneToOneField(User, on_delete=models.CASCADE, blank=True, null=True)

比如像这种带有关系字段user的,是怎么用serializer进行处理user的呢?

# serializers.py
class ProfileSerializer(serializers.ModelSerializer):
    '''
    用户扩展类数据解析
    '''
    class Meta:
        model = UserProfile
        fields = '__all__'
(.venv) ➜  Cube git:(dev) ✗ python cube/manage.py shell
Python 3.6.4 (v3.6.4:d48ecebad5, Dec 18 2017, 21:07:28)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.4.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from users.serializers import ProfileSerializer

In [2]: print(ProfileSerializer)
<class 'users.serializers.ProfileSerializer'>

In [3]: serializer = ProfileSerializer()

In [4]: serializer
Out[4]:
ProfileSerializer():
    id = IntegerField(label='ID', read_only=True)
    name = CharField(allow_null=True, max_length=30, required=False)
    gender = IntegerField(allow_null=True, label='性别', max_value=2147483647, min_value=-2147483648, required=False)
    user = PrimaryKeyRelatedField(allow_null=True, queryset=User.objects.all(), required=False, validators=[<UniqueValidator(queryset=UserProfile.objects.all())>])

可以看到,serializer默认使用PrimaryKeyRelatedField字段来定义model中外键(包括一对一,一对多),其序列化的数据格式为:

{
    "id": 2,
    "name": "admin",
    "gender": 1,
    "user": 1
}

当然我们也可以使用其他field来定义,类似StringRelatedFieldStringRelatedField可用于定义了__str__方法的model。

user = StringRelatedField()

user返回的数据则是__str__方法中定义的数据。这里的user对应的是model中外键的字段名

类似的字段还有HyperlinkedRelatedFieldSlugRelatedFieldHyperlinkedIdentityField,具体可参看官方文档。

serializer 嵌套

当我们对UserProfile进行数据校验和序列化的时候,还想对UserProfile中外键关联的User model进行校验和序列化,该怎么做呢?

serializer支持嵌套,也就是可以在serializer中定义serializer,比如:

# serializers.py
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('username', 'email')

class ProfileSerializer(serializers.ModelSerializer):
    user = UserSerializer()
    class Meta:
        model = UserProfile
        fields = '__all__'

那么序列化后数据类似:

{
    "id": 2,
    "name": "admin",
    "gender": 1,
    "user": {
        "username": "admin",
        "email": "[email protected]"
    }
}

当然上面的UserUserProfile的关联是通过外键实现的,若没有外键,则需要自己去建立这一层关系。

举个🌰,将user外键改成一个普通的int字段:

class UserProfile(models.Model):
    name = models.CharField(null=True, max_length=30)
    gender = models.IntegerField(null=True, verbose_name="性别")
    user = models.IntegerField(null=True, verbose_name="user id")

然后再来实现我们的serializer:

class ProfileSerializer(serializers.ModelSerializer):
    user = UserSerializer()
    class Meta:
        model = UserProfile
        fields = '__all__'

    def to_representation(self, instance):
        ret = super().to_representation(instance)

        try:
            user = User.objects.get(id=instance.user)
        except User.DoesNotExist:
            ret['user'] = None
        else:
            serializer = UserSerializer(user)
            if serializer.is_valid:
                ret['profile'] = serializer.data

        return ret
-> instance = UserProfile.objects.get(id=2)
-> serializer = ProfileSerializer(instance)
-> print(serializer.data)
-> {
->     "id": 2,
->     "name": "admin",
->     "gender": 1,
->     "user": {
->         "username": "admin",
->         "email": "[email protected]"
->      }
-> }
Copyright © itrunner.cn 2020 all right reserved,powered by Gitbook该文章修订时间: 2022-08-28 07:44:16

results matching ""

    No results matching ""