Django rest framework(一)-序列化器

本文为学习官方文档时所做笔记,可以看做是官方文档的全文翻译

序列化器

序列化器API

  • 基于验证数据返回完整的实例对象,需要实现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)
    
        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)
            #如果对象实例对应于Django模型,那么还需要确保这些方法将对象保存到数据库中
            instance.save()
            return instance
    
  • save

    • 常规存储

      #创建和更新取决于在实例化serializer类时是否传递了一个已有的实例:
      serializer = CommentSerializer(data=data) #创建
      serializer = CommentSerializer(comment, data=data) #更新,comment为某个已经存在的实例
      
      #当反序列化数据时,我们可以调用.save()来根据验证过的数据返回一个对象实例。
      comment = serializer.save()
      
    • 存储时传递额外参数:有时希望视图代码能够在保存实例时注入额外数据。这些附加数据可能包括当前用户、当前时间或其他不属于请求数据的信息。 可以通过在调用save()时包含额外的关键字参数来实现。

      serializer.save(owner=request.user)
      

      传递的参数可以在validated_data 中得到

    • 重写save():某些时候并不需要创建、更新数据库或者在创建、更新数据库的同时完成一些其他功能,此时就可以重写save

      class ContactForm(serializers.Serializer):
          email = serializers.EmailField()
          message = serializers.CharField()
      
          def save(self):
              email = self.validated_data['email']
              message = self.validated_data['message']
              send_email(from=email, message=message) #收到数据后发送邮件
      

      注意:在上面的例子中,我们现在必须直接访问serializer .validated_data属性而不是通过get方法。

  • validation数据验证

    • 视图中使用

      serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})
      serializer.is_valid()
      # False
      serializer.errors
      #验证错误时可以打印出错误信息 {'email': ['Enter a valid e-mail address.'], 'created': ['This field is required.']} 
      

      注意:字典中的每个键都是是字段名,值则是与该字段对应的任何错误消息的字符串列表。还可能出现non_field_errors键,它将列出所有常规验证错误。non_field_errors键的名称可以在全局设置中使用NON_FIELD_ERRORS_KEY REST定制。

    • 直接引发异常:当发生数据验证错误时,不返回错误信息直接引发异常,这些异常由REST框架提供的默认异常处理程序自动处理,默认情况下将返回HTTP 400 Bad请求响应。

      # Return a 400 response if the data was invalid.
      serializer.is_valid(raise_exception=True)
      
    • 自定义字段验证

      #类似于django中form的clean验证
      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,那么若不包含该字段,则不会执行此验证步骤。

    • 自定义全局验证:可同时验证多个字段值或不需要保存到数据库中的字段值

      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
      
    • 在字段声明中使用验证器Validators进行验证

      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进行验证

      class EventSerializer(serializers.Serializer):
          name = serializers.CharField()
          room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
          date = serializers.DateField()
      
          class Meta:
              # Each room only has one event per day.
              validators = UniqueTogetherValidator(
                  queryset=Event.objects.all(),
                  fields=['room_number', 'date']
              )
      
  • 访问序列化初始值initial_data

  • 部分更新:默认情况下,必须向序列化器传递所有必需字段的值,否则它们将引发验证错误。但可以使用partial参数来允许部分更新。

    serializer = CommentSerializer(comment, data={'content': 'foo bar'}, partial=True)
    
  • 处理嵌套(关系)数据

    class UserSerializer(serializers.Serializer):
        email = serializers.EmailField()
        username = serializers.CharField(max_length=100)
    
    class CommentSerializer(serializers.Serializer):
        '''
    	如果接收None值,则需要传递required=False
    	如果接收一个集合值,则需要传递many=True
        '''
        user = UserSerializer(required=False)
        edits = EditItemSerializer(many=True)  
    
    #数据传入-嵌套表示
    serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})
    
    #数据验证错误-嵌套表示
    serializer.is_valid()
    serializer.errors
    {'user': {'email': ['Enter a valid e-mail address.']}, 'created': ['This field is required.']}
    

    保存嵌套数据:通过重写 .create() 或者 .update() 方法

    class UserSerializer(serializers.ModelSerializer):
        profile = ProfileSerializer()
    
        class Meta:
            model = User
            fields = ['username', 'email', 'profile']
            
        def create(self, validated_data):
            profile_data = validated_data.pop('profile')
            user = User.objects.create(**validated_data)
            Profile.objects.create(user=user, **profile_data)
            return user
        
        def update(self, instance, validated_data):
            profile_data = validated_data.pop('profile')
            # Unless the application properly enforces that this field is
            # always set, the follow could raise a `DoesNotExist`, which
            # would need to be handled.
            profile = instance.profile
    
            instance.username = validated_data.get('username', instance.username)
            instance.email = validated_data.get('email', instance.email)
            instance.save()
    
            profile.is_premium_member = profile_data.get(
                'is_premium_member',
                profile.is_premium_member
            )
            profile.has_support_contract = profile_data.get(
                'has_support_contract',
                profile.has_support_contract
             )
            profile.save()
    
            return instance
    

    保存嵌套数据:通过对应的重写模型管理器

    #创建模型时重写User对应的管理器
    class UserManager(models.Manager):
        ...
    
        def create(self, username, email, is_premium_member=False, has_support_contract=False):
            user = User(username=username, email=email)
            user.save()
            profile = Profile(
                user=user,
                is_premium_member=is_premium_member,
                has_support_contract=has_support_contract
            )
            profile.save()
            return user
        
    #序列化器中重写create可以便捷保存,相当于把保存逻辑移到了模型管理器中
    class UserSerializer(serializers.ModelSerializer):
        profile = ProfileSerializer()
        
        def create(self, validated_data):
            return User.objects.create(
                username=validated_data['username'],
                email=validated_data['email']
                is_premium_member=validated_data['profile']['is_premium_member']
                has_support_contract=validated_data['profile']['has_support_contract']
            )
    
  • 处理多个对象(对象列表):要序列化一个查询集或对象列表而不是单个对象实例,在实例化序列化器时应该传递many=True参数。

    queryset = Book.objects.all()
    serializer = BookSerializer(queryset, many=True) #传递一个查询集
    serializer.data
    #输出:
    # [
    #     {'id': 0, 'title': 'The electric kool-aid acid test', 'author': 'Tom Wolfe'},
    #     {'id': 1, 'title': 'If this is a man', 'author': 'Primo Levi'},
    #     {'id': 2, 'title': 'The wind-up bird chronicle', 'author': 'Haruki Murakami'}
    # ]
    
  • 向序列化器提供额外的上下文

    #传递
    serializer = AccountSerializer(account, context={'request': request})
    
    #在序列化器中使用,上下文字典可以在任何序列化字段逻辑中使用
    request = self.context['request']
    
  • 序列化器继承

    class MyBaseSerializer(Serializer):
        my_field = serializers.CharField()
    
        def validate_my_field(self, value):
            ...
    
    class MySerializer(MyBaseSerializer):
        #不需要继承的字段可以设置为None
        my_field = None
        ...
        #如果需要继承元类则必须显式的写出来,但通常不建议继承元类
        class Meta(MyBaseSerializer.Meta):
            model = Account
    
  • 动态序列化:根据输入的参数决定虚拟化的参数

    class DynamicFieldsModelSerializer(serializers.ModelSerializer):
        """
        A ModelSerializer that takes an additional `fields` argument that
        controls which fields should be displayed.
        """
    
        def __init__(self, *args, **kwargs):
            # Don't pass the 'fields' arg up to the superclass
            fields = kwargs.pop('fields', None)
    
            # Instantiate the superclass normally
            super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)
    
            if fields is not None:
                # Drop any fields that are not specified in the `fields` argument.
                allowed = set(fields)
                existing = set(self.fields)
                for field_name in existing - allowed:
                    self.fields.pop(field_name)
    

    动态输入参数

    >>> class UserSerializer(DynamicFieldsModelSerializer):
    >>>     class Meta:
    >>>         model = User
    >>>         fields = ['id', 'username', 'email']
    >>>
    >>> print(UserSerializer(user))
    {'id': 2, 'username': 'jonwatts', 'email': 'jon@example.com'}
    >>>
    >>> print(UserSerializer(user, fields=('id', 'email')))
    {'id': 2, 'email': 'jon@example.com'}
    

