Django REST框架的终极教程——选择性字段和相关对象 (第七部分)

376 阅读3分钟

这是我对Django REST框架教程的最后一部分。

请务必赶上我们在该系列的其他部分所完成的工作。

在这篇文章中,我仔细研究了禁用不必要的字段和扩展相关对象的问题。

我想以两种方式介绍这个话题。第一种将遵循本指南前几节的流程。第二种方法将破坏这种秩序--然后带来新的秩序,但是,我们不要超前了。 选择性字段

对于这个功能,我们需要drf-dynamic-fields库。

$ pip install drf-flex-fields

接下来,我们替换掉当前的序列化器类(也可以使用混合器):

# serializers.py
from rest_flex_fields import FlexFieldsModelSerializer
from rest_framework import serializers

from . import models

class FriendSerializer(FlexFieldsModelSerializer):
   owner = serializers.HiddenField(
       default=serializers.CurrentUserDefault()
   )

   class Meta:
       model = models.Friend
       fields = ('id', 'name', 'owner', 'has_overdue')

从现在开始,我们可以指向我们需要的特定字段:

'http://127.0.0.1:8000/api/v1/friends/?fields=id,name'

或者列出我们想省略的字段:

'http://127.0.0.1:8000/api/v1/friends/?omit=has_overdue'

添加相关对象

添加相关对象的最简单方法是在关系字段中使用正确的序列器:

# serializers.py
class BorrowedSerializer(FlexFieldsModelSerializer):
   what = BelongingSerializer()
   to_who = FriendSerializer()

   class Meta:
       model = models.Borrowed
       fields = ('id', 'what', 'to_who', 'when', 'returned')

但是我们需要记住这里的两件事。

  1. 为了避免给我们的数据库带来负担,让我们用select_related来填充默认的queryset:
# views.py
class BorrowedViewset(NestedViewSetMixin, viewsets.ModelViewSet):
   queryset = models.Borrowed.objects.all().select_related('to_who', 'what')
   # ...

2.以这种方式定义的关系默认只用于读取和添加,保存选项将需要更多工作。然而,我们可以通过使用一个允许在需要时扩展字段的库来避免这一点。

首先,我们指向将用于扩展字段的序列器:

class BorrowedSerializer(FlexFieldsModelSerializer):
   expandable_fields = {
       "what": (BelongingSerializer, {"source": "what"}),
       "to_who": (FriendSerializer, {"source": "to_who"})
   }

   class Meta:
       model = models.Borrowed

接下来,我们需要对视图集进行处理。在这一点上,我们应该改变基类(也可以使用mixin)并添加一个可扩展字段的列表。

class BorrowedViewset(NestedViewSetMixin, FlexFieldsModelViewSet):
   queryset = models.Borrowed.objects.all().select_related('to_who', 'what')
   permit_list_expands = ["what", "to_who"]
   # ...

注意:一定要记住queryset中的select_related。它将大大减少数据库的负担。对于ManyToMany关系,你可以通过使用prefetch_related来获得类似的结果。

我们现在可以扩展我们的字段了:

'http://127.0.0.1:8000/api/v1/borrowings/?expand=what,to_who'

这里有一个不同的方法

最后,我想向你展示另一种方法,它破坏了我们到目前为止建立的秩序。我将使用最完善的DRF扩展之一DREST(Dynamic REST)来谈论它。你可以在我们仓库的一个单独的分支中找到这部分的代码,名为drest,并在part07-drest标签下。

DREST的文档非常全面,所以我将只关注与我们主题相关的最重要的片段。

安装和配置。

$ pip install dynamic-rest
# settings.py
INSTALLED_APPS = [
    # ...
    "dynamic_rest",
]

在安装之后,我们应该添加的第一件事是用所有可用的端点的列表来填充我们的可浏览API:

REST_FRAMEWORK = {
    "DEFAULT_RENDERER_CLASSES": [
        "rest_framework.renderers.JSONRenderer",
        "dynamic_rest.renderers.DynamicBrowsableAPIRenderer",
    ],
}

为了充分利用DREST,我们应该把目前使用的ModelSerializer类换成DynamicModelSerializer。我们也要把ModelViewSet类改成DynamicModelViewSet。

#serializers.py
from rest_framework import serializers
from dynamic_rest.serializers import DynamicModelSerializer
# ...

class FriendSerializer(DynamicModelSerializer):
   # ...

class BelongingSerializer(DynamicModelSerializer):
   # ...

class BorrowedSerializer(DynamicModelSerializer):
   # ...

# views.py 
import django_filters
from django.core.mail import send_mail
from dynamic_rest.viewsets import DynamicModelViewSet
# ...


class FriendViewset(NestedViewSetMixin, DynamicModelViewSet):
   # ...

class BelongingViewset(DynamicModelViewSet):
   # ...

class BorrowedViewset(NestedViewSetMixin, DynamicModelViewSet):
   # ...

不幸的是,在编写这篇文章的时候,这个库与嵌套的路由器不兼容(我最近报道过)。然而,它部分地自动实现了这个功能,允许我们查看ManyToMany关系。为了结构的需要,让我们把负责嵌套的代码去掉。

# api.py
from dynamic_rest.routers import DynamicRouter
from rest_framework_extensions.routers import NestedRouterMixin

from core import views as myapp_views


router = DynamicRouter()
friends = router.register(r"friends", myapp_views.FriendViewset)
router.register(r"belongings", myapp_views.BelongingViewset)
router.register(r"borrowings", myapp_views.BorrowedViewset)

我们现在可以开始查看可用的功能了。

选择性字段

在我们的序列化器中计算特定字段的值可能非常昂贵。这就是为什么我们可能想在某些情况下省略它们,或者只在请求时添加它们。DREST允许实现这两种途径。

删除我们不需要的字段很容易。只需要在我们的查询中加入参数exclude[]和字段名。

'http://127.0.0.1:8000/api/v1/friends/?exclude[]=has_overdue'

值得注意的是,DynamicModelViewSet改变了返回数据的默认格式。这样的结构是符合REST的最佳实践的。

至于根据要求添加的字段,我们可以在deferred_fields参数的帮助下进行标记:

class FriendSerializer(DynamicModelSerializer):
   owner = serializers.HiddenField(default=serializers.CurrentUserDefault())

   class Meta:
       model = models.Friend
       fields = ("id", "name", "owner", "has_overdue")
       deferred_fields = ("has_overdue",)

这就是我们如何将字段添加到响应中。

'http://127.0.0.1:8000/api/v1/friends/?include[]=has_overdue'

另一个关键事项是...

添加相关对象

DREST带有一个方便的功能,可以额外地优化查询执行时间。

我们用DynamicRelationField字段标记的每一个关系都可以被扩展,相关元素的列表将和原始结果一起被返回。

例如,下面的查询将在结果中包括朋友和项目:

'http://127.0.0.1:8000/api/v1/borrowings?include[]=to_who.\*&include[]=what.\*'

是的,我确实意识到,借来的项目的默认名称并不是那么好...

如果我们的应用程序有一个更复杂的结构,我们可以使用这种方法来包括任何层次嵌套的对象。然而,我们需要记住我们给数据库带来的负担,因为每一级的负担都会变大。

这篇文章结束了我关于Django REST框架的系列文章,在这个系列中,我几乎涉及到了REST API工作的每一个方面。