Django REST Framework API Guide 05

时间:2023-03-09 07:03:28
Django REST Framework API Guide 05

本节大纲

  1、Serializer fields

  2、Serializer relations

Serializer fields

1、serializer 字段定义在fields.py文件内
2、导入from rest_framework import serializers
3、引用serializers.<FieldName>

核心参数

read_only 

# 默认是False,True的时候在序列化显示的时候展示,在反序列化实例进行创建或者更新的时候不被使用。

write_only

# 默认是false,True的时候可以用来更新或者创建实例,在序列化呈现的时候不显示

required

# 默认是True,False的时候可以遗漏此字段,如果不存在,就不会显示

default

# 提供一个默认值

allow_null

# 默认是false

source

# 默认指向字段名;EmailField(source='user.email');URLField(source='get_absolute_url')
# source='*' 有一个特殊的含义,要用来显示整个对象,在嵌套展示的时候很有用

validator

# 验证器,通过返回数据,不通过引发报错,Django内置的serializers.ValidationError

error_messages

# 错误信息的字典

label

# 标签,可以用来当HTML标签的表单字段

help_text

# 帮助提示

initial

# 预填充字段值
import datetime
from rest_framework import serializers
class ExampleSerializer(serializers.Serializer):
day = serializers.DateField(initial=datetime.date.today)

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

主要还是因为相关的字段太多,楼主觉得没必要一次性看好,有需求的时候查找可能会更好一点。

http://www.django-rest-framework.org/api-guide/fields/

Serializer relations

关系字段,顾名思义,用来表示模型之间的关系,可以用ForeignKey, ManyToManyField和OneToOneField来表示关系,客制化关系可以用GenericForeignKey

关系字段是在relations.py文件内申明的,但是你应该从serializers模块导入,使用from rest_framework import serializers然后serializers.<FieldName>

Inspecting relationships

打开pycharm的python console或者在terminal启动python3 manage.py shell输入

from app01.serializers import PersonModelSerializer
serializer = PersonModelSerializer()
print(repr(serializer))
PersonModelSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(max_length=128)
age = IntegerField()
get_gender = ReadOnlyField()
get_job = ReadOnlyField()
modify_time = DateTimeField(read_only=True)

API Reference

为了解释不同类型的关系字段,我们将使用简单的模型作为示例。

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 __unicode__(self):
return '%d: %s' % (self.order, self.title)

StringRelatedField

StringRelatedField可以被用来展示目的关系使用它的__str__方法(这里官网上给的是__unicode__方法,但楼主测试下来并不是)

class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.StringRelatedField(many=True) class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')

将会被序列化成下面的样子

{'album_name': 'reputation', 'artist': 'Taylor Swift', 'tracks': ['reputation: Look What you make me do', 'reputation: Delicate']}

字段只读,如果对应多个关系,需要添加many=True

PrimaryKetRelatedField

PrimaryKeyRelatedField可以使用主键来表示目的关系

class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True) class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')

结果如下

{'album_name': 'reputation', 'artist': 'Taylor Swift', 'tracks': [2, 3]}

默认这个字段可读写,你可以改变这个行为通过使用read_only标志

参数

queryset, many,allow_null,pk_field...(pk_field=UUIDField(format='hex'))

HyperlinkedRelatedField

HyperlinkedRelatedField可以通过超链接来显示目的关系

class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.HyperlinkedRelatedField(
many=True,
read_only=True,
view_name='track-detail'
) class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')

结果

{
'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/',
...
]
}

这边就不细讲这个具体实现了,可以查看serializer章节,通过链接https://www.cnblogs.com/wuzdandz/p/9631032.html 并查找HyperlinkedModelSerializer关键字搜索标题

Django REST Framework API Guide 05

参数

view_name
queryset
many
allow_null
lookup_field
look_url_kwarg
format

SlugRelatedField

SlugRelatedField可以用来用目的对象的一个字段来表示关系。

class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.SlugRelatedField(
many=True,
read_only=True,
slug_field='title'
) class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')

