首页 > 解决方案 > DRF:带有外键查找字段的 ViewSet 路由器

问题描述

使用 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看出我希望能够通过使用相关字段而不是其主键来查询-detail端点。(对于给定的令牌是唯一的。)Domainnamename

我认为这可以用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对象的列表。

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

其他意见:

我错过了什么?

标签: pythondjangodjango-rest-frameworkdjango-rest-viewsets

解决方案


根据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 = '[^/]+'

推荐阅读