python - 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
端点。(对于给定的令牌是唯一的。)Domain
name
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
对象的列表。
但是,假设有一个与 this 相关的Domain
对象。我想我可以 GET来检索这个对象,但结果是 404。name = 'test.net'
Token
/auth/tokens/6f82e9bc-46b8-4719-b99f-2dc0da062a02/domain_policies/test.net/
其他意见:
如果我设置它几乎可以
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 = '[^/]+'
推荐阅读
- onclick - 如何使用 registerDocumentFormattingEditProvider 使用自定义语言在 monaco-editor 中手动缩进代码?
- azure-pipelines - Azure 管道步骤 - 触发 Databricks 作业
- java - Warrior 不是抽象的,不会覆盖 Hero 中的抽象方法 getName()
- java - Android 应用可在模拟器上运行,但不能在设备上运行
- java - 在不知道何时完成所有任务的情况下关闭执行程序
- ruby - 是否需要在这里超时重试?
- javascript - ReactJs:我怎样才能简化这个组件,这样我就不必渲染我的 Modal 和 window 组件两次?
- amazon-web-services - 找不到 LibCrypto(缺少:LibCrypto_LIBRARY LibCrypto_INCLUDE_DIR)
- swift - 快速断开当前 GSM 通话
- python - 为每个样本长度生成随机样本并计算均值问题的标准偏差