DRF: router for ViewSet with a foreign-key lookup_field



使用Django REST框架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显示与正确<domain__name>URL kwarg,即使没有上一颗子弹的覆盖。如果添加覆盖,输出中的对应行将作为相同的副本显示两次。.

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

我错过了什么?

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

相关内容

  • 没有找到相关文章

最新更新