模型序列化器

  • 概述:通常需要与Django模型定义紧密映射的序列化器类。

    • 根据模型自动生成一组字段
    • 自动生成字段对应的验证器,如unique_together
    • 包含了一些简单的对createupdate方法的默认实现
    class AccountSerializer(serializers.ModelSerializer):
        
        #添加额外字段、重写默认字段配置
        url = serializers.CharField(source='get_absolute_url', read_only=True)
        groups = serializers.PrimaryKeyRelatedField(many=True)
        
        class Meta:
            model = Account
            fields = ['id', 'account_name', 'users', 'created']
            #exclude = []
            depth = 1,用在嵌套关系时,指向下遍历的深度,但通常还是使用自定义嵌套字段
            read_only_fields = ['account_name'] #给多个字段设置只读属性,设置了editable=False和属于AutoField的字段默认含有只读属性
            extra_kwargs = {'url': {'write_only': True}}#给字段设置额外的属性,字典形式,优先级在显示设置之下
    

列表序列化器

  • ListSerializer类提供了同时序列化和验证多个对象的行为。通常不需要直接使用ListSerializer,而是会在实例化一个传递了many=True参数的序列化器时自动调用。

  • 使用场合:希望提供列表的特定验证,例如检查一个元素是否与列表中的另一个元素冲突;需要自定义多个对象的创建或更新行为 等

    #自定义多对象创建,通常情况下会对列表中的每个对象调用一次create方法,下面改为使用bulk——create进行创建
    class BookListSerializer(serializers.ListSerializer):
        def create(self, validated_data):
            books = [Book(**item) for item in validated_data]
            return Book.objects.bulk_create(books)
    
    class BookSerializer(serializers.Serializer):
        ...
        class Meta:
            list_serializer_class = BookListSerializer
    

