自定义操作的 Django Rest Framework URL 不起作用

Posted

技术标签:

【中文标题】自定义操作的 Django Rest Framework URL 不起作用【英文标题】:Django Rest Framework URL for custom action not working 【发布时间】:2021-05-31 14:56:39 【问题描述】:

我的视图中有以下自定义操作:

class OrderAPIViewSet(viewsets.ViewSet):

    def create(self, request):
        print("Here: working")

    @action(detail=True, methods=['post'])
    def add(self, request, *arg, **kwargs):
        print("HERE in custom action")
        order = self.get_object()
        print(order)

我的应用程序的urls.py 是:

from rest_framework import routers
from .views import OrderAPIViewSet


router = routers.DefaultRouter()
router.register(r'orders', OrderAPIViewSet, basename='order')

urlpatterns = router.urls

所以在我的测试中,当我尝试访问orders/post 时它可以工作,但是当我尝试访问orders/pk/add 时它失败了。我的意思是,反过来本身就失败了:

ORDERS_LIST_URL = reverse('order-list')
ORDERS_ADD_URL = reverse('order-add')

class PublicOrderApiTests(TestCase):

   def test_sample_test(self):
       data = 
       res = self.client.post(ORDERS_ADD_URL, data, format='json')

正如我之前所说,我有一个单独的测试,我像这样使用ORDERS_LIST_URL

res = self.client.post(ORDERS_LIST_URL, data, format='json')

但是在运行测试时出现以下错误:

ImportError:导入测试模块失败:orders.tests Traceback (最近一次通话最后):文件 “/usr/local/lib/python3.7/unittest/loader.py”,第 436 行,在 _find_test_path 模块 = self._get_module_from_name(name) 文件“/usr/local/lib/python3.7/unittest/loader.py”,第 377 行,在 _get_module_from_name import(名称)文件“/app/orders/tests.py”,第 22 行,在 ORDERS_ADD_URL = reverse('order-add') 文件“/usr/local/lib/python3.7/site-packages/django/urls/base.py”,第 87 行, 相反 return iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs)) 文件 "/usr/local/lib/python3.7/site-packages/django/urls/resolvers.py", 第 685 行,在 _reverse_with_prefix raise NoReverseMatch(msg) django.urls.exceptions.NoReverseMatch:“order-add”的反向,没有找不到参数。 2 个图案 试过:['orders/(?P[^/.]+)/add\.(?P[a-z0-9]+)/?$', '订单/(?P[^/.]+)/add/$']

----------------------------------- ----------------------- 在 0.000 秒内运行 1 次测试

失败(错误=1)

根据文档我不需要注册这个端点,路由器应该自己做。我错过了什么?

【问题讨论】:

在模块级别删除reverse(...)的使用并将其带到您的班级级别 【参考方案1】:

你错过的第一件事是pk 在你的反面。由于添加 API 需要您的 Order 对象的 pk,因此您需要将其传递给 reverse 函数。例如:

order_add_url = reverse('order-add', kwargs='pk': 1)
print(order_add_url)  # which will print '/orders/1/add/'

所以我认为你应该把这部分移到 PublicOrderApiTests 的方法体中,因为你需要每个测试对象的动态 url。

另一个问题是ViewSet类不支持self.get_object(),如果你想使用这个方法你应该有自己的方法或者使用rest框架GenericViewSet(即from rest_framework.viewsets import GenericViewSet并从这个类继承而不是ViewSet) 那么您可以访问get_object() 方法。您还可以在rest framework docs 中阅读有关通用视图的更多信息。

【讨论】:

是的,就是这样,谢谢。我将 URL consts 移动到 PublicOrderApiTests。有没有办法动态填充 id 或者每次我想调用该端点时我需要在执行调用的方法中执行 order_add_url = reverse('order-add', kwargs='pk': 1) ?如果是这样,就不需要定义的 const ORDERS_ADD_URL 对吧? 是的,你是对的......一个简单的方法(动态测试目的)是首先在你的方法中创建一个Order对象(即order_instance = Order.objects.create(required_fields_here),然后使用反向只是创建对象后。例如order_add_url = reverse('order-add', kwargs='pk': order_instance.id) 最后一个问题。我认为这种方法可以用作模板来构造其他 url,但似乎并非如此。我想创建一个删除端点,比如/order/order_id/remove/item_id,所以我为删除创建了一个@action,在我的测试中我定义了url=reverse('order-remove' kwargs='pk':1,'order_id:'1',但它给了我一个错误:django.urls.exceptions.NoReverseMatch: Reverse for 'order-remove' with keyword arguments ''pk': 12345, 'product_id': 1' not found.知道为什么吗?

以上是关于自定义操作的 Django Rest Framework URL 不起作用的主要内容,如果未能解决你的问题,请参考以下文章

如何在 django rest 框架中反转 ViewSet 自定义操作的 URL

用于覆盖 post 方法的 Django REST 框架自定义装饰器

raise PermissionDenied 中的自定义消息在 Django rest 中不起作用

Django REST framework 单元测试

django-rest-auth 自定义注册无法保存额外字段

Django-Rest-Framework 系统检查自定义 HTTP 标头(应用程序 - 令牌)