这是对DRF官方文档:Serializer relations的翻译,根据个人的理解对内容做了些补充、修改和整理。

一,django模型间的关系

在我们对数据进行建模时,最重要的一点就是根据功能需求分析出实体及其关系,

在实现阶段:

  • 一个实体对应一个模型,一个模型就是一张数据表;
  • 实体间的关系由模型中的关系字段进行表示,模型间的关系就是数据表间的关系。

这种设计理念与关系型数据库的设计理念相符。而关系型数据库的强大之就处在于表示与处理各表之间的关联关系。为此,Django 提供了定义三种最常见的数据库关联关系的方法的关系字段:

  • OneToOneField:一对一关系
  • ForeignKey:多对一关系
  • ManyToManyField:多对多关系

二,检查序列化器实例详情

在使用 Serializer 类创建序列化器类时,对关系进行正确的的表示就非常重要。而使用 ModelSerializer 类创建序列化器类时,它会自动使用合适的字段处理模型间的关系,检查这些自动生成的字段对于如何确定自定义关系来说是非常有帮助的。

例如查看一个ModelSerializer 子类的实例详情:

>>> from myapp.serializers import AccountSerializer
>>> serializer = AccountSerializer()
>>> print repr(serializer) # Or `print(repr(serializer))` in Python 3.x.
AccountSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(allow_blank=True, max_length=100, required=False)
owner = PrimaryKeyRelatedField(queryset=User.objects.all())

三,序列化关系字段

为了解释各种类型的关系字段,官网的教程为我们提供了一些简单的模型:用于音乐专辑以及其中的歌曲。

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)

模型中只用到多对一关系,Album 是主模型,产生主表,Track 是从模型,产生从表。

(一)StringRelatedField

字段作用:
主模型序列化器中的StringRelatedField字段可以使用从模型中的__str__方法来表示关联关系。

字段参数:

  • many:如果应用于多对一的关系,应该设置为 True。

使用示例:

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

效果就是 tracks 属性将使用 Track 模型的 __str__ 方法来产生关联关系,并使用它返回的内容。

将产生以下形式的序列化结果:

{
  'album_name': 'Things We Lost In The Fire',
  'artist': 'Low',
  'tracks': [
  '1: Sunflower',
  '2: Whitetail',
  '3: Dinosaur Act',
  ...
  ]
}

(二)PrimaryKeyRelatedField

字段作用:
主模型序列化器中的PrimaryKeyRelatedField字段可以使用从模型中的主键字段来表示关联关系。

每个模型必须有一个主键。要么手动使用primary_key=True设置某字段为主键,否则 django 会为表创建名为 id (别名 pk)的自动增长的字段作为主键。

参考django doc:Automatic primary key fields
字段参数:

  • queryset:验证字段输入时用于模型实例查找的查询集。关系字段必须显式设置 queryset 或者 read_only=True。
  • many:如果应用于多对一的关系,应该设置为 True。
  • allow_null:是否允许该字段接受 None 或为空关系接受空字符串。默认值为 False。
  • pk_field:设置一个字段来控制主键值的序列化/反序列化。

使用示例:

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

默认PrimaryKeyRelatedField字段是可读写的。可以使用read_only标志来更改此行为。
效果就是 tracks 属性将使用 Track 模型的主键即 id (我们没在模型中指定主键)来产生关联关系,并使用主键值。

将产生以下形式的序列化结果:

{
  'album_name': 'Undun',
  'artist': 'The Roots',
  'tracks': [
  89,
  90,
  91,
  ...
  ]
}

(三)HyperlinkedRelatedField

字段作用:
主模型序列化器中的HyperlinkedRelatedField可以用来表示使用超链接的关系目标。从模型中的主键来表示关联关系。