基础序列化器

  • BaseSerializer可用来方便的自定义序列化器,基本用法与常规的序列化器无太大区别,注意下面两个方法,这两个方法在其他序列化器中同样可以通过重写来是实现需要的逻辑

    • to_representation() :重写它以支持读取操作的序列化。
    • to_internal_value() :重写它以支持写操作的反序列化
  • 自定义一个只读序列化器

    #模型
    class HighScore(models.Model):
        created = models.DateTimeField(auto_now_add=True)
        player_name = models.CharField(max_length=10)
        score = models.IntegerField()
        
    #序列化器
    class HighScoreSerializer(serializers.BaseSerializer):
        def to_representation(self, instance):#自定义序列化
            return {
                'score': instance.score,
                'player_name': instance.player_name
            }
        
    #视图
    @api_view(['GET'])
    def high_score(request, pk):
        instance = HighScore.objects.get(pk=pk)
        serializer = HighScoreSerializer(instance)
        return Response(serializer.data)
    
    @api_view(['GET'])
    def all_high_scores(request):
        queryset = HighScore.objects.order_by('-score')
        serializer = HighScoreSerializer(queryset, many=True)
        return Response(serializer.data)
    
  • 自定义一个读写序列化器

    • 重写to_internal_value(),此后就有可能引发验证错误,.is_valid(), .validated_data and .errors 等API也将可用
    • 如果相同时支持save(),那么需要重写create()update()
    class HighScoreSerializer(serializers.BaseSerializer):
        def to_internal_value(self, data):
            score = data.get('score')
            player_name = data.get('player_name')
    
            # Perform the data validation.
            if not score:
                raise serializers.ValidationError({
                    'score': 'This field is required.'
                })
            if not player_name:
                raise serializers.ValidationError({
                    'player_name': 'This field is required.'
                })
            if len(player_name) > 10:
                raise serializers.ValidationError({
                    'player_name': 'May not be more than 10 characters.'
                })
    
            # Return the validated values. This will be available as
            # the `.validated_data` property.
            return {
                'score': int(score),
                'player_name': player_name
            }
    
        def to_representation(self, instance):
            return {
                'score': instance.score,
                'player_name': instance.player_name
            }
    
        def create(self, validated_data):
            return HighScore.objects.create(**validated_data)
    

