Django Rest Framework 1:序列化与反序列化

这个案例来自于官网django-rest-framework.org

一,开发准备

1,创建django项目tutorial

django-admin.py startproject tutorial

2,创建应用snippet

python manage.py startapp snippet

3,安装DRF

pip install djangorestframework
pip install pygments  # 用于代码高亮

4,配置settings.py

INSTALLED_APPS = (
    ...
    'rest_framework',
    'snippet.apps.SnippetConfig',
)

完成后项目结构如下:
在这里插入图片描述

5,创建模型Snippet

在snippet/models.py中创建模型Snippet

from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles

# 提取出了 pygments 支持的所有语言的词法分析程序
LEXERS = [item for item in get_all_lexers() if item[1]]
# 提取出了 pygments 支持的所有语言列表
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
# 提取出了 pygments 支持的所有格式化风格列表
STYLE_CHOICES = sorted((item, item) for item in get_all_styles())


# Create your models here.
class Snippet(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default='')
    code = models.TextField()
    linenos = models.BooleanField(default=False)  # 是否显示行号
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)

    class Meta:
        ordering = ('created',)

6,数据迁移

python manage.py makemigrations snippets
python manage.py migrate

产生如下数据表:
在这里插入图片描述

序列化器允许将复杂数据 (如查询集和模型实例) 转换为可以轻松渲染成 JSON,XML 或其他内容类型的原生 Python 数据类型。
序列化器还提供反序列化功能,在验证传入的数据之后允许将解析数据转换回复杂类型。

序列化器需要通过继承 Rest Framework 提供的序列化类 SerializersModelSerializers 类来实现,在使用方法与 django 的表单类的使用方法相似。

二,使用 Serializers 类

在进一步了解之前,我们需要先熟悉使用我们的 Serializer 类。

(一)Serializers 类

1,声明序列化器

使用 Serializers 类声明序列化器时,最主要的就是定义序列化器的各个字段以及相应的方法,例如:

模型:
class Comment(models.Model):
    email = models.EmailField()
    content = models.CharField(max_length=100)
    created = models.DateTimeField(auto_now_add=True)

序列化器:
from rest_framework import serializers

class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=100)
    created = serializers.DateTimeField()
	
	def create(self, validated_data):
        """
        给定验证过的数据创建并返回一个新的 Comment 实例。
        """
        return Comment.objects.create(**validated_data)
    
    def update(self, instance, validated_data):
        """
        根据已验证的数据更新并返回已存在的 Comment 实例。
        """
        instance.email = validated_data.get('email', instance.email)
        instance.content = validated_data.get('content', instance.code)
        instance.created = validated_data.get('created', instance.created)
        instance.save()
        return instance

序列化字段是原始数据和 python 数据类型之间转换的桥梁,同时还负责对输入的值进行校验,以及从父级对象查询和设置相关的值。

(1)序列化字段的核心参数

序列化字段共同接收一些核心参数:
1,read_only:是否需要设置该字段为只读字段。

  • 设置为 True 时该字段只会出现在序列化结果中,即只读;而创建或者更新的反序列化操作将忽略该字段的输入。默认是 False。

2,Write_only:是否需要设置该字段为只写字段。

  • 设置为 True 时该字段只会作为创建或更新的反序列化操作中的输入,即只写;而该字段不会出现在序列化结果中。默认是 False。

3,required:是否需要在反序列化期间使用。

  • 如果该字段在反序列化期间不需要使用,则设置为False;默认为True。

4,default:是否设置默认值。

  • 如果设置了,则该字段在没有提供输入值时使用这个默认值;否则不填充默认值。
  • 设置该参数意味着该字段不是必需的,所以同时包含defaultrequired这两个关键字参数时将引发错误。
  • 在部分更新操作期间不应使用该参数,目的是为避免破坏“部分更新时只对传入数据中被提供的字段返回经过验证的值”这一更新要求而产生的各种问题。

5,allow_null:是否将 None 视为该字段的有效值。

  • 如果没有显式设置default参数且此参数设置为 True 时,则序列化输出的 defaultnull,但不意味着反序列化输入的默认值为null。默认为 False

6,source:

7,validators:应用到输入字段的验证器函数的列表。

  • 它要么引发验证错误,要么简单地返回。
  • 验证器函数通常应该引发serializers.ValidationError,但 Django 内置的ValidationError也被支持,以便与 Django 代码库或第三方 Django 包中定义的验证器兼容。

8,error_message:映射错误码到错误消息的字典。

9,label:一个可以用作 HTML 表单字段或其他描述性元素中字段名的短文本字符串。

10,help_text:一个可以用作 HTML 表单字段或其他描述性元素中字段描述的文本字符串。

11,initial:用于预填充 HTML 表单字段值的值。

  • 可以像在 Django Field 中那样传递一个可调用对象给它。
import datetime
from rest_framework import serializers
class ExampleSerializer(serializers.Serializer):
    day = serializers.DateField(initial=datetime.date.today)

12,style:设置渲染器渲染字段时做使用的部件:

# Use <input type="password"> for the input.
password = serializers.CharField(
    style={'input_type': 'password'}
)

# Use a radio input instead of a select input.
color_channel = serializers.ChoiceField(
    choices=['red', 'green', 'blue'],
    style={'base_template': 'radio.html'}
)
(2)简单序列化字段

Rest Framework 提供了一系列一般序列化字段类型用于定义序列化字段。

而对于涉及到对模型中关联关系的字段的序列化工作,Rest Framework 专门提供关系字段来表示模型关系。它们可以应用于 ForeignKeyManyToManyFieldOneToOneField 、反向关系和自定义关系。

由于这些关系的序列化工作很重要但又复杂,故后面单独再说。

2,序列化对象

现在,我们首先需要使用前面声明的序列化器 CommentSerializer 来序列化一个 comment 对象或 comment 对象的集合,完成后就能将模型实例(几乎都是 QuerySet 数据类型)转换为 Python 原生的数据类型:

  • 使用Serializer类与使用Form类非常相似。
  • .data属性能获取序列化后的数据。