序列化呈现

{'album_name': 'reputation', 'artist': 'Taylor Swift', 'tracks': ['Look What you make me do', 'Delicate']}

同样SlugRelatedField是一个可读写的字段,你可以通过read_only来修改行为。当使用SlugRelatedField作为一个可读写的字段,你通常想确保SlugRelatedField对应model里面unique=True的一个字段。

参数

slug_field,queryset,many,allow_null

HyperlinkedIdentityField

这个应该也不需要说了,有问题看下面的还是上面的链接https://www.cnblogs.com/wuzdandz/p/9631032.html

Nested relationships

嵌套关系可以通过使用serializers作为字段来描述。

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_name': 'reputation',
'artist': 'Taylor Swift',
'tracks': [
OrderedDict([('order', 2), ('title', 'Look What you make me do'), ('duration', 2)]),
OrderedDict([('order', 3), ('title', 'Delicate'), ('duration', 4)])
]
}

Writable nested serializers

默认嵌套的serializers是只读的,如果你想要对嵌套的serializer字段支持写操作,就需要创建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') def create(self, validated_data):
tracks_data = validated_data.pop('tracks')
album = Album.objects.create(**validated_data)
for track_data in tracks_data:
Track.objects.create(album=album, **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>

自定义create里面我们pop出来了tracks的数据然后单独对它进行了创建。

Custom relationnal fields

在少数案例里面没有现存的关系格式满足你想要的表现形式,你可以构造一个完整的客制化的关系字段用来描述从模型实例生产的准确的输出形式。要构建一个客制化的关系字段,你应该重写RelatedField和构建.to_representation(self, value). 这个方法会把目的字段的值作为value参数,并返回此对象序列化的表现。这个value参数通常将会是一个模型(model)实例.

想要基于context提供一个动态的queryset,你也可以重写.get_queryset(self)代替在类上标注.queryset或者当初始化字段时

import time

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)',
...
]
}

Custom hyperlinked fields

在一些案例里面,你可能需要客制化一个hyperlinked字典的行为,为了呈现URLs,可能需要不止一个查找字段。

你可以通过重写HyperlinkedRelatedField获取。有两个你可以重写的方法

get_url(self, obj, view_name, request, format)  # 被用来映射对象实例到url呈现;可以引发NoReverseMatch如果view_name和lookup_field属性没有被正确的配置匹配url设置。
get_object()self, queryset, view_name, view_args, view_kwargs) # 实现可写的hyperlinked field,为了映射将传入的URLs到他们所代表的对象。只读的没必要重写。这个方法的返回值应该是对应匹配的url设置参数的对应的对象。可以引发ObjectDoesNotExist错误

我们有一个客制化对象的url,里面有2个关键字参数

/api/<organization_slug>/customers/<customer_pk>/

这种用默认的执行不能被展示,因为只接受一个单独的查找字段。这个示例里面,我们需要重写HyperlinkedRelatedField来获得我们需要的行为:

from rest_framework import serializers
from rest_framework.reverse import reverse class CustomerHyperlink(serializers.HyperlinkedRelatedField):
# We define these as class attributes, so we don't need to pass them as arguments.
view_name = 'customer-detail'
queryset = Customer.objects.all() def get_url(self, obj, view_name, request, format):
url_kwargs = {
'organization_slug': obj.organization.slug,
'customer_pk': obj.pk
}
return reverse(view_name, kwargs=url_kwargs, request=request, format=format) def get_object(self, view_name, view_args, view_kwargs):
lookup_kwargs = {
'organization__slug': view_kwargs['organization_slug'],
'pk': view_kwargs['customer_pk']
}
return self.get_queryset().get(**lookup_kwargs)

源码如下

     def get_object(self, view_name, view_args, view_kwargs):