序列化器第三方包

Serpy

  • serpy是序列化器的另一种实现,构建它可以提高序列化速度。Serpy将复杂的数据类型序列化为简单的本机类型。原生类型可以很容易地转换为JSON或任何其他需要的格式 。

Dynamic REST

  • Dynamic REST扩展了ModelSerializerModelViewSet接口,添加了用于过滤、排序以及包括/排除序列化器定义的所有字段和关系的API查询参数。

HTML JSON Forms

  • 提供一种对表单数据进行序列化和计算的算法。序列化器有助于处理HTML中任意嵌套的JSON结构 。如<input name="items[0][id]" value="5">将会被解析为 {"items": [{"id": "5"}]}.

DRF-Base64

  • 提供了一组字段和模型序列化器,用于处理base64编码的文件的上传 。

QueryFields

  • djangorestframe- queryfields允许API客户端通过包含/排除查询参数指定在响应中返回的字段。

DRF Writable Nested

  • 提供可写的嵌套模型序列化器,允许用嵌套的相关数据创建/更新模型。

序列化字段

  • 序列化器字段处理原始值和内部数据类型之间的转换。它们还处理验证输入值,以及从父对象检索和设置值。

通用参数

  • 任何字段都能接收以下参数,属于通用的、核心的参数

    • read_only:只读,只读字段包含在API输出中,但是在创建或更新操作期间不应该包含在输入中。

    • write_only:在更新或创建实例时可以使用该字段,但在序列化表示时不包括该字段。

    • required:是否必须

    • default:默认值,注意,设置默认值意味着该字段不是必需的

    • allow_null:是否允许为空

    • source:引用其他方法或属性作为值,引用值为空时请注意设置default

      • URLField(source='get_absolute_url')
      • EmailField(source='user.email').
    • validators:验证器

    • error_messages:错误信息,字典格式

    • label:HTML表单字段或其他描述性元素中用作字段名称的短文本字符串

    • help_text:在HTML表单字段或其他描述性元素中用作字段描述的文本字符串

    • initial:一个应该用于预填充HTML表单字段值的值

      class ExampleSerializer(serializers.Serializer):
          day = serializers.DateField(initial=datetime.date.today)
      
    • style:控制HTML元素的呈现方式,字典格式

      password = serializers.CharField(
          style={'input_type': 'password'}
      ) #<input type="password">
      
      color_channel = serializers.ChoiceField(
          choices=['red', 'green', 'blue'],
          style={'base_template': 'radio.html'}
      ) #使用单选框
      

Boolean fields

  • Booleanfields:注意当使用HTML表单传递时,若有缺省值默认为False即使设置了default=True
  • NullBooleanField:接收null

String fields

  • CharField:字符串类型

    • max_length
    • min_length
    • allow_blank :如果设置为真,那么空字符串应该被认为是一个有效值。否则则认为空字符串无效,并将引发验证错误。默认值为False,不建议使用allow_null或者两者混合使用
    • trim_whitespace :是否删除首位空格
  • EmailField:验证邮箱格式

    EmailField(max_length=None, min_length=None, allow_blank=False)
    
  • RegexField:验证数据是否匹配某个正则表达式

    RegexField(regex, max_length=None, min_length=None, allow_blank=False)#使用了django.core.validators.RegexValidator验证器进行验证
    
  • SlugField:只匹配数字和大小写字母,至少有一个,正则表达式为[a-zA-Z0-9_-]+

  • URLField:匹配路由地址http://<host>/<path>

    URLField(max_length=200, min_length=None, allow_blank=False)#使用了django.core.validators.URLValidator验证器
    
  • UUIDField:确保输入是有效的UUID(通用唯一识别码)字符串的字段

    UUIDField(format='hex_verbose')
    
    • 'hex_verbose' 带连字符的格式: "5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
    • 'hex' 不带连字符的格式: "5ce0e9a55ffa654bcee01238041fb31a"
    • 'int' 128位整数格式: "123456789012312313134124512351145145114"
    • 'urn' - RFC 4122 URN 格式: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"

    注意:更改格式只改变表示值而不影响实际值

  • FilePathField:一种字段,其选择仅限于文件系统上某个目录中的文件名 ,通俗讲即仅限于选择服务器上的路径

    FilePathField(path, match=None, recursive=False, allow_files=True, allow_folders=False, required=None, **kwargs)
    
    • path :文件系统上的某个目录名,必填
    • match :正则表达式,用来过滤文件名
    • recursive :指定是否包含path的所有子目录
    • allow_files :指定是否应包括指定位置中的文件,默认为真
    • allow_folders:指定是否应包括指定位置中的文件夹 ,默认为假
  • IPAddressField:IPV4或IPV6格式

    IPAddressField(protocol='both', unpack_ipv4=False, **options)
    #对应django.forms.fields.IPAddressField和django.forms.fields.GenericIPAddressField
    
    • protocol可选both (default), IPv4 或者 IPv6.
    • unpack_ipv4:默认禁用,可以将IPV6地址解包为IPV4地址,只有在protocol='both'下可用