>>> serializer = CommentSerializer(comment)		# 获得了序列化器实例
>>> serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}

为了完成序列化过程,我们需要继续将 Python 原生的数据类型转化为复杂数据格式,比如转化为 JSON 格式:

>>> from rest_framework.renderers import JSONRenderer	
>>> json = JSONRenderer().render(serializer.data)	# 使用JSONRenderer渲染器渲染为 JSON 格式
>>> json
# b'{"email":"leila@example.com","content":"foo bar","created":"2016-01-27T15:17:10.375877"}'

整个序列化过程其实就是将模型数据格式输出为复杂数据格式的过程。

3,反序列化对象

反序列化过程其实就是将复杂数据格式输入为 Python 原生的数据类型甚至将数据保存到数据库的过程。

假设我们需要将一个 JSON 格式的数据流(stream)解析为 Python 原生的数据类型:

>>> from django.utils.six import BytesIO
>>> from rest_framework.parsers import JSONParser
>>> stream = BytesIO(json)				# 先获取网络中的二进制数据流
>>> data = JSONParser().parse(stream)	# 再用 JSONParser 解析器进行解析

然后继续将这些原生数据类型转为模型支持的类型并进行验证:

>>> serializer = CommentSerializer(data=data)	# 完成反序列化,获得了序列化器实例
>>> serializer.is_valid()						# 验证
# True
>>> serializer.validated_data					# 获取验证后的数据内容
# {'content': 'foo bar', 'email': 'leila@example.com', 'created': datetime.datetime(2012, 08, 22, 16, 20, 09, 822243)}

4,保存或更新实例

(1)为保存实例做准备

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

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

    def create(self, validated_data):
        """
        将验证后的数据保存到数据库
        :param validated_data: 验证后的数据
        :return: 模型实例
        """
        return Comment(**validated_data)

    def update(self, instance, validated_data):
        """
        部分更新数据
        :param instance: 被更新的实例
        :param validated_data: 验证后的数据
        :return: 被更新的实例
        """
        instance.email = validated_data.get('email', instance.email)
        instance.content = validated_data.get('content', instance.content)
        instance.created = validated_data.get('created', instance.created)
        return instance
(2)保存或更新数据

如果你的序列化器对应 Django 的模型,一般来说就表明你需要将数据到数据库。当反序列化数据完成时,就可以基于验证过的数据调用.save()方法:

>>> comment = serializer.save()

.save()方法会在被调用后,会调用create()update()方法的其中一个来保存或更新数据库数据,具体取决于实例化序列化器类的时候是否传递了现有实例:

# .save() will create a new instance.
>>> serializer = CommentSerializer(data=data)			# 不传入实例

# .save() will update the existing `comment` instance.
>>> serializer = CommentSerializer(comment, data=data)	# 要传入实例

一般这么用而不单独调用.save()方法:

    def create(self, validated_data):
        return Comment.objects.create(**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)
        instance.save()		# 在更新时
        return instance
(3)传递附加内容

有时你会希望你的视图代码能够在保存实例时注入额外的数据,比如当前用户、当前时间或者不是属于请求数据的其他信息。

这可以通过在调用.save()时添加额外的关键字参数来实现:

  • .create().update()被调用时,任何其他关键字参数将被包含在validated_data参数中。
>>> serializer.save(owner=request.user)

在某些情况下,除了要序列化的对象之外,还需要为序列化程序提供额外的上下文,可以在实例化序列化器的时候传递一个context参数来传递任意的附加上下文 (例如如果你使用包含超链接关系的序列化程序,这需要序列化器能够访问当前的请求以便正确生成完全限定的URL):

serializer = AccountSerializer(account, context={'request': request})
serializer.data
# {'id': 6, 'owner': u'denvercoder9', 'created': datetime.datetime(2013, 2, 12, 09, 44, 56, 678870), 'details': 'http://example.com/accounts/6/details'}

这个上下文的字典可以在任何序列化器字段的逻辑中使用(例如.to_representation()方法中可以通过访问self.context属性获取上下文字典)。

(5)直接重写.save()方法

在某些情况下我们科蒙并不需要保存或更新数据,此时还是调用原本调用.create().update().save()方法就没有意义。

此时可以选择直接重写.save()方法:

  • 但验证过的数据仍然保留在实例的.validated_data属性中。
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)

反序列化数据的时候,应始终:

  1. 先调用is_valid()方法;
  2. 再访问已验证的数据或保存对象实例。

5,验证

(1)验证错误

如果发生任何验证错误,.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.']}
(2)字段级别的验证

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

  • 方法应将需要验证的单个字段值作为参数。
  • 方法应该返回一个验证过的数据或者抛出一个serializers.ValidationError异常。
  • 如果在序列化器中该字段带有required=False参数,则字段会在不被包含的时候不执行这个字段的验证。
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 "Django: " + value
(3)对象级别的验证

如果要执行访问多个字段的验证,则应该在序列化器中实现.validate()方法。

  • 方法应将需要验证的多个字段值以字典形式作为参数。
  • 方法应抛出一个 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
(4)使用验证器

除了使用验证方法外,序列化器上的各个字段还可以使用独立的验证器:

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

序列化器类还可以使用应用于一组字段的可重用验证器:

  • 这些验证器要在内部的Meta类中声明。
class EventSerializer(serializers.Serializer):
    name = serializers.CharField()
    room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
    date = serializers.DateField()

    class Meta:
        # 每间屋子每天只能有1个活动。
        validators = UniqueTogetherValidator(	# 使用UniqueTogetherValidator验证器来强制执行模型实例的unqieru=True约束
            queryset=Event.objects.all(),
            fields=['room_number', 'date']
        )

更多验证器详情,请参考官方API:Validators

6,访问初始数据和实例

