【问题标题】:DRF: router for ViewSet with a foreign-key lookup_fieldDRF:带有外键查找字段的 ViewSet 路由器
【发布时间】:2021-10-25 18:49:28
【问题描述】:

使用 Django REST Framework 3.12.4,如果 ViewSet 具有外键查找字段,我无法正确获取 ViewSet 的 URL。

我在models.py中有以下内容:

class Domain(models.Model):
    name = models.CharField(max_length=191, unique=True)


class Token(rest_framework.authtoken.models.Token):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    domain_policies = models.ManyToManyField(Domain, through='TokenDomainPolicy')


class TokenDomainPolicy(models.Model):
    token = models.ForeignKey(Token, on_delete=models.CASCADE)
    domain = models.ForeignKey(Domain, on_delete=models.CASCADE)

    class Meta:
        constraints = [models.UniqueConstraint(fields=['token', 'domain'], name='unique_entry')]

views.py,我有:

class TokenDomainPolicyViewSet(viewsets.ModelViewSet):
    lookup_field = 'domain__name'
    serializer_class = serializers.TokenDomainPolicySerializer

    def get_queryset(self):
        return models.TokenDomainPolicy.objects.filter(token_id=self.kwargs['id'], token__user=self.request.user)

您可以从TokenDomainPolicyViewSet.lookup_field 看到,我希望能够使用相关的Domainname 字段而不是其主键来查询-detail 端点。 (name 对于给定的令牌是唯一的。)

我认为这可以通过 lookup_field = 'domain__name' 完成。

但是,它并不完全有效。这是我的urls.py

tokens_router = SimpleRouter()
tokens_router.register(r'', views.TokenViewSet, basename='token')

tokendomainpolicies_router = SimpleRouter()
tokendomainpolicies_router.register(r'', views.TokenDomainPolicyViewSet, basename='token_domain_policies')

auth_urls = [
    path('tokens/', include(tokens_router.urls)),  # for completeness only; problem persists if removed
    path('tokens/<id>/domain_policies/', include(tokendomainpolicies_router.urls)),
]

urlpatterns = [
    path('auth/', include(auth_urls)),
]

列表端点工作正常(例如/auth/tokens/6f82e9bc-46b8-4719-b99f-2dc0da062a02/domain_policies/);它返回一个序列化的TokenDomainPolicy 对象列表。

但是,假设有一个Domain 对象与name = 'test.net' 与此Token 相关。我想我可以 GET /auth/tokens/6f82e9bc-46b8-4719-b99f-2dc0da062a02/domain_policies/test.net/ 来检索这个对象,但结果是 404。

补充意见:

  • 如果我设置lookup_field = 'domain',它几乎确实工作。但是,这会导致 URL 包含 Domain 的 ID(如 .../25/),这不是我想要的。但基于此,我得出结论,-detail 端点原则上确实会被路由。

  • 如果我添加一个明确的覆盖,它确实工作

      path('tokens/<id>/domain_policies/<domain__name>/', views.TokenDomainPolicyViewSet.as_view(
          {'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'}
      ), name='token_domain_policies-detail'),
    

    但是,为什么必须像这样显式绑定方法? (如果lookup_field 没有指定外键查找,则没有必要!)

  • 最奇怪的是,如果我安装 django_extensions 并运行 manage.py show_urls-detail 端点 URL 会显示 正确 &lt;domain__name&gt; URL kwarg,即使 没有 上一个项目符号的覆盖。如果我添加覆盖,输出中的相应行将显示为两次相同的重复

    已知 URL 集如何在有或没有覆盖的情况下保持不变,但在一种情况下端点按预期工作,而在另一种情况下响应是 404?

我错过了什么?

【问题讨论】:

    标签: python django django-rest-framework django-rest-viewsets


    【解决方案1】:

    根据docs,默认匹配查找将忽略斜杠和句点字符,这就是找不到test.name的原因:

    路由器将匹配包含除斜杠和句点字符之外的任何字符的查找值。

    您也可以在source 中找到它:

    lookup_value = getattr(viewset, 'lookup_value_regex', '[^/.]+')
    

    所以要修复,只需更改 lookup_value_regex 以允许视图集查找中的句点:

    class TokenDomainPolicyViewSet(viewsets.ModelViewSet):
        lookup_field = 'domain__name'
        lookup_value_regex = '[^/]+'
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-03
      • 2018-03-12
      相关资源
      最近更新 更多