Numeric fields

  • IntegerField

    • 对应与Django原生中的django.db.models.fields.IntegerField, django.db.models.fields.SmallIntegerField, django.db.models.fields.PositiveIntegerField and django.db.models.fields.PositiveSmallIntegerField. 四个
  • FloatField

  • DecimalField:小数、十进制,对应于django.db.models.fields.DecimalField.

    • max_digits :最大显示位数,必须是None或大于等于decimal_places的整数。
    • decimal_places:小数位数
    • coerce_to_string :决定返回字符串形式还是Decimal对象,后者的表现形式将有渲染器决定;注意若设置了localize 将强制为真,默认值与COERCE_DECIMAL_TO_STRING 全局配置相同,为真。
    • localize :设置为True将启用基于当前语言环境的输入和输出本地化 ,注意如果在settings中已经设置了USE_L10N=True 则无需设置该值
    • rounding :设置量化到配置精度时使用的舍入模式
    #表示一个不超过10亿的数字,小数位精确到10位
    serializers.DecimalField(max_digits=19, decimal_places=10)
    

Date and time fields

  • DateTimeField
    • format :输出格式,默认为DATETIME_FORMATiso-8601 比如'2013-01-29T12:34:56.000000Z'
    • input_formats :表示可用于解析日期的输入格式的字符串列表,默认。 DATETIME_INPUT_FORMATS ,为['iso-8601'] ,设置为None表示日期编码将由渲染器决定。
    • default_timezone :时区设置,默认USE_TZ
    • auto_now=Trueauto_now_add=True ,默认会使用read_only=True
  • DateField示例'2013-01-29'
  • TimeField:示例'12:34:56.000000'
  • DurationField:时长、持续时间、间隔,格式'[DD] [HH:[MM:]]ss[.uuuuuu]'
    • max_value
    • min_value

Choice selection fields

  • ChoiceField
    • choices,有效值列表(元组)
    • allow_blankallow_null
    • html_cutoff :用来限定HTML下拉选项数量的最大值
    • html_cutoff_text :当选项数量超过html_cutoff 值时提供一个文本指示器进行提示,字符串格式
  • MultipleChoiceField:接收0、1或多个值

File upload fields

本类中的fields支持在MultiPartParserFileUploadParser 解析器下使用,大多数解析器如JSONj解析器不支持文件类型

  • FileField
    • max_length :上传文件名的最大长度
    • allow_empty_file:是否允许上传空文件
    • use_url:默认值UPLOADED_FILES_USE_URL 为真,使用URL 字符串之作为输出表示;否则使用文件名字符串作为字符串输出表示
  • ImageField:图片上传,注意必须使用第三方包Pillow

Composite fields

  • ListField:验证对象列表的字段类

    • child:一个字段实例,用于验证列表中的对象。如果没有提供此参数,则不会验证列表中的该类型对象
    • allow_empty
    • min_lengthmax_length ,限制列表元素个数
    scores = serializers.ListField(
       child=serializers.IntegerField(min_value=0, max_value=100)
    )
    

    定制可重用的

    class StringListField(serializers.ListField):
        child = serializers.CharField()
    
  • DictFild:验证对象字典的字段类。DictField中的键总是假设为字符串值。

    document = DictField(child=CharField())
    
  • HstoreField

  • JSONField