将初始化对象或者查询集传递给序列化实例时,可以通过.instance访问:

  • 如果没有传递初始化对象,那么.instance为None。

将数据传递给序列化器实例时,未修改的数据可以通过.initial_data获取:

  • 如果没有传递data关键字参数,那么.initial_data属性就不存在。
# .save() will update the existing `comment` instance.
>>> serializer = CommentSerializer(comment, data=data)
>>> serializer.initial_data

7,部分更新

尽管update()方法必须从序列化器接收所有必填字段的值来更新数据,但也可以使用 partial参数来允许部分更新:

# 使用部分数据更新`comment` 
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)

8,处理嵌套对象

前面的实例适用于处理只有简单数据类型的对象,但是有时候我们也需要表示更复杂的对象,其中的某些属性可能不是字符串、日期、整数这样简单的数据类型。

Serializer类本身也是一种字段类型,并且可以用来表示“一个对象类型嵌套在另一个对象中”这种关系:

class UserSerializer(serializers.Serializer):
    email = serializers.EmailField()
    username = serializers.CharField(max_length=100)

class CommentSerializer(serializers.Serializer):
    user = UserSerializer()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

如果嵌套表示可以接收 None 值,则应该将 required=False标志传递给嵌套的序列化器:

class CommentSerializer(serializers.Serializer):
    user = UserSerializer(required=False)  # 可能是匿名用户。
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

如果嵌套的关联字段可以接收一个集合,那么应该将many=True标志传递给嵌套的序列化器:

class CommentSerializer(serializers.Serializer):
    user = UserSerializer(required=False)
    edits = EditItemSerializer(many=True)  # edit'项的嵌套列表
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

9,可写的嵌套

(1)嵌套的验证错误

当处理需要支持反序列化的嵌套时,嵌套对象的任何错误都嵌套在它本身的字段名下:

  • .validated_data 属性依然包括验证后的嵌套的数据。
>>> serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})
>>> serializer.is_valid()
# False
>>> serializer.errors
# {'user': {'email': ['Enter a valid e-mail address.']}, 'created': ['This field is required.']}
(2)为嵌套关系定义 .create() 方法

如果需要支持可写的嵌套表示,则需要编写.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
(3 )为嵌套关系定义 .update() 方法

对于更新,则需要仔细考虑如何处理关联字段的更新,例如,如果关联字段的值是None,或者没有提供,那么会发生下面哪一项?

  • 在数据库中将关联字段设置成NULL。
  • 删除关联的实例。
  • 忽略数据并保留这个实例。
  • 抛出验证错误。

下面是我们之前 UserSerializer 类中update()方法的一个例子:

def update(self, instance, validated_data):
        profile_data = validated_data.pop('profile')
        # 除非应用程序正确执行,
        # 保证这个字段一直被设置,
        # 否则就应该抛出一个需要处理的`DoesNotExist`。
        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
(4)在模型管理类中保存关联实例

在序列化器中保存多个相关实例的另一种方法是编写能创建正确实例的自定义模型管理器类(例如,假设我们想确保 User 实例和 Profile 实例总是成对一起创建,就应该自定义一个类似这样的自定义管理器类):

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()方法以使用新的管理器方法:

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

关于更多这种封装方式的详情,请参考Django文档:模型管理器Django models, encapsulation and data integrity

10,处理多个对象

为了能够序列化一个查询集或者一个对象列表而不是单个对象,应该在实例化序列化器类的时候传一个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'}
# ]

反序列化多个对象默认支持多个对象的创建,但是不支持多个对象的更新。有关如何支持或自定义这些情况的更多信息,请查阅官方API:ListSerializer

12,对关联字段的序列化

对关联字段的序列化与对嵌套对象的序列化完全不同,这是一个重要而又复杂的问题,将在后面单独说,具体请参考官方API:Serializer relations

(二)使用 Serializers 类创建对应于模型的序列化器

回到我们的教程中来。

在 snippet 的目录下创建一个名为 serializers.py 文件:

from rest_framework import serializers
from snippet.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES


class SnippetSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(required=False, allow_blank=True, max_length=100)
    code = serializers.CharField(style={'base_template': 'textarea.html'})
    linenos = serializers.BooleanField(required=False)
    language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
    style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')

    def create(self, validated_data):
        """
        给定验证过的数据创建并返回一个新的 Snippet 实例。
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        根据已验证的数据更新并返回已存在的 Snippet 实例。
        """
        instance.title = validated_data.get('title', instance.title)
        instance.code = validated_data.get('code', instance.code)
        instance.linenos = validated_data.get('linenos', instance.linenos)
        instance.language = validated_data.get('language', instance.language)
        instance.style = validated_data.get('style', instance.style)
        instance.save()
        return instance
  • 序列化器继承 Serializer,定义了获得序列化/反序列化能力的字段及其控制参数。
  • 实现了 create() 和 update() 方法。
  • 实际上也可以通过使用 ModelSerializer 类来简化代码,但是现在我们还是使用需要明确定义的 Serializer 类。

(三)Serializers 序列化器的使用

这里先在 shell 中实现,以了解使用序列化器的大致的过程。

序列化:

>>> from snippet.models import Snippet
>>> from snippet.serializers import SnippetSerializer
>>> from rest_framework.renderers import JSONRenderer
>>> from rest_framework.parsers import JSONParser
>>> snippet = Snippet(code='foo = "bar"\n')		# 实例化模型,获得模型实例
>>> snippet.save()								# 先保存一些数据到数据库中准备使用
>>> snippet = Snippet(code='print "hello, world"\n')
>>> snippet.save()
>>> serializer = SnippetSerializer(snippet)		# 在实例化序列化器的时候传入待序列化的模型实例
>>> serializer.data								# 查看序列化结果
{'id': 2, 'title': '', 'code': 'print "hello, world"\n', 'linenos': False, 'language': 'python', 'style':
 'friendly'}
此时,就完成了序列化的第一步——将模型实例转换为 Python 原生数据类型。


