python - DRF : router for ViewSet with a foreign-key lookup_field

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

使用 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使用相关的端点 Domainname字段而不是它的主键。 (name 对于给定的 token 是唯一的。)

我认为这可以用 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 .我想我可以得到 /auth/tokens/6f82e9bc-46b8-4719-b99f-2dc0da062a02/domain_policies/test.net/检索此对象,但结果是 404。

补充观察:

  • 如果我设置 lookup_field = 'domain',它几乎确实有效.但是,这会导致包含 Domain 的 URL。的 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'),
    

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

  • 最奇怪的是,如果我安装 django_extensions然后运行 ​​manage.py show_urls , -detail端点 URL 显示为正确 <domain__name> URL kwarg,甚至没有上一个项目符号的覆盖。如果我添加覆盖,输出中的相应行将显示两次,作为相同的副本

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

我错过了什么?

最佳答案

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

The router will match lookup values containing any characters except slashes and period characters.

您可以在 source 中找到它还有:

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

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

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

关于python - DRF : router for ViewSet with a foreign-key lookup_field,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68926149/

相关文章:

python - 使用递归计算字符串中的元音

python - 寻找数字最后一位数字的最有效方法?

mysql - Django - 无法按外键名字过滤

python - 操作错误 : cursor "_django_curs_<id>" does not exist

python - Django Rest Framework 嵌套序列化器

python - 用另一个数据框中的匹配 ID 替换 Pandas 中的单元格值

python - 如何将列表添加到列表列表中

Django:如何在 API View 中模拟类

Django Rest Framework 如何发布日期字段

django - 优化 Django Rest ORM 查询