该字段是为映射到一个URL的对象设计的,该URL接受一个 URL 关键字参数,使用lookup_field和lookup_url_kwarg参数设置。这适用于URL中包含一个主键或slug参数的URL。如果需要更复杂的超链接表示,则需要自定义字段,请参考自定义超链接字段。
字段参数:

  • view_name:应该用作关系目标的视图名称。如果使用标准的路由器类,则因该是一个格式为 modelname-detail 的字符串。是必要参数。
  • queryset:验证字段输入时用于模型实例查找的查询集。关系字段必须显式设置 queryset 或者 read_only=True。
  • many:如果应用于多对一的关系,应该设置为 True。
  • allow_null:是否允许该字段接受 None 或为空关系接受空字符串。默认值为 False。
  • lookup_field:应该用于查找的字段。应该对应于被引用视图上的 URL 关键字参数。默认设置是 pk。
  • lookup_url_kwarg:在 URL 配置文件中定义的关键字参数的名称,该参数是应该用于查找字段。默认使用与lookup_field相同的值。
  • format:如果使用格式后缀,超链接字段将为目标使用相同的格式后缀,除非使用format参数重写。

使用示例:

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

默认HyperlinkedRelatedField字段是可读写的。可以使用read_only标志来更改此行为。
效果就是 tracks 属性将使用 Track 模型的主键即 id (我们没在模型中指定主键)来产生关联关系,并使用主键值。

将产生以下形式的序列化结果:

{
  '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

字段作用:
主模型序列化器中的SlugRelatedField字段可以使用从模型中的Slug字段来表示关联关系。可以使用目标上的字段来表示关系的目标。

django向模型提供Slug字段来创建更加完善的 URL。Rest Framework的SlugRelatedField字段也采用相同的理念。
django相关字段解释(slug)、在 Django 中生成 slug、django中slug是什么?有什么用?可以应用于什么地方?

字段参数:

  • slug_field:从模型中用来表示它的字段。这应该是唯一标识任何给定实例的字段。是必要参数。当使用
  • queryset:验证字段输入时用于模型实例查找的查询集。关系字段必须显式设置 queryset 或者 read_only=True。
  • many:如果应用于多对一的关系,应该设置为 True。
  • allow_null:是否允许该字段接受 None 或为空关系接受空字符串。默认值为 False。

使用示例:

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

默认情况下,这个字段是可读写的。可以使用read_only标志来更改此行为。还必须确保模型中该字段设置了unique=True。
效果就是 tracks 属性将使用 Track 模型的 title 字段来产生关联关系,并使用其值。

将产生以下形式的序列化结果:

{
  'album_name': 'Dear John',
  'artist': 'Loney Dear',
  'tracks': [
  'Airport Surroundings',
  'Everything Turns to You',
  'I Was Only Going Out',
  ...
  ]
}

(五)HyperlinkedIdentityField

字段作用:
主模型序列化器中的HyperlinkedIdentityField字段可以作为身份关系应用,例如HyperlinkedModelSerializer上的“url”字段。它也可以用于对象的属性。

字段参数:

  • view_name:应该用作关系目标的视图名称。如果使用标准的路由器类,则因该是一个格式为 modelname-detail 的字符串。是必要参数。
  • lookup_field:应该用于查找的字段。应该对应于被引用视图上的 URL 关键字参数。默认设置是 pk。
  • lookup_url_kwarg:在 URL 配置文件中定义的关键字参数的名称,该参数是应该用于查找字段。默认使用与lookup_field相同的值。
  • format:如果使用格式后缀,超链接字段将为目标使用相同的格式后缀,除非使用format参数重写。

使用示例:

class AlbumSerializer(serializers.HyperlinkedModelSerializer):
  track_listing = serializers.HyperlinkedIdentityField(view_name='track-list')   class Meta:
    model = Album
    fields = ['album_name', 'artist', 'track_listing']

这个字段是只读写的。
效果就是 tracks 属性将使用 Track 模型的主键即 id (我们没在模型中指定主键)来产生关联关系,并使用主键值。

将产生以下形式的序列化结果:

{
  'album_name': 'The Eraser',
  'artist': 'Thom Yorke',
  'track_listing': 'http://www.example.com/api/track_list/12/',
}

四,嵌套关系

与在序列化器中使用序列化关系字段专门序列化模型间关系的方式所不同的是,序列化器本身还能作为序列化字段来表示一种模型间的嵌套关系。例如:

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

五,自定义关系字段

如果希望实现在现有的关系样式都不适合的输出需求时,则可以自定义关系字段:子类化RelatedField,并实现.to_representation(self, value)方法:

此方法需要value参数(通常是一个模型实例),并应该返回所需要的序列化表示形式。

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

如果希望实现可读写的关系字段,则必须实现.to_internal_value(self, data)方法。

如果希望提供基于上下文的动态queryset,则需要重写.get_queryset(self),而不是在类上或初始化字段时指定.queryset。

六,自定义超链接字段

如果希望自定义超链接字段的行为,以表示需要多个查询字段的 URL,则可以通过子类化 HyperlinkedRelatedField 来实现。有两个方法重需要写:

  • 1, get_url(self, obj, view_name, request, format):用于将对象实例映射到其 URL 表示。

如果 view_name 和 lookup_field 属性未配置为正确匹配 URL,可能会引发 NoReverseMatch。

  • 2,get_object(self, queryset, view_name, view_args, view_kwargs):用于支持可写的超链接字段,以便将传入的 URL 映射回它们表示的对象。对于只读的超链接字段,无需重写此方法。

此方法的返回值应该是与匹配的 URL 参数对应的对象,可能会引发 ObjectDoesNotExist 异常
假设我们有一个 customer 对象的 URL,它接受两个关键字参数:

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

这时不能用仅接受单个查找字段的默认实现来表示,就需要重写 HyperlinkedRelatedField 以获得我们想要的行为:

from rest_framework import serializers
from rest_framework.reverse import reverse class CustomerHyperlink(serializers.HyperlinkedRelatedField):
  # 我们将它们定义为类属性,因此我们不需要将它们作为参数传递。
  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)