"""
Return the object corresponding to a matched URL. Takes the matched URL conf arguments, and should return an
object instance, or raise an `ObjectDoesNotExist` exception.
"""
lookup_value = view_kwargs[self.lookup_url_kwarg]
lookup_kwargs = {self.lookup_field: lookup_value}
return self.get_queryset().get(**lookup_kwargs) def get_url(self, obj, view_name, request, format):
"""
Given an object, return the URL that hyperlinks to the object. May raise a `NoReverseMatch` if the `view_name` and `lookup_field`
attributes are not configured to correctly match the URL conf.
"""
# Unsaved objects will not yet have a valid URL.
if hasattr(obj, 'pk') and obj.pk in (None, ''):
return None lookup_value = getattr(obj, self.lookup_field)
kwargs = {self.lookup_url_kwarg: lookup_value}
return self.reverse(view_name, kwargs=kwargs, request=request, format=format)

上面的列相当于继承HyperlinkedRelatedField,并重写了里面的方法。

Customizing the HTML display

模型的内置__str__方法将用于生成用于填充choices属性的对象的字符串表示。这些choices被用来填充可浏览API的选择HTML输入。

为了对于这些输入提供客制化的表现,可以重写RelatedFIeld子类的display_value()方法。这个方法将会接收一个模型对象,并应该返回一个合适的字符串来展示。

class TrackPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):
def display_value(self, instance):
return 'Track: %s' % (instance.title)

Select field cutoffs

可浏览的API默认只展示最多1000条选中的对象。2个关键字参数可以用来控制这个行为

html_cutoff
设置这个,HTML选择下拉将显示的最大选择数。设置为None所有限制失效。默认1000 html_vutoff_text
如果在HTML选择下拉中截断了最大数量的项目,则将显示文本指示符。默认'More than {count} items...'

你也可以控制这些通过设定全局参数HTML_SELECT_CUTOFF和HTML_SELECT_CUTOFF_TEXT

在强制执行中断的情况下,您可能希望使用HTML表单中的普通输入字段。您可以使用style关键字参数。例如:

assigned_to = serializers.SlugRelatedField(
queryset=User.objects.all(),
slug_field='username',
style={'base_template': 'input.html'}
)

Reverse relations

注意,反转管理没有自动的被包括在ModelSerializer和HyperlinkedModelSerializer类里。为了能实现,你必须清楚的添加它到字段列表里。

class AlbumSerializer(serializers.ModelSerializer):
class Meta:
fields = ('tracks', ...)

你通常想确保已经设置了一个合适的related_name参数在关系上,你可以使用它作为字段名。

class Track(models.Model):
album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)
...

如果没有设置related_name在反转关系上,你需要使用自动生成的关联名称在fields参数内

class AlbumSerializer(serializers.ModelSerializer):
class Meta:
fields = ('track_set', ...)

Generic relationships

如果你先要序列化一个通用的外间,你需要定义一个自定义字段,清楚的决定你想要目的关系怎么序列化

class TaggedItem(models.Model):
"""
Tags arbitrary model instances using a generic relation. See: https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/
"""
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 __unicode__(self):
return self.tag_name

下面2个模型类有关联:

class Bookmark(models.Model):
"""
A bookmark consists of a URL, and 0 or more descriptive tags.
"""
url = models.URLField()
tags = GenericRelation(TaggedItem) class Note(models.Model):
"""
A note consists of some text, and 0 or more descriptive tags.
"""
text = models.CharField(max_length=1000)
tags = GenericRelation(TaggedItem)

我们可以自定义一个字段用来序列化目的示例

class TaggedObjectRelatedField(serializers.RelatedField):
"""
A custom field to use for the `tagged_object` generic relationship.
""" def to_representation(self, value):
"""
Serialize tagged objects to a simple textual representation.
"""
if isinstance(value, Bookmark):
return 'Bookmark: ' + value.url
elif isinstance(value, Note):
return 'Note: ' + value.text
raise Exception('Unexpected type of tagged object')

如果需要目的关系有一个嵌套的表示,你可以使用需要的serializer在.to_representation()方法内

 def to_representation(self, value):
"""
Serialize bookmark instances using a bookmark serializer,
and note instances using a note serializer.
"""
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