如何解决Django Rest Framework更新商户时的NoReverseMatch错误?
看起来你遇到的问题主要集中在路由反向解析错误、lookup字段配置不匹配以及测试认证缺失这几个方面,咱们一步步来解决:
1. 修正路由反向解析的名称
你测试里用了merchant-list来生成更新URL,但DRF的ModelViewSet为单个对象的更新/详情接口生成的路由名称是{basename}-detail,而不是-list。-list对应的是批量查询/创建的接口(/merchants/),而单个对象的PUT请求需要用详情接口(/merchants/<your-id>/)。
所以测试里的URL生成代码要改成:
url = reverse("bige_transactions:merchant-detail", kwargs={"id": self.merchant_geraldine.prefixed_id})
这里的id参数要和你ViewSet里的lookup_field对应,咱们接下来看这个配置的问题。
2. 修正ViewSet的lookup字段配置
你的MerchantViewSet里设置了lookup_field = "id",但从lookup_value_regex的规则(匹配带前缀的merch_xxx格式)来看,你实际是想用模型的prefixed_id字段作为URL里的查找标识,而不是默认的主键id(通常是整数)。
所以需要把lookup_field改成对应的字段名:
class MerchantViewSet(GetPrefixedIDMixin, viewsets.ModelViewSet): # ... 其他配置 ... lookup_field = "prefixed_id" # 改成你模型里存储带前缀ID的字段名 lookup_value_regex = f"{models.Merchant.id_prefix}_[a-f0-9]{32}" # ... 其他代码 ...
这样DRF才能正确匹配URL里的prefixed_id,找到对应的商户对象,避免404错误。
3. 修复测试的认证问题
你测试里的PUT请求返回401,是因为当前请求没有用管理员用户认证,而你的MerchantPermission里要求PUT请求必须是管理员。
在测试的test_edit_merchant方法里,需要先切换到管理员用户,比如用DRF测试客户端的force_authenticate方法:
def test_edit_merchant(self): # noqa url = reverse("bige_transactions:merchant-detail", kwargs={"prefixed_id": self.merchant_geraldine.prefixed_id}) data = {"name": "Edited"} # 用管理员用户认证请求 self.client.force_authenticate(user=self.admin) response = self.put(url, data, status_code=200) # 验证更新是否成功 self.merchant_geraldine.refresh_from_db() self.assertEqual(self.merchant_geraldine.name, "Edited")
注意这里的kwargs参数名要和lookup_field一致,如果改成了prefixed_id,就要用{"prefixed_id": ...}。
4. 验证路由是否正确生成
可以在终端运行python manage.py show_urls(如果安装了django-extensions),或者直接打印router.urls来查看生成的路由是否符合预期,确保详情路由的格式是/merchants/<prefixed_id>/。
额外检查点
- 确认你的
CreateMerchantSerializer支持更新操作(如果是专门用于创建的序列化器,可能需要换成支持更新的,或者在get_serializer_class里根据请求方法切换序列化器)。 - 检查
Merchant模型里确实存在prefixed_id字段,并且值符合lookup_value_regex的规则。
按这些步骤调整后,应该就能正常执行更新操作的测试了。
内容的提问来源于stack exchange,提问作者Subhaac

