Django Rest Framework(2)-----序列化详解(serializers)

   REST framework中的序列化类与Django的FormModelForm类非常相似。我们提供了一个Serializer类,它提供了一种强大的通用方法来控制响应的输出,以及一个ModelSerializer类,它为创建处理模型实例和查询集的序列化提供了有效的快捷方式。

Serializers 

  序列化器允许把像查询集和模型实例这样的复杂数据转换为可以轻松渲染成JSONXML或其他内容类型的原生Python类型。序列化器还提供反序列化,在验证传入的数据之后允许解析数据转换回复杂类型。不仅仅有序列化功能,更提供了数据验证的功能(与django中的form类似)

 

牛刀小试:

    让我们从创建一个简单的对象开始,我们可以使用下面的例子:

from datetime import datetime

class Comment(object):
    def __init__(self, email, content, created=None):
        self.email = email
        self.content = content
        self.created = created or datetime.now()

comment = Comment(email='leila@example.com', content='foo bar')

    然后声明一个序列化器,我们可以使用它来序列化和反序列化与Comment对象相应的数据。

声明一个序列化器看起来非常像声明一个form:

from rest_framework import serializers

class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

1、序列化与反序列化

# 序列化(Python原生的数据类型dict)
serializer = CommentSerializer(comment)
serializer.data   # {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}

#
转化为json类型 from rest_framework.renderers import JSONRenderer json = JSONRenderer().render(serializer.data) json # b'{"email":"leila@example.com","content":"foo bar","created":"2016-01-27T15:17:10.375877"}'


#
反序列化 from django.utils.six import BytesIO from rest_framework.parsers import JSONParser stream = BytesIO(json) data = JSONParser().parse(stream)

2、保存实例

如果我们希望能够返回基于验证数据的完整对象实例,我们需要实现其中一个或全部实现.create()update()方法。例如:

class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

    def create(self, validated_data):
        return Comment(**validated_data)
        # return Comment.objects.create(**validated_data) # model对象

    def update(self, instance, validated_data):
        instance.email = validated_data.get('email', instance.email)
        instance.content = validated_data.get('content', instance.content)
        instance.created = validated_data.get('created', instance.created)
        # instance.save() model对象
        return instance

ps:.create().update()方法都是可选的。你可以根据你序列化器类的用例不实现、实现它们之一或都实现。

现在当我们反序列化数据的时候,基于验证过的数据我们可以调用.save()方法返回一个对象实例。

comment = serializer.save()

调用.save()方法将创建新实例或者更新现有实例,具体取决于实例化序列化器类的时候是否传递了现有实例:

# .save() will create a new instance.
serializer = CommentSerializer(data=data)

# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)

默认情况下,序列化程序必须为所有必填字段传递值,否则会引发验证错误。你可以使用partial参数以允许部分更新

# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data,partial=True)

 3、验证

    反序列化数据的时候,你始终需要先调用is_valid()方法,然后再尝试去访问经过验证的数据或保存对象实例。如果发生任何验证错误,.errors属性将包含表示生成的错误消息的字典。例如:

serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors   # {'email': [u'Enter a valid e-mail address.'], 'created': [u'This field is required.']}

字典里的每一个键都是字段名称,值是与该字段对应的任何错误消息的字符串列表。non_field_errors键可能存在,它将列出任何一般验证错误信息。non_field_errors的名称可以通过REST framework设置中的NON_FIELD_ERRORS_KEY来自定义。 当对对象列表进行序列化时,返回的错误是每个反序列化项的字典列表。

抛出无效数据的异常

.is_valid()方法使用可选的raise_exception标志,如果存在验证错误将会抛出一个serializers.ValidationError异常。

这些异常由REST framework提供的默认异常处理程序自动处理,默认情况下将返回HTTP 400 Bad Request响应。

# 如果数据无效就返回400响应
serializer.is_valid(raise_exception=True)

字段级别的验证

    你可以通过向你的Serializer子类中添加.validate_<field_name>方法来指定自定义字段级别的验证。这些类似于Django表单中的.clean_<field_name>方法。

    这些方法采用单个参数,即需要验证的字段值。

    你的validate_<field_name>方法应该返回一个验证过的数据或者抛出一个serializers.ValidationError异常。例如:

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

注意: 如果你在序列化器中声明<field_name>的时候带有required=False参数,字段不被包含的时候这个验证步骤就不会执行。