Miscellaneous fields

  • ReadOnlyField() :当需要展示一个属性值而不是模型字段时会自动生成一个只读字段

    #has_expired为属性值
    class AccountSerializer(serializers.ModelSerializer):
        class Meta:
            model = Account
            fields = ['id', 'account_name', 'has_expired']
    
  • HiddenField:不根据用户输入获取值,而是从默认值或可调用值获取值的字段类。 常在需要基于某些预先提供的字段值进行某些验证但又不想向用户公开这些字段时使用。

    modified = serializers.HiddenField(default=timezone.now)
    
  • ModelField:可以绑定到任意模型字段的通用字段

  • SerializerMethodField:只读字段,获得一个与之相关联的方法的返回值作为值,可用于向对象的序列化表示中添加任何类型的数据。

    • method_name:关联的方法名,如果没有设置则默认为get_<field_name>

      class UserSerializer(serializers.ModelSerializer):
          days_since_joined = serializers.SerializerMethodField()
      
          class Meta:
              model = User
      
          def get_days_since_joined(self, obj):#obj是序列化的对象
              return (now() - obj.date_joined).days #可返回任何类型的数据
      

自定义fields

继承serializers.Field后重写to_representation() and to_internal_value() 方法,这两个方法用于在初始数据类型和原始的可序列化数据类型之间进行转换

第三方包

  • DRF Compound Fields:提供复合序列化字段
  • DRF Extra Fields:提供一下其他的序列化字段,如Base64ImageFieldPointField
  • djangorestframework-recursive:提供RecursiveField 用于序列化和反序列化递归结构
  • django-rest-frameword-gis:提供地理字段 GeometryField

序列化关系

ForeignKey, ManyToManyFieldOneToOneField 表示正向和反向关系,也可以使用GenericForeignKey.自定义关系字段

当使用ModelSerializer类时,将自动为您生成序列化器字段和关系。检查这些自动生成的字段是确定如何定制关系样式的有用工具。

