django - 如何获取自定义超链接字段以包含额外的 url 变量并适用于所有 ModelViewSet?

标签 django api django-rest-framework hypermedia

我在 API url 的基础上使用了一个变量,与 the docs 中的设置相同对于 Django REST 框架:

/api/<brand>/states/<state_pk>/

基本品牌 slug 之后的所有内容都是标准 API 格式,因此我使用 ModelViewSets 为我的对象生成所有列表和详细 View 。 API 中的所有内容均按品牌过滤,因此此设置是有意义的。

简化的项目/urls.py

urlpatterns = patterns(
    '',
    url(r'^v2/(?P<brand_slug>\w+)/', include(router.urls, namespace='v2')),
)

简化的 api/urls.py

router = routers.DefaultRouter()
router.register(r'states', StateViewSet)
router.register(r'cities', CityViewSet)

我还需要所有模型的超媒体链接,这就是我遇到问题的地方。 REST 框架不知道如何获取这个品牌变量并使用它来生成正确的链接。尝试按照文档解决这个问题给我带来了两个挫折:

  1. 虽然文档解释了如何覆盖 HyperlinkRelatedField 类,但他们从未说明将该类放在哪里,以便它可以与我的序列化程序一起使用。
  2. 没有提及如何实际将品牌变量从 URL 获取到 HyperlinkRelatedField 类中。

这里缺少哪些元素?

最佳答案

所以,我明白了。

将 URL 变量获取到序列化器

为此,您需要覆盖 ModelViewSet 的 get_serializer_context() 方法,并从 kwargs 中发送变量

class BrandedViewSet(viewsets.ModelViewSet):
    def get_serializer_context(self):
        context = super().get_serializer_context()
        context['brand_slug'] = self.kwargs.get('brand_slug')
        return context

然后,您可以使用该类扩展所有 ModelViewSet:

class StateViewSet(BrandedViewSet):
    queryset = State.objects.all()
    serializer_class = StateSerializer

好的一点是,即使您已经使用此变量注入(inject)了序列化器,也可以通过 self.context 从 HyperlinkedRelatedField 类访问它,这就是下一部分的可能性。

使用额外的 URL 变量构建自定义超媒体链接

文档在覆盖 get_url() 方面是正确的:

class BrandedHyperlinkMixin(object):
    def get_url(self, obj, view_name, request, format):
        """ Extract brand from url
        """
        if hasattr(obj, 'pk') and obj.pk is None:
            return None

        lookup_value = getattr(obj, self.lookup_field)
        kwargs = {self.lookup_url_kwarg: lookup_value}
        kwargs['brand_slug'] = self.context['brand_slug']
        return reverse(
            view_name, kwargs=kwargs, request=request, format=format)

但是,您会注意到我正在从第 1 部分中设置的上下文中获取变量。我无法按照文档的建议从对象中获取上下文,而且这种方法更简单。

它是一个 mixin 的原因是因为我们需要扩展两个类才能使其在所有 url 超链接上工作,而不仅仅是相关字段超链接。

class BrandedHyperlinkedIdentityField(BrandedHyperlinkMixin,
                                      serializers.HyperlinkedIdentityField):
    pass


class BrandedHyperlinkedRelatedField(BrandedHyperlinkMixin,
                                     serializers.HyperlinkedRelatedField):
    pass


class BrandedSerializer(serializers.HyperlinkedModelSerializer):
    serializer_related_field = BrandedHyperlinkedRelatedField
    serializer_url_field = BrandedHyperlinkedIdentityField

现在我们可以安全地扩展我们的序列化器并且超链接显示品牌变量!

class StateSerializer(BrandedSerializer):
    class Meta:
        model = State
        fields = ('url', 'slug', 'name', 'abbrev', )

关于django - 如何获取自定义超链接字段以包含额外的 url 变量并适用于所有 ModelViewSet?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33244719/

相关文章:

java - Apache Camel 中的 header 谓词

api - 设置未知属性:yii\web\UrlRule::GET

html - 嵌套时,使用 css 舍入图像边框不起作用

django - 条件 order_by 语句,Django

jquery - 使用 ajax 和 GET 搜索表单

android-TextView 的 setText() 上的 NullPointerException

python - Django Rest Framework 无法修补 View 测试中的 Serializer(many=True).data

python - 减少渲染具有相同内联可编辑字段的项目列表的查询数量

json - Django RestFramework 分组依据

django - 在 django-rest-framework 中序列化通用关系