对象级别的验证

    要执行需要访问多个字段的任何其他验证,请添加一个.validate()方法到你的Serializer子类中。这个方法采用字段值字典的单个参数,如果需要应该抛出一个 ValidationError异常,或者知识返回经过验证的值。例如:

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 the start is before the stop.
        """
        if data['start'] > data['finish']:
            raise serializers.ValidationError("finish must occur after start")
        return data

验证器

序列化器上的各个字段都可以包含验证器,通过在字段实例上声明,例如:

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])

序列化器类还可以包括应用于一组字段数据的可重用的验证器。validators给我们提供了很多很好的功能:UniqueValidator,UniqueTogetherValidator等。

这些验证器要在内部的Meta类中声明,如下所示:

UniqueTogetherValidator:(表示联合唯一)

class EventSerializer(serializers.Serializer):
    name = serializers.CharField()
    room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
    date = serializers.DateField()

    class Meta:
        # 每间屋子每天只能有1个活动。
        validators = UniqueTogetherValidator(
            queryset=Event.objects.all(),
            fields=['room_number', 'date']
        )

UniqueValidator:

username = serializers.CharField(
        max_length=11, 
        min_length=11,
        validators=[UniqueValidator(queryset=UserProfile.objects.all())
    )

更多信息请参阅 validators文档

 

ModelSerializer

    它为创建用于处理模型实例和查询集的序列化程序提供了有用的快捷实现方式。自动创建一个Serializer类,字段与model的字段一一对应。

ModelSerializer类与常规Serializer类相同,不同之处在于:

  • 它会根据模型自动生成一组字段。
  • 它会自动为序列化类生成验证器,例如unique_together验证器。
  • 它包含.create()和.update()的简单默认实现。它能够满足将post或patch上来的数据进行进行直接地创建与更新,除非有额外需求,那么就可以重载create与update方法。

声明ModelSerializer如下,在Meta中设置fields字段,系统会自动进行映射成序列化字段,省去每个字段再写一个field。

1、序列化

class CourseSerializer(serializers.ModelSerializer):
    """
    课程序列化
    """class Meta:
        model = models.Course
        # 字段显示
        fields = ['id','title','level'] # 
        # fields = '__all__':  表示所有字段 
        # exclude = ('add_time',): 除去指定的某些字段
        # 只读字段  ---多个字段只读,我们可以这样设置,不用每个字段都设置read_only=True
        read_only_fields=(“field_name”,) # editable=False、AutoField 默认只读,不用添加

2、关于外键

任何关系(如模型上的外键)都将映射到PrimaryKeyRelatedField(但获得外键类别的id)。除非在序列化关系文档中指定,否则默认不包括反向关系。

class CourseDetailSerializer(serializers.ModelSerializer):
    """
    课程详细序列化
    """
    # choice
    level = serializers.CharField(source='course.get_level_display')
    # one2one/fk
    # 外键方式一 需要单个信息
    title = serializers.ReadOnlyField(source='course.title')
    # 外键方式二 需要更多信息
    course=CourseSerializer(many=True)

    # m2m 方法一:使用SerializerMethodField(method_name=None)方法,but该方法为readonly字段。
    #---当不指定method_name ,默认为get_field_name,
    recommends = serializers.SerializerMethodField()
    chapter = serializers.SerializerMethodField()


    class Meta:
        model = models.CourseDetail
        fields = ['course','title','img','level','slogon','why','recommends','chapter']
        # 外键方式三 depth 表示应该遍历的关联深度
        # depth = 1  另外访问时显示外键字段的所有信息,但是只读的,不可编辑,即新建时不能传值
   def get_recommends(self,obj): 

       # 获取推荐的所有课程 
       queryset = obj.recommend_courses.all() 
       return [{'id':row.id,'title':row.title} for row in queryset] 

   def get_chapter(self,obj): 

      # 获取推荐的所有课程 
      queryset = obj.course.chapter_set.all() 
      return [{'id':row.id,'name':row.name} for row in queryset]

ModelSerializer主要需要解决的2个问题:

 1,某个字段不属于指定model,它是write_only,需要用户传进来,但我们不能对它进行save( ),因为ModelSerializer是基于Model,这个字段在Model中没有对应,这个时候,我们需要重载validate!
  如在用户注册时,我们需要填写验证码,这个验证码只需要验证,不需要保存到用户这个Model中:
def validate(self, attrs):
        del attrs["code"]
        return attrs
2,某个字段不属于指定model,它是read_only,只需要将它序列化传递给用户,但是在这个model中,没有这个字段!我们需要用到SerializerMethodField。
  假设需要返回用户加入这个网站多久了,不可能维持这样加入的天数这样一个数据,一般会记录用户加入的时间点,然后当用户获取这个数据,我们再计算返回给它。
class UserSerializer(serializers.ModelSerializer):    
    days_since_joined = serializers.SerializerMethodField()
        
    class Meta:
        model = User

    # 方法写法:get_ + 字段
    def get_days_since_joined(self, obj):
    # obj指这个model的对象
        return (now() - obj.date_joined).days

 

posted @ 2019-01-23 18:21  似是故人来~  阅读(3476)  评论(0编辑  收藏  举报