引用API

  • 模型:

    class Album(models.Model):
        album_name = models.CharField(max_length=100)
        artist = models.CharField(max_length=100)
    
    class Track(models.Model):
        album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)
        order = models.IntegerField()
        title = models.CharField(max_length=100)
        duration = models.IntegerField()
    
        class Meta:
            unique_together = ['album', 'order']
            ordering = ['order']
        def __str__(self):
        	return '%d: %s' % (self.order, self.title)
    
  • StringRelatedField:以关系目标的 __str__方法表示目标关系

    • 序列化

      class AlbumSerializer(serializers.ModelSerializer):
          tracks = serializers.StringRelatedField(many=True) #只读
      
          class Meta:
              model = Album
              fields = ['album_name', 'artist', 'tracks']
      
    • 输出

      {
          'album_name': 'Things We Lost In The Fire',
          'artist': 'Low',
          'tracks': [
              '1: Sunflower',
              '2: Whitetail',
              '3: Dinosaur Act', #输出格式为Track中__str__方法所表示的格式
              ...
          ]
      }
      
  • PrimaryKeyRelatedField:利用主键表示关系

    • 序列化

      class AlbumSerializer(serializers.ModelSerializer):
          tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True) #可读写
      
      • queryset:验证字段输入时用于模型实例查找的查询集 ,若没有设置read_only则必须显示的设置该查询集,本例中即为queryset = track.object.all()
      • many
      • allow_null:默认为False
      • pk_field:设控制主键值的序列化/反序列化表示形式的字段,如。 pk_field=UUIDField(format='hex') 可以将UUID主键序列化为紧凑的十六进制表示。
    • 输出

      {
          'album_name': 'Undun',
          'artist': 'The Roots',
          'tracks': [
              89,
              90,
              91, #主键表示
              ...
          ]
      }
      
  • HyperlinkedRelatedField :使用超链接表示关系目标

    • 序列化

      class AlbumSerializer(serializers.ModelSerializer):
          tracks = serializers.HyperlinkedRelatedField(
              many=True,
              read_only=True,
              view_name='track-detail'
      
      • view_name :路由别名对应于路由设置中的name,标准格式为<modelname>-detail
      • queryset
      • many
      • allow_null
      • lookup_field : 查询单一数据库对象时使用的过滤字段 ,用来对queryset进行过滤。默认是pk
      • lookup_url_kwarg : 查询单一数据时URL中所带的参数关键字名称 ,默认与lookup_field 相同
      • format:格式后缀,超链接字段将为目标使用相同的格式后缀
    • 输出

      {
          'album_name': 'Graceland',
          'artist': 'Paul Simon',
          'tracks': [
              'http://www.example.com/api/tracks/45/',
              'http://www.example.com/api/tracks/46/',
              'http://www.example.com/api/tracks/47/',  #超链接形式表示
              ...
          ]
      }
      
  • SlugRelatedField:使用关系目标上的某个字段来表示关系目标。

    • 序列化

      class AlbumSerializer(serializers.ModelSerializer):
          tracks = serializers.SlugRelatedField(
              many=True,
              read_only=True,
              slug_field='title'
           )
      
      • slug_field :关系目标上的某个字段
      • queryset
      • many
      • allow_null
    • 输出

      {
          'album_name': 'Dear John',
          'artist': 'Loney Dear',
          'tracks': [
              'Airport Surroundings',
              'Everything Turns to You',
              'I Was Only Going Out', #使用title字段
              ...
          ]
      }
      
  • HyperlinkedIdentityField:通常用来根据传进来的路由参数反向解析生成对应的链接

    • 序列化

      class AlbumSerializer(serializers.HyperlinkedModelSerializer):
          track_listing = serializers.HyperlinkedIdentityField(view_name='track-list')
      
      • view_name :路由别名
      • lookup_field ,默认为pk
      • lookup_url_kwarg
      • format
    • 输出

      {
          'album_name': 'The Eraser',
          'artist': 'Thom Yorke',
          'track_listing': 'http://www.example.com/api/track_list/12/',#根据view_name和lookup_field反向解析生成的链接,这里没有其他参数即无额外的lookup_url_kwarg
      }
      

嵌套关系

与之前讨论的对另一个实例的关系引用相比,被引用的实例也可以直接嵌入或嵌套在引用它的对象的表示中,这种嵌套关系可以通过使用序列化器作为字段来表示。

  • 只读的嵌套关系

    • 序列化

      class TrackSerializer(serializers.ModelSerializer):
          class Meta:
              model = Track
              fields = ['order', 'title', 'duration']
      
      class AlbumSerializer(serializers.ModelSerializer):
          tracks = TrackSerializer(many=True, read_only=True) #序列化器直接作为字段
      
          class Meta:
              model = Album
              fields = ['album_name', 'artist', 'tracks']
      
    • 输入输出

      >>> album = Album.objects.create(album_name="The Grey Album", artist='Danger Mouse')
      >>> Track.objects.create(album=album, order=1, title='Public Service Announcement', duration=245)
      <Track: Track object>
      >>> Track.objects.create(album=album, order=2, title='What More Can I Say', duration=264)
      <Track: Track object>
      >>> Track.objects.create(album=album, order=3, title='Encore', duration=159)
      <Track: Track object>
      >>> serializer = AlbumSerializer(instance=album)
      >>> serializer.data
      {
          'album_name': 'The Grey Album',
          'artist': 'Danger Mouse',
          'tracks': [
              {'order': 1, 'title': 'Public Service Announcement', 'duration': 245},
              {'order': 2, 'title': 'What More Can I Say', 'duration': 264},
              {'order': 3, 'title': 'Encore', 'duration': 159},
              ...
          ],
      }
      
  • 可写的嵌套关系:默认情况下,嵌套序列化器中的数据是只读的。如果希望支持对嵌套序列化字段的写操作,则需要重写create()和或update()方法,以便显式地指定应该如何保存嵌套关系。

    • 序列化

      class TrackSerializer(serializers.ModelSerializer):
          class Meta:
              model = Track
              fields = ['order', 'title', 'duration']
              
      class AlbumSerializer(serializers.ModelSerializer):
          tracks = TrackSerializer(many=True)       
          class Meta:
              model = Album
              fields = ['album_name', 'artist', 'tracks']
              
          #重写create方法
          def create(self,validated_data):
              tracks_data = validated_data.pop('tracks')
              album = Album.object.create(**validated)
              for track_data in tracks_data:
                  Track.object.create(album=aibum,**track_data)
              return album
      
    • 输出输出

      >>> data = {
          'album_name': 'The Grey Album',
          'artist': 'Danger Mouse',
          'tracks': [
              {'order': 1, 'title': 'Public Service Announcement', 'duration': 245},
              {'order': 2, 'title': 'What More Can I Say', 'duration': 264},
              {'order': 3, 'title': 'Encore', 'duration': 159},
          ],
      }
      >>> serializer = AlbumSerializer(data=data)
      >>> serializer.is_valid()
      True
      >>> serializer.save()
      <Album: Album object>
      

自定义关系字段

某些情况下,现有的关系样式没有用户需要的输出表示,所以可以实现一个完全自定义的关系字段,它精确地描述应该如何从模型实例生成输出表示。

  • 步骤:

    • 重写RelatedField
    • 实现to_representation(self, value) 方法,实现输出表示,该方法将字段的目标作为值参数 。value参数通常是一个模型实例。
    • 实现to_internal_value(self, data) 方法,可以实现读写
  • 举例:

    class TrackListingField(serializers.RelatedField):
        def to_representation(self, value):
            duration = time.strftime('%M:%S', time.gmtime(value.duration))
            return 'Track %d: %s (%s)' % (value.order, value.name, duration)
        
    class AlbumSerializer(serializers.ModelSerializer):
        tracks = TrackListingField(many=True)
    
        class Meta:
            model = Album
            fields = ['album_name', 'artist', 'tracks']
    

    输出

    {
        'album_name': 'Sometimes I Wish We Were an Eagle',
        'artist': 'Bill Callahan',
        'tracks': [
            'Track 1: Jim Cain (04:39)',
            'Track 2: Eid Ma Clack Shaw (04:19)',
            'Track 3: The Wind and the Dove (04:34)',
            ...
        ]
    }
    

自定义超链接字段

https://www.django-rest-framework.org/api-guide/relations/#custom-hyperlinked-fields

自定义通用关系

如果要序列化通用外键,则需要定义自定义字段,以显式确定要如何序列化关系的目标

  • 模型

    class TaggedItem(models.Model):
        tag_name = models.SlugField()
        content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
        object_id = models.PositiveIntegerField()
        tagged_object = GenericForeignKey('content_type', 'object_id')
    
        def __str__(self):
            return self.tag_name
        
    #可能与TaggerItem关联的两个模型
    class Bookmark(models.Model):
        url = models.URLField()
        tags = GenericRelation(TaggedItem)
        
    class Note(models.Model):
        text = models.CharField(max_length=1000)
        tags = GenericRelation(TaggedItem)    
    
  • 自定义序列化字段,根据传进来的实例自动选择关系字段的序列化输出

    class TaggedObjectRelatedField(serializers.RelatedField):
        def to_representation(self, value):
            if isinstance(value, Bookmark):
                return 'Bookmark: ' + value.url
            elif isinstance(value, Note):
                return 'Note: ' + value.text
            raise Exception('Unexpected type of tagged object')
    
  • 更进一步地,如果关系字段含有嵌套关系则需要使用所需的序列化器

        def to_representation(self, value):
            if isinstance(value, Bookmark):
                serializer = BookmarkSerializer(value)
            elif isinstance(value, Note):
                serializer = NoteSerializer(value)
            else:
                raise Exception('Unexpected type of tagged object')
            return serializer.data
    

第三方包

  • drf-nested-routers package :提供和支持嵌套资源的路由器和关系字段
  • rest-framework-generic-relations :提供对通用外键generic foreign keys 的读写序列化支持
posted @ 2020-06-24 17:56  言兴  阅读(455)  评论(0)    收藏  举报