请注意,如果想将此样式与通用视图一起使用,那么还需要在视图上重写 .get_object 以获得正确的查找行为。

官方建议还是尽可能使用序列化关系字段 API ​​,但适度使用嵌套的 URL 样式也是合理的。

文章知识点与官方知识档案匹配,可进一步学习相关知识
————————————————
版权声明:本文为CSDN博主「dangfulin」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dangfulin/article/details/122799426

Django Rest Framework中文文档:Serializer relations的更多相关文章

  1. Django REST framework 中文文档

    Django REST framework介绍 现在前后端分离的架构设计越来越流行,业界甚至出现了API优先的趋势. 显然API开发已经成为后端程序员的必备技能了,那作为Python程序员特别是把Dj ...

  2. Django 1.10中文文档—第一个Django应用Part1

    在本教程中,我们将引导您完成一个投票应用程序的创建,它包含下面两部分: 一个可以进行投票和查看结果的公开站点: 一个可以进行增删改查的后台admin管理界面: 我们假设你已经安装了Django.您可以 ...

  3. Django 1.10中文文档-第一个应用Part2-模型和管理站点

    本教程继续Part1.我们将设置数据库,创建您的第一个模型,并快速介绍Django的自动生成的管理网站. 数据库设置 现在,编辑mysite/settings.py.它是一个用模块级别变量表示Djan ...

  4. Django 1.10中文文档-执行查询

    Django 1.10中文文档: https://github.com/jhao104/django-chinese-doc 只要创建好 数据模型, Django 会自动为生成一套数据库抽象的API, ...

  5. Django 1.10中文文档-聚合

    Django 数据库抽象API 描述了使用Django 查询来增删查改单个对象的方法. 然而,有时候你要获取的值需要根据一组对象聚合后才能得到. 这个主题指南描述了如何使用Django的查询来生成和返 ...

  6. Django 1.10中文文档-第一个应用Part7-自定义管理站点

    开发第一个Django应用,Part7 本教程上接Part6.将继续完成这个投票应用,本节将着重讲解如果用Django自动生成后台管理网站. 自定义管理表单 通过admin.site.register ...

  7. Django 1.10中文文档-第一个应用Part6-静态文件

    本教程上接Part5 .前面已经建立一个网页投票应用并且测试通过,现在主要讲述如何添加样式表和图片. 除由服务器生成的HTML文件外,网页应用一般还需要提供其它必要的文件——比如图片.JavaScri ...

  8. Django 1.10中文文档-第一个应用Part5-测试

    本教程上接教程Part4. 前面已经建立一个网页投票应用,现在将为它创建一些自动化测试. 自动化测试简介 什么是自动化测试 测试是检查你的代码是否正常运行的行为.测试也分为不同的级别.有些测试可能是用 ...

  9. Django 1.10中文文档-第一个应用Part4-表单和通用视图

    本教程接Part3开始.继续网页投票应用程序,并将重点介绍简单的表单处理和精简代码. 一个简单表单 更新一下在上一个教程中编写的投票详细页面的模板polls/detail.html,让它包含一个HTM ...

  10. Django 1.10中文文档-第一个应用Part3-视图和模板

    本教程上接Django 1.10中文文档-第一个应用Part2-模型和管理站点.我们将继续开发网页投票这个应用,主要讲如何创建一个对用户开放的界面. 概览 视图是Django应用中的一“类”网页,它通 ...