要完成序列化过程,我们还需要将数据渲染成 json 类型来作为响应结果:
>>> content = JSONRenderer().render(serializer.data)
>>> content
b'{"id":2,"title":"","code":"print \\"hello, world\\"\\n","linenos":false,"language":"python","style":"friendly"}'
  1. 用序列化器将模型实例转换为 Python 原生数据类型;
  2. 再用 JSONRenderer() 将 Python 原生数据类型渲染为 JSON 数据类型。

反序列化:

反序列化是类似的。首先需要将一个 JSON 流解析为 Python 原生数据类型:
>>> from django.utils.six import BytesIO
>>> stream = BytesIO(content)
>>> data = JSONParser().parse(stream)
>>> data
{'id': 2, 'title': '', 'code': 'print "hello, world"\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}

再将得到的 Python 原生数据类型转为模型能使用的 OrderedDict 数据类型:
>>> serializer = SnippetSerializer(data=data)
# 应始终先调用 is_valid() 方法:
>>> serializer.is_valid()
True
# 再获取数据或保存数据:
>>> serializer.validated_data
OrderedDict([('title', ''), ('code', 'print "hello, world"'), ('linenos', False), ('language', 'python'),
 ('style', 'friendly')])
>>> serializer.save()
<Snippet: Snippet object (3)>

在snippet_snippet表中能够找到刚刚的数据记录:
在这里插入图片描述
除了可以序列化模型实例外,还能序列化查询集,为此,只需在序列化参数中添加一个 many = True:

>>> serializer = SnippetSerializer(Snippet.objects.all(), many=True)
>>> serializer.data
[OrderedDict([('id', 1), ('title', ''), ('code', 'foo = "bar"\n'), ('linenos', False), ('language', 'pyth
on'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', ''), ('code', 'print "hello, world"\n'),
 ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', '
'), ('code', 'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
]

三,使用 ModelSerializers 类

我们的 SnippetSerializer 类复制了包含在 Snippet 模型中的大量字段信息。如果我们能让代码更简洁一点就好了。

与Django同时提供 Form 类和ModelForm类一样,REST框架同时提供Serializer类和ModelSerializer类——能够自动创建指定模型所对应的序列化器类。

它常规的Serializer类一样,不同的是:

  • 它根据模型自动生成一组字段。
  • 它自动生成序列化器的验证器,比如unique_together验证器。
  • 它默认简单实现了.create()方法和.update()方法。

(一)ModelSerializers 类

1,声明序列化器

声明序列化器最重要的就是指定模型与字段:

模型:
class Account(models.Model):
    id = models.IntegerField()
    account_name = models.CharField(blank=True, max_length=100)
    users = models.ForeignKey(User, on_delete=models.CASCADE)
    created = models.DateTimeField(auto_now_add=True)

序列化器:
class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('id', 'account_name', 'users', 'created')

2,检查 ModelSerializer 实例

既然 ModelSerializers 类能够自动创建指定模型所对应的序列化器类,则具体字段的状态可以使用repr()来展示,以进一步确定自动创建了哪些字段和各自使用了哪些控制参数:

>>> from snippet.serializers import *
>>> serializer = AccountSerializer()
>>> print(repr(serializer))
AccountSerializer():
    id = IntegerField()
    account_name = CharField(max_length=100)
    users = PrimaryKeyRelatedField(queryset=User.objects.all())
    created = DateTimeField(read_only=True)

3,指定要包括的字段

如果希望只在模型序列化器中使用所有模型字段的一部分,则可以使用fieldsexclude选项来执行此操作:

  • List item
  • 官方强烈建议使用fields属性显式的设置要序列化的字段及其顺序,这样就不太可能因为修改了模型而无意中暴露数据。
class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('id', 'account_name', 'users', 'created')
        # 等价于
        # fields = '__all__'
        # 等价于
        # exclude = ()

4,明确指定字段

还可以通过在ModelSerializer子类上声明字段来增加额外的字段或者重写默认的字段:

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','url', 'groups')

5,指定只读字段

如果希望将多个字段指定为只读,既可以显式地为每个字段添加read_only=True属性,也可以使用Meta的read_only_fields选项:

  • 该选项应该是字段名称的列表或元祖。
  • 模型中已经设置editable=False的字段和默认就被设置为只读的AutoField字段都不需要添加到read_only_fields选项中。
class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('id', 'account_name', 'users', 'created')
        read_only_fields = ('account_name',)

6,附加关键字参数

还有一个快捷方式:可以通过使用extra_kwargs选项快捷地在字段上指定任意附加的关键字参数:

  • read_only_fields的情况一样,就不需要在序列化器上显式声明该字段。
  • 选项是一个将具体字段名称当作键值的字典
class CreateUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['email', 'username', 'password']
        extra_kwargs = {'password': {'write_only': True}}

    def create(self, validated_data):
        user = User(
            email=validated_data['email'],
            username=validated_data['username']
        )
        user.set_password(validated_data['password'])
        user.save()
        return user

7,对关联字段的序列化

当序列化模型实例时,可以选择许多不同的方式来表示关联字段关系,ModelSerializer的默认使用相关实例的主键。

也可以使用超链接进行关系的序列化、对完整的嵌套的进行序列化,或使用自定义表示进行序列化。

具体请参考官方API:Serializer relations

处理嵌套对象
默认的ModelSerializer使用主键作为关系,但你也可以使用depth选项轻松地生成嵌套表示:

(二)使用 ModelSerializer 类创建模型的序列化器

打开文件 snippet/serializers.py,然后将 SnippetSerializer类替换为以下内容:

class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'linenos', 'language', 'style')

ModelSerializer类与 ModelForm类的用法差不多:

  1. 指定模型;
  2. 指定需要序列化的字段。

序列化程序一个非常棒的属性就是可以通过打印其结构 (representation) 来检查序列化实例中的所有字段:

>>> from snippet.serializers import SnippetSerializer
>>> serializer = SnippetSerializer()
>>> print(repr(serializer))
SnippetSerializer():
    id = IntegerField(read_only=True)
    title = CharField(allow_blank=True, max_length=100, required=False)
    code = CharField(style={'base_template': 'textarea.html'})
    linenos = BooleanField(required=False)
    language = ChoiceField(choices=[('abap', 'ABAP'), ('abnf', 'ABNF'), ('ada', 'Ada'), ('adl', 'ADL'), (
'agda', 'Agda'), ('aheui', 'Aheui'), ('ahk', 'autohotkey'), ('alloy', 'Alloy'), ('ampl', 'Ampl'), ('antlr
', 'ANTLR'), ('antlr-as', 'ANTLR With ActionScript Target'), ('antlr-cpp', 'ANTLR With CPP Target'), ('an
tlr-csharp', 'ANTLR With C# Target'), ('antlr-java', 'ANTLR With Java Target'), ('antlr-objc', 'ANTLR Wit
h ObjectiveC Target'), ('antlr-perl', 'ANTLR With Perl Target'), ('antlr-python', 'ANTLR With Python Targ
et'), ('antlr-ruby', 'ANTLR With Ruby Target'), ('apacheconf', 'ApacheConf'), ('apl', 'APL'), ('applescri
pt', 'AppleScript'), ('arduino', 'Arduino'), ('as', 'ActionScript'), ('as3', 'ActionScript 3'), ('aspectj
', 'AspectJ'), ('aspx-cs', 'aspx-cs'), ('aspx-vb', 'aspx-vb'), ('asy', 'Asymptote'), ('at', 'AmbientTalk'
), ('augeas', 'Augeas'), ('autoit', 'AutoIt'), ('awk', 'Awk'), ('basemake', 'Base Makefile'), ('bash', 'B
ash'), ('bat', 'Batchfile'), ('bbcbasic', 'BBC Basic'), ('bbcode', 'BBCode'), ('bc', 'BC'), ('befunge', '
Befunge'), ('bib', 'BibTeX'), ('blitzbasic', 'BlitzBasic'), ('blitzmax', 'BlitzMax'), ('bnf', 'BNF'), ('b
oa', 'Boa'), ('boo', 'Boo'), ('boogie', 'Boogie'), ('brainfuck', 'Brainfuck'), ('bst', 'BST'), ('bugs', '
BUGS'), ('c', 'C'), ('c-objdump', 'c-objdump'), ('ca65', 'ca65 assembler'), ('cadl', 'cADL'), ('camkes',
'CAmkES'), ('capdl', 'CapDL'), ('capnp', "Cap'n Proto"), ('cbmbas', 'CBM BASIC V2'), ('ceylon', 'Ceylon')
, ('cfc', 'Coldfusion CFC'), ('cfengine3', 'CFEngine3'), ('cfm', 'Coldfusion HTML'), ('cfs', 'cfstatement
'), ('chai', 'ChaiScript'), ('chapel', 'Chapel'), ('charmci', 'Charmci'), ('cheetah', 'Cheetah'), ('cirru
', 'Cirru'), ('clay', 'Clay'), ('clean', 'Clean'), ('clojure', 'Clojure'), ('clojurescript', 'ClojureScri
pt'), ('cmake', 'CMake'), ('cobol', 'COBOL'), ('cobolfree', 'COBOLFree'), ('coffee-script', 'CoffeeScript
'), ('common-lisp', 'Common Lisp'), ('componentpascal', 'Component Pascal'), ('console', 'Bash Session'),
 ('control', 'Debian Control file'), ('coq', 'Coq'), ('cpp', 'C++'), ('cpp-objdump', 'cpp-objdump'), ('cp
sa', 'CPSA'), ('cr', 'Crystal'), ('crmsh', 'Crmsh'), ('croc', 'Croc'), ('cryptol', 'Cryptol'), ('csharp',
 'C#'), ('csound', 'Csound Orchestra'), ('csound-document', 'Csound Document'), ('csound-score', 'Csound
Score'), ('css', 'CSS'), ('css+django', 'CSS+Django/Jinja'), ('css+erb', 'CSS+Ruby'), ('css+genshitext',
'CSS+Genshi Text'), ('css+lasso', 'CSS+Lasso'), ('css+mako', 'CSS+Mako'), ('css+mozpreproc', 'CSS+mozprep
roc'), ('css+myghty', 'CSS+Myghty'), ('css+php', 'CSS+PHP'), ('css+smarty', 'CSS+Smarty'), ('cucumber', '
Gherkin'), ('cuda', 'CUDA'), ('cypher', 'Cypher'), ('cython', 'Cython'), ('d', 'D'), ('d-objdump', 'd-obj
dump'), ('dart', 'Dart'), ('dasm16', 'DASM16'), ('delphi', 'Delphi'), ('dg', 'dg'), ('diff', 'Diff'), ('d
jango', 'Django/Jinja'), ('docker', 'Docker'), ('doscon', 'MSDOS Session'), ('dpatch', 'Darcs Patch'), ('
dtd', 'DTD'), ('duel', 'Duel'), ('dylan', 'Dylan'), ('dylan-console', 'Dylan session'), ('dylan-lid', 'Dy
lanLID'), ('earl-grey', 'Earl Grey'), ('easytrieve', 'Easytrieve'), ('ebnf', 'EBNF'), ('ec', 'eC'), ('ecl
', 'ECL'), ('eiffel', 'Eiffel'), ('elixir', 'Elixir'), ('elm', 'Elm'), ('emacs', 'EmacsLisp'), ('email',
'E-mail'), ('erb', 'ERB'), ('erl', 'Erlang erl session'), ('erlang', 'Erlang'), ('evoque', 'Evoque'), ('e
xtempore', 'xtlang'), ('ezhil', 'Ezhil'), ('factor', 'Factor'), ('fan', 'Fantom'), ('fancy', 'Fancy'), ('
felix', 'Felix'), ('fennel', 'Fennel'), ('fish', 'Fish'), ('flatline', 'Flatline'), ('floscript', 'FloScr
ipt'), ('forth', 'Forth'), ('fortran', 'Fortran'), ('fortranfixed', 'FortranFixed'), ('foxpro', 'FoxPro')
, ('freefem', 'Freefem'), ('fsharp', 'F#'), ('gap', 'GAP'), ('gas', 'GAS'), ('genshi', 'Genshi'), ('gensh
itext', 'Genshi Text'), ('glsl', 'GLSL'), ('gnuplot', 'Gnuplot'), ('go', 'Go'), ('golo', 'Golo'), ('goodd
ata-cl', 'GoodData-CL'), ('gosu', 'Gosu'), ('groff', 'Groff'), ('groovy', 'Groovy'), ('gst', 'Gosu Templa
te'), ('haml', 'Haml'), ('handlebars', 'Handlebars'), ('haskell', 'Haskell'), ('haxeml', 'Hxml'), ('hexdu
mp', 'Hexdump'), ('hlsl', 'HLSL'), ('hsail', 'HSAIL'), ('hspec', 'Hspec'), ('html', 'HTML'), ('html+cheet
ah', 'HTML+Cheetah'), ('html+django', 'HTML+Django/Jinja'), ('html+evoque', 'HTML+Evoque'), ('html+genshi
', 'HTML+Genshi'), ('html+handlebars', 'HTML+Handlebars'), ('html+lasso', 'HTML+Lasso'), ('html+mako', 'H
TML+Mako'), ('html+myghty', 'HTML+Myghty'), ('html+ng2', 'HTML + Angular2'), ('html+php', 'HTML+PHP'), ('
html+smarty', 'HTML+Smarty'), ('html+twig', 'HTML+Twig'), ('html+velocity', 'HTML+Velocity'), ('http', 'H
TTP'), ('hx', 'Haxe'), ('hybris', 'Hybris'), ('hylang', 'Hy'), ('i6t', 'Inform 6 template'), ('icon', 'Ic
on'), ('idl', 'IDL'), ('idris', 'Idris'), ('iex', 'Elixir iex session'), ('igor', 'Igor'), ('inform6', 'I
nform 6'), ('inform7', 'Inform 7'), ('ini', 'INI'), ('io', 'Io'), ('ioke', 'Ioke'), ('irc', 'IRC logs'),
('isabelle', 'Isabelle'), ('j', 'J'), ('jags', 'JAGS'), ('jasmin', 'Jasmin'), ('java', 'Java'), ('javascr
ipt+mozpreproc', 'Javascript+mozpreproc'), ('jcl', 'JCL'), ('jlcon', 'Julia console'), ('js', 'JavaScript
'), ('js+cheetah', 'JavaScript+Cheetah'), ('js+django', 'JavaScript+Django/Jinja'), ('js+erb', 'JavaScrip
t+Ruby'), ('js+genshitext', 'JavaScript+Genshi Text'), ('js+lasso', 'JavaScript+Lasso'), ('js+mako', 'Jav
aScript+Mako'), ('js+myghty', 'JavaScript+Myghty'), ('js+php', 'JavaScript+PHP'), ('js+smarty', 'JavaScri
pt+Smarty'), ('jsgf', 'JSGF'), ('json', 'JSON'), ('json-object', 'JSONBareObject'), ('jsonld', 'JSON-LD')
, ('jsp', 'Java Server Page'), ('julia', 'Julia'), ('juttle', 'Juttle'), ('kal', 'Kal'), ('kconfig', 'Kco
nfig'), ('kmsg', 'Kernel log'), ('koka', 'Koka'), ('kotlin', 'Kotlin'), ('lagda', 'Literate Agda'), ('las
so', 'Lasso'), ('lcry', 'Literate Cryptol'), ('lean', 'Lean'), ('less', 'LessCss'), ('lhs', 'Literate Has
kell'), ('lidr', 'Literate Idris'), ('lighty', 'Lighttpd configuration file'), ('limbo', 'Limbo'), ('liqu
id', 'liquid'), ('live-script', 'LiveScript'), ('llvm', 'LLVM'), ('llvm-mir', 'LLVM-MIR'), ('llvm-mir-bod
y', 'LLVM-MIR Body'), ('logos', 'Logos'), ('logtalk', 'Logtalk'), ('lsl', 'LSL'), ('lua', 'Lua'), ('make'
, 'Makefile'), ('mako', 'Mako'), ('maql', 'MAQL'), ('mask', 'Mask'), ('mason', 'Mason'), ('mathematica',
'Mathematica'), ('matlab', 'Matlab'), ('matlabsession', 'Matlab session'), ('md', 'markdown'), ('mime', '
MIME'), ('minid', 'MiniD'), ('modelica', 'Modelica'), ('modula2', 'Modula-2'), ('monkey', 'Monkey'), ('mo
nte', 'Monte'), ('moocode', 'MOOCode'), ('moon', 'MoonScript'), ('mosel', 'Mosel'), ('mozhashpreproc', 'm
ozhashpreproc'), ('mozpercentpreproc', 'mozpercentpreproc'), ('mql', 'MQL'), ('ms', 'MiniScript'), ('mscg
en', 'Mscgen'), ('mupad', 'MuPAD'), ('mxml', 'MXML'), ('myghty', 'Myghty'), ('mysql', 'MySQL'), ('nasm',
'NASM'), ('ncl', 'NCL'), ('nemerle', 'Nemerle'), ('nesc', 'nesC'), ('newlisp', 'NewLisp'), ('newspeak', '
Newspeak'), ('ng2', 'Angular2'), ('nginx', 'Nginx configuration file'), ('nim', 'Nimrod'), ('nit', 'Nit')
, ('nixos', 'Nix'), ('notmuch', 'Notmuch'), ('nsis', 'NSIS'), ('numpy', 'NumPy'), ('nusmv', 'NuSMV'), ('o
bjdump', 'objdump'), ('objdump-nasm', 'objdump-nasm'), ('objective-c', 'Objective-C'), ('objective-c++',
'Objective-C++'), ('objective-j', 'Objective-J'), ('ocaml', 'OCaml'), ('octave', 'Octave'), ('odin', 'ODI
N'), ('ooc', 'Ooc'), ('opa', 'Opa'), ('openedge', 'OpenEdge ABL'), ('pacmanconf', 'PacmanConf'), ('pan',
'Pan'), ('parasail', 'ParaSail'), ('pawn', 'Pawn'), ('peg', 'PEG'), ('perl', 'Perl'), ('perl6', 'Perl6'),
 ('php', 'PHP'), ('pig', 'Pig'), ('pike', 'Pike'), ('pkgconfig', 'PkgConfig'), ('plpgsql', 'PL/pgSQL'), (
'pony', 'Pony'), ('postgresql', 'PostgreSQL SQL dialect'), ('postscript', 'PostScript'), ('pot', 'Gettext
 Catalog'), ('pov', 'POVRay'), ('powershell', 'PowerShell'), ('praat', 'Praat'), ('prolog', 'Prolog'), ('
properties', 'Properties'), ('protobuf', 'Protocol Buffer'), ('ps1con', 'PowerShell Session'), ('psql', '
PostgreSQL console (psql)'), ('pug', 'Pug'), ('puppet', 'Puppet'), ('py2tb', 'Python 2.x Traceback'), ('p
ycon', 'Python console session'), ('pypylog', 'PyPy Log'), ('pytb', 'Python Traceback'), ('python', 'Pyth
on'), ('python2', 'Python 2.x'), ('qbasic', 'QBasic'), ('qml', 'QML'), ('qvto', 'QVTO'), ('racket', 'Rack
et'), ('ragel', 'Ragel'), ('ragel-c', 'Ragel in C Host'), ('ragel-cpp', 'Ragel in CPP Host'), ('ragel-d',
 'Ragel in D Host'), ('ragel-em', 'Embedded Ragel'), ('ragel-java', 'Ragel in Java Host'), ('ragel-objc',
 'Ragel in Objective C Host'), ('ragel-ruby', 'Ragel in Ruby Host'), ('raw', 'Raw token data'), ('rb', 'R
uby'), ('rbcon', 'Ruby irb session'), ('rconsole', 'RConsole'), ('rd', 'Rd'), ('reason', 'ReasonML'), ('r
ebol', 'REBOL'), ('red', 'Red'), ('redcode', 'Redcode'), ('registry', 'reg'), ('resource', 'ResourceBundl
e'), ('rexx', 'Rexx'), ('rhtml', 'RHTML'), ('ride', 'Ride'), ('rnc', 'Relax-NG Compact'), ('roboconf-grap
h', 'Roboconf Graph'), ('roboconf-instances', 'Roboconf Instances'), ('robotframework', 'RobotFramework')
, ('rql', 'RQL'), ('rsl', 'RSL'), ('rst', 'reStructuredText'), ('rts', 'TrafficScript'), ('rust', 'Rust')
, ('sarl', 'SARL'), ('sas', 'SAS'), ('sass', 'Sass'), ('sc', 'SuperCollider'), ('scala', 'Scala'), ('scam
l', 'Scaml'), ('scdoc', 'scdoc'), ('scheme', 'Scheme'), ('scilab', 'Scilab'), ('scss', 'SCSS'), ('sgf', '
SmartGameFormat'), ('shen', 'Shen'), ('shexc', 'ShExC'), ('sieve', 'Sieve'), ('silver', 'Silver'), ('slas
h', 'Slash'), ('slim', 'Slim'), ('slurm', 'Slurm'), ('smali', 'Smali'), ('smalltalk', 'Smalltalk'), ('sma
rty', 'Smarty'), ('sml', 'Standard ML'), ('snobol', 'Snobol'), ('snowball', 'Snowball'), ('solidity', 'So
lidity'), ('sourceslist', 'Debian Sourcelist'), ('sp', 'SourcePawn'), ('sparql', 'SPARQL'), ('spec', 'RPM
Spec'), ('splus', 'S'), ('sql', 'SQL'), ('sqlite3', 'sqlite3con'), ('squidconf', 'SquidConf'), ('ssp', 'S
calate Server Page'), ('stan', 'Stan'), ('stata', 'Stata'), ('swift', 'Swift'), ('swig', 'SWIG'), ('syste
mverilog', 'systemverilog'), ('tads3', 'TADS 3'), ('tap', 'TAP'), ('tasm', 'TASM'), ('tcl', 'Tcl'), ('tcs
h', 'Tcsh'), ('tcshcon', 'Tcsh Session'), ('tea', 'Tea'), ('termcap', 'Termcap'), ('terminfo', 'Terminfo'
), ('terraform', 'Terraform'), ('tex', 'TeX'), ('text', 'Text only'), ('thrift', 'Thrift'), ('todotxt', '
Todotxt'), ('toml', 'TOML'), ('trac-wiki', 'MoinMoin/Trac Wiki markup'), ('treetop', 'Treetop'), ('ts', '
TypeScript'), ('tsql', 'Transact-SQL'), ('ttl', 'Tera Term macro'), ('turtle', 'Turtle'), ('twig', 'Twig'
), ('typoscript', 'TypoScript'), ('typoscriptcssdata', 'TypoScriptCssData'), ('typoscripthtmldata', 'Typo
ScriptHtmlData'), ('ucode', 'ucode'), ('unicon', 'Unicon'), ('urbiscript', 'UrbiScript'), ('usd', 'USD'),
 ('vala', 'Vala'), ('vb.net', 'VB.net'), ('vbscript', 'VBScript'), ('vcl', 'VCL'), ('vclsnippets', 'VCLSn
ippets'), ('vctreestatus', 'VCTreeStatus'), ('velocity', 'Velocity'), ('verilog', 'verilog'), ('vgl', 'VG
L'), ('vhdl', 'vhdl'), ('vim', 'VimL'), ('wdiff', 'WDiff'), ('webidl', 'Web IDL'), ('whiley', 'Whiley'),
('x10', 'X10'), ('xml', 'XML'), ('xml+cheetah', 'XML+Cheetah'), ('xml+django', 'XML+Django/Jinja'), ('xml
+erb', 'XML+Ruby'), ('xml+evoque', 'XML+Evoque'), ('xml+lasso', 'XML+Lasso'), ('xml+mako', 'XML+Mako'), (
'xml+myghty', 'XML+Myghty'), ('xml+php', 'XML+PHP'), ('xml+smarty', 'XML+Smarty'), ('xml+velocity', 'XML+
Velocity'), ('xorg.conf', 'Xorg'), ('xquery', 'XQuery'), ('xslt', 'XSLT'), ('xtend', 'Xtend'), ('xul+mozp
reproc', 'XUL+mozpreproc'), ('yaml', 'YAML'), ('yaml+jinja', 'YAML+Jinja'), ('zeek', 'Zeek'), ('zephir',
'Zephir'), ('zig', 'Zig')], default='python')
    style = ChoiceField(choices=[('abap', 'abap'), ('algol', 'algol'), ('algol_nu', 'algol_nu'), ('arduin
o', 'arduino'), ('autumn', 'autumn'), ('borland', 'borland'), ('bw', 'bw'), ('colorful', 'colorful'), ('d
efault', 'default'), ('emacs', 'emacs'), ('friendly', 'friendly'), ('fruity', 'fruity'), ('igor', 'igor')
, ('inkpot', 'inkpot'), ('lovelace', 'lovelace'), ('manni', 'manni'), ('monokai', 'monokai'), ('murphy',
'murphy'), ('native', 'native'), ('paraiso-dark', 'paraiso-dark'), ('paraiso-light', 'paraiso-light'), ('
pastie', 'pastie'), ('perldoc', 'perldoc'), ('rainbow_dash', 'rainbow_dash'), ('rrt', 'rrt'), ('sas', 'sa
s'), ('solarized-dark', 'solarized-dark'), ('solarized-light', 'solarized-light'), ('stata', 'stata'), ('
stata-dark', 'stata-dark'), ('stata-light', 'stata-light'), ('tango', 'tango'), ('trac', 'trac'), ('vim',
 'vim'), ('vs', 'vs'), ('xcode', 'xcode')], default='friendly')

Serializer 类相比,ModelSerializer 类并不会做任何特别神奇的事情,它只是创建序列化器的快捷方式:

  • 自动确定一组字段(不用重复去定义类属性)。
  • 默认简单实现的 create()update() 方法。

(三)编写视图

经过前面在 shell 中的大致实现过程,这里就具体看看如何在简单的视图中实现使用序列化器的大概过程。

编辑 snippets/views.py 文件,并且添加以下内容。

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from snippet.models import Snippet
from snippet.serializers import SnippetSerializer


# Create your views here.
class JSONResponse(HttpResponse):
    """
    An HttpResponse that renders its content into JSON.
    """

    def __init__(self, data, **kwargs):
        content = JSONRenderer().render(data)
        kwargs['content_type'] = 'application/json'
        super(JSONResponse, self).__init__(content, **kwargs)


@csrf_exempt
def snippet_list(request):
    """
    列出所有的code snippet,或创建一个新的snippet。
    """
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return JSONResponse(serializer.data)

    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data, status=201)
        return JSONResponse(serializer.errors, status=400)


@csrf_exempt
def snippet_detail(request, pk):
    """
    获取,更新或删除一个 code snippet。
    """
    try:
        snippet = Snippet.objects.get(pk=pk)
    except Snippet.DoesNotExist:
        return HttpResponse(status=404)

    if request.method == 'GET':
        serializer = SnippetSerializer(snippet)
        return JSONResponse(serializer.data)

    elif request.method == 'PUT':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(snippet, data=data)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data)
        return JSONResponse(serializer.errors, status=400)

    elif request.method == 'DELETE':
        snippet.delete()
        return HttpResponse(status=204)

(四)编写路由

snippet/urls.py 文件:
from django.conf.urls import url
from snippet import views

urlpatterns = [
    url(r'^snippets/$', views.snippet_list),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.snippet_detail),
]


tutorial/urls.py 文件:
from django.conf.urls import url, include

urlpatterns = [
    url(r'^', include('snippet.urls')),
]

(五)运行

启动开发服务器,访问 http://127.0.0.1:8000/snippets/
在这里插入图片描述
更友好的结果:

[
	{
		"id":1,
		"title":"",
		"code":"foo = \"bar\"\n",
		"linenos":false,
		"language":"python",
		"style":"friendly"
	},
	{
		"id":2,
		"title":"",
		"code":"print \"hello, world\"\n",
		"linenos":false,
		"language":"python",
		"style":"friendly"
	},
	{
		"id":3,
		"title":"",
		"code":"print \"hello, world\"",
		"linenos":false,
		"language":"python",
		"style":"friendly"
	}
]

访问http://127.0.0.1:8000/snippets/2/
在这里插入图片描述

四,其他的序列化类

HyperlinkedModelSerializer也是一个常用的序列化类,同样子类化它来用 URL 的形式序列化模型关系。

ListSerializer一次性提供了序列化和验证多个对象的行为。通常不需要直接使用ListSerializer,因为
当一个序列化器被实例化并且传递了many=True时,就睡创建一个ListSerializer实例,此时这个序列化器类已经就是ListSerializer的子类。

BaseSerialAlgisher更是不会被直接用到,它就是提供支持序列化和反序列化功能的基序列化器类。

这些序列化器类存在下面这样的直接继承关系:
在这里插入图片描述

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值