在 django 中测试自定义管理操作

Posted

技术标签:

【中文标题】在 django 中测试自定义管理操作【英文标题】:Testing custom admin actions in django 【发布时间】:2015-05-15 14:43:01 【问题描述】:

我是 django 新手,在测试 app_model_changelist 下拉列表中的自定义操作(例如 actions=['mark_as_read'])时遇到问题,它与标准的“已选择删除”下拉列表相同。自定义操作在管理视图中工作,但我只是不知道如何在我的模拟请求中调用它,我知道我需要发布数据但怎么说我希望对我发布的数据执行“mark_as_read”操作?

我想反转 changelist url 并发布查询集,以便“mark_as_read”操作函数将处理我发布的数据。

change_url = urlresolvers.reverse('admin:app_model_changelist')
response = client.post(change_url, <QuerySet>)

【问题讨论】:

我想重复一遍。这个应用程序的 urls.py 看起来如何?换句话说:admin:app_model_changelist 来自哪里? 没关系,我已经在这里找到了:docs.djangoproject.com/en/dev/ref/contrib/admin/… 【参考方案1】:

只需将参数action 与操作名称一起传递即可。

response = client.post(change_url, 'action': 'mark_as_read', ...)

选中的项目作为_selected_action 参数传递。所以代码会是这样的:

fixtures = [MyModel.objects.create(read=False),
            MyModel.objects.create(read=True)]
should_be_untouched = MyModel.objects.create(read=False)

#note the unicode() call below
data = 'action': 'mark_as_read',
        '_selected_action': [unicode(f.pk) for f in fixtures]
response = client.post(change_url, data)

【讨论】:

为了让它更健壮,你可以使用django.contrib.admin.ACTION_CHECKBOX_NAME而不是"_selected_action" 自 2019 年 11 月起仅可从 django.contrib.admin.helpers.ACTION_CHECKBOX_NAME 获得。 如果它不起作用,请确保 change_url 没有查询参数【参考方案2】:

这就是我的工作:

data = 'action': 'mark_as_read', '_selected_action': Node.objects.filter(...).values_list('pk', flat=True)
response = self.client.post(reverse(change_url), data, follow=True)
self.assertContains(response, "blah blah...")
self.assertEqual(Node.objects.filter(field_to_check=..., pk__in=data['_selected_action']).count(), 0)

与接受的答案相比,有几点说明:

我们可以使用values_list而不是列表解析来获取id。 我们需要指定follow=True,因为预计成功的帖子会导致重定向 可选择断言成功消息 检查更改是否确实反映在数据库上。

【讨论】:

如果它不起作用,请确保 change_url 没有查询参数【参考方案3】:

这里是你如何使用登录和一切,一个完整的测试用例:

from django.test import TestCase
from django.urls import reverse

from content_app.models import Content

class ContentModelAdminTests(TestCase):

    def setUp(self):
        # Create some object to perform the action on
        self.content = Content.objects.create(titles='"main": "test tile", "seo": "test seo"')

        # Create auth user for views using api request factory
        self.username = 'content_tester'
        self.password = 'goldenstandard'
        self.user = User.objects.create_superuser(self.username, 'test@example.com', self.password)

    def shortDescription(self):
        return None

    def test_actions1(self):
        """
        Testing export_as_json action
        App is content_app, model is content
        modify as per your app/model
        """
        data = 'action': 'export_as_json',
                '_selected_action': [self.content._id, ]
        change_url = reverse('admin:content_app_content_changelist')
        self.client.login(username=self.username, password=self.password)
        response = self.client.post(change_url, data)
        self.client.logout()

        self.assertEqual(response.status_code, 200)

只需修改以使用您的模型和自定义操作并运行您的测试。

更新:如果您收到 302,您可能需要在 self.client.post() 中使用 follow=True

【讨论】:

这非常非常接近工作。我必须将follow=true 参数添加到self.client.post() 调用中。没有它,我收到了 302 重定向,断言失败。 实际上有效,您的测试可能需要遵循重定向。就我而言,我不必这样做。 确保将testserver 添加到设置文件中的ALLOWED_HOSTS 变量中。测试时调试错误请求见code.djangoproject.com/ticket/27760【参考方案4】:

请注意,即使 POST 成功,您仍然需要测试您的操作是否成功执行了预期的操作。

这是直接从 Admin 类测试操作的另一种方法:

from django.contrib.auth.models import User
from django.contrib.admin.sites import AdminSite
from django.shortcuts import reverse
from django.test import RequestFactory, TestCase
from django.contrib.messages.storage.fallback import FallbackStorage

from myapp.models import MyModel
from myapp.admin import MyModelAdmin


class MyAdminTestCase(TestCase):
    def setUp(self) -> None:
        self.site = AdminSite()
        self.factory = RequestFactory()
        self.superuser = User.objects.create_superuser(username="superuser", is_staff=True)

    def test_admin_action(self):
        ma = MyModelAdmin(MyModel, self.site)
        url = reverse("admin:myapp_mymodel_changelist")
        superuser_request = self.factory.get(url)
        superuser_request.user = self.superuser

        # if using 'messages' in your actions
        setattr(superuser_request, 'session', 'session')
        messages = FallbackStorage(superuser_request)
        setattr(superuser_request, '_messages', messages)

        qs = MyModel.objects.all()        
        ma.mymodeladminaction(superuser_request, queryset=qs)
        
        # check that your action performed the operations intended
        ...

【讨论】:

没有必要单独使用RequestFactory,因为它已经内置在django.test.TestCase 中——它可以通过self.client.get(...) 访问并消除所有样板代码。 请注意,这实际上并不像self.client.get() 那样发出http 请求,而是创建一个请求对象,用于调用modeladmin 的操作。

以上是关于在 django 中测试自定义管理操作的主要内容,如果未能解决你的问题,请参考以下文章

在管理界面上向 Django 内联对象添加自定义操作

Django 自定义管理操作:如何在完成时取消选择?

如何在 Django 中测试自定义模板标签?

如何直接从测试驱动程序调用自定义 Django manage.py 命令?

您可以将参数添加到 Django 自定义管理操作吗?

如何测试自定义 django-admin 命令