随机推荐

  1. Boost线程处理机制

    采自文章:https://www.cnblogs.com/renyuan/p/6613638.html 大多数共享数据的线程均采用 boost::mutex mtx; boost::condition ...

  2. Spring异步Async和事务Transactional注解

    Spring开发中我们我们常常用到@Transaction和@Async,但这2个注解加在一起很多的开发者不敢用,担心事务不生效.下面我们就仔细讲解一下这2个注解同时运用,文章用3个场景讲述它们之间的 ...

  3. 浅谈Python中的if,可能有你不知道的

    Python中的if,没那么简单,虽然也不难 https://docs.python.org/zh-cn/3.9/reference/compound_stmts.html#if python语言参考 ...

  4. 基于Apache Hudi 构建Serverless实时分析平台

    NerdWallet 的使命是为生活中的所有财务决策提供清晰的信息. 这涵盖了一系列不同的主题:从选择合适的信用卡到管理您的支出,到找到最好的个人贷款,再到为您的抵押贷款再融资. 因此,NerdWal ...

  5. composer 安装 laravel指定版本

    composer 安装 laravel指定版本 $ composer create-project --prefer-dist laravel/laravel notelog '7.*'

  6. 关于vue项目和内嵌iframe页面之间的通信问题

    最近项目中遇到一个与内嵌iframe页面之间通信的问题,起初与原生之间通信很简单,没想到过与vue项目通信的问题,霎时间一脸懵*啊,百度了一下,原来是那么简单,这里记录下以供下次参考 //vue页面 ...

  7. 代码随想录算法训练营day12 | leetcode 239. 滑动窗口最大值 347.前 K 个高频元素

    基础知识 ArrayDeque deque = new ArrayDeque(); /* offerFirst(E e) 在数组前面添加元素,并返回是否添加成功 offerLast(E e) 在数组后 ...

  8. 2021-08-17:cocos creator 如何控制 spine 动画的进度

    // 传入spine与进度比例(注意初始要把spine的TimeScale设置为0)updateSpine(spine: sp.Skeleton,rate: number) { const track ...

  9. USACO2023Feb游记

    由于学校要求,过来打 USACO. 由于上次已经打到白金了,所以继续. 然后还是 AK 了. 感觉题意很迷惑,所以都翻译一下. Hungry Cow Bessie 很饿,每天晚饭如果有干草就会吃 \( ...

  10. 9.增加Marker 标记

    1 <!DOCTYPE html> 2 <html lang="zh"> 3 <head> 4 <meta charset="U ...