简体   繁体   English

Django中的异常错误测试错误地认为实例不存在

[英]Unusual error in Django tests wrongly thinking instance doesn't exist

I have very customised Django app that checking to see if a user can change a ForeignKey to certain values. 我有一个非常定制的Django应用程序,该应用程序检查用户是否可以将ForeignKey更改为某些值。

In this instance, a User belongs to a Workgroup and an Item can also belong to a Workgroup , and consequently, when a User makes an Item , they can only put it in Workgroup s they belong to. 在这种情况下, User属于WorkgroupItem也可以属于Workgroup ,因此,当User制作Item ,他们只能将其放入所属的Workgroup中。 Complicating matters Item is a parent class, so there are lots of types of "Item". 使事情复杂化Item是父类,因此“ Item”的类型很多。

At present I have a custom admin form setup to check this: 目前,我有一个自定义管理表单设置来检查:

class AdminConceptForm(autocomplete_light.ModelForm):
    def __init__(self, *args, **kwargs):
        #... other code
        self.fields['workgroup'].queryset = self.request.user.profile.editable_workgroups.all()

The important bits of this test are: 该测试的重要部分是:

def setUp(self):
    from django.test import Client

    self.client = Client()
    self.wg1 = models.Workgroup.objects.create(name="Test WG 1") # Editor is member

def test_editor_change_item(self):
    self.login_editor()
    response = self.client.get(reverse("admin:%s_%s_change"%(self.itemType._meta.app_label,self.itemType._meta.model_name),args=[self.item1.pk]))
    self.assertResponseStatusCodeEqual(response,200)

    updated_item = dict((k,v) for (k,v) in model_to_dict(self.item1).items() if v is not None)
    updated_name = updated_item['name'] + " updated!"
    updated_item['name'] = updated_name

    updated_item.update({
        'statuses-TOTAL_FORMS': 0, 'statuses-INITIAL_FORMS': 0 #no statuses
    })
    updated_item.update(self.form_defaults)
    self.assertTrue(self.wg1 in self.editor.profile.myWorkgroups)

    self.assertEqual([self.wg1],list(response.context['adminform'].form.fields['workgroup'].queryset))

    self.assertTrue(perms.user_can_edit(self.editor,self.item1))
    self.assertTrue(self.item1.workgroup in self.editor.profile.editable_workgroups.all())

    response = self.client.post(
            reverse("admin:%s_%s_change"%(self.itemType._meta.app_label,self.itemType._meta.model_name),args=[self.item1.pk]),
            updated_item
            )

# HERE IS WHERE THE FAILURE IS!!!
    self.assertResponseStatusCodeEqual(response,302)

    self.item1 = self.itemType.objects.get(pk=self.item1.pk)
    self.assertEqual(self.item1.name,updated_name)

But sometimes (and intermittently), when I run the test suite, I post to this form to test saving content, and I get this error: 但是有时(间歇地),当我运行测试套件时,我post到此表单以测试保存的内容,并且出现此错误:

======================================================================
FAIL: test_editor_change_item (aristotle_mdr.tests.test_extension_api.QuestionAdmin)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/travis/build/aristotle-mdr/aristotle-metadata-registry/aristotle_mdr/tests/test_admin_pages.py", line 285, in test_editor_change_item
    self.assertResponseStatusCodeEqual(response,302)
  File "/home/travis/build/aristotle-mdr/aristotle-metadata-registry/aristotle_mdr/tests/utils.py", line 501, in assertResponseStatusCodeEqual
    self.assertEqual(response.status_code, code)
AssertionError: 200 != 302

Because of the nature of this, if posted successfully, the page should redirect, and I have some code that just spits of the response HTML if this isn't the case, and in those cases I get this: 由于这种性质,如果成功发布,则页面应该重定向,如果不是这种情况,我有一些代码只会吐出响应HTML,在这种情况下,我得到了:

<label class="required" for="id_workgroup">Workgroup</label>
<select id="id_workgroup" name="workgroup">
    <option value="">---------</option>
    <option value="17" selected="selected">Test WG 1</option>
</select>
<ul class="errorlist">
    <li>workgroup instance with pk 17 does not exist.</li>
</ul>

However, when this error fires, not every item type will throw the error, only one or two. 但是,当引发此错误时,并不是每个项目类型都会引发错误,只有一个或两个会引发该错误。 But if you look at the select field, the workgroup with id (or pk ) 17 is there! 但是,如果您查看select字段,则id (或pk )为17的工作组就在那里! Plus, when I rerun the test suite it will be fine (sometimes afer a few "warm-up" goes). 另外,当我重新运行测试套件时,它会很好(有时会进行一些“热身”操作)。 I've also never encountered this in a non-test site. 我也从未在非测试站点中遇到过此问题。

I think this might be due to the way Django tests are kept in transactions? 我认为这可能是由于Django测试保留在事务中的原因? I'm starting to get annoyed with this, as it used to be quite intermittent but now its getting more frequent - but still random. 我开始对此感到恼火,因为它曾经是断断续续的,但现在变得越来越频繁-但仍然是随机的。


So this is still failing and I can say what doesn't fix it: 所以这仍然是失败的,我能说什么不能解决问题:

  • Using a file-based SQLite instance instead of in-memory 使用基于文件的SQLite实例而不是内存中的实例
  • Using PostgreSQL for tests 使用PostgreSQL进行测试
  • Switching from TestCase to TransactionTestCase 从TestCase切换到TransactionTestCase

What I know: 我知道的:

  • Tests run fine on the development serve, but the same tests fail on Travis-CI 测试可以在开发服务器上正常运行,但是在Travis-CI上相同的测试失败
  • Its not just the calls to the test web Client, but also some other querysets work 它不仅可以调用测试Web客户端,还可以使用其他一些查询集
  • It might be Transaction based, but I'm not sure. 可能是基于交易的,但是我不确定。

And for the ultra curious here is the issue I'm trying to quash but can't . 出于好奇, 这是我试图解决但不能解决的问题


Edit: 2015-06-11 编辑:2015-06-11

I have built a failing, self contained example!! 建立了一个失败的,自给自足的例子! SQLite consistently works, Postgres consistently fails. SQLite始终有效,Postgres始终失败。

It seems that for some reason this code is consistently bad: 似乎出于某种原因,此代码始终很糟糕:

    def test_bar(self):
        # This test will always work
        print("Do Bar")
        self.do_foo()
        print("Bar done")
    def test_foo(self):
        # This test will always work
        print("Do Foo")
        self.login_editor()
        response = self.client.get(reverse("admin:%s_%s_changelist"%(self.itemType._meta.app_label,self.itemType._meta.model_name)))
        self.assertResponseStatusCodeEqual(response,200)
        self.do_foo()
        print("Foo done")
    def test_zip(self):
        # This test will always FAIL
        print("Do Zip")
        self.do_foo()
        print("Zip done")

In fact, calling an admin changelist view will always cause any subsequent admin pages to fail on Postgres when trying to save as the Workgroup no longer appears in querysets. 实际上,当尝试保存为Workgroup不再出现在查询集中时, 调用管理员changelist视图将始终导致任何后续的管理员页面在Postgres上失败 Now, why is that? 现在,为什么呢?

The full code: 完整代码:

class MinimalExample(TestCase):
    itemType=models.ObjectClass
    form_defaults = {}
    create_defaults = {}
    def setUp(self):
        self.wg1 = models.Workgroup.objects.create(name="Test WG")

        self.editor = User.objects.create_user('eddie','','editor')
        self.editor.is_staff=True
        self.editor.save()

        self.wg1.submitters.add(self.editor)

        self.assertEqual(self.editor.profile.editable_workgroups.count(),1)
        self.item1 = self.itemType.objects.create(name="admin_page_test_oc",description=" ",workgroup=self.wg1,**self.create_defaults)
    def logout(self):
        self.client.post(reverse('django.contrib.auth.views.logout'), {})

    def login_editor(self):
        self.logout()
        response = self.client.post(reverse('friendly_login'), {'username': 'eddie', 'password': 'editor'})
        self.assertEqual(response.status_code,302)
        return response
    def assertResponseStatusCodeEqual(self,response,code):
        self.assertEqual(response.status_code, code)

    def test_bar(self):
        print("Do Bar")
        self.do_foo()
        print("Bar done")
    def test_foo(self):
        print("Do Foo")
        self.login_editor()
        response = self.client.get(reverse("admin:%s_%s_changelist"%(self.itemType._meta.app_label,self.itemType._meta.model_name)))
        self.assertResponseStatusCodeEqual(response,200)
        self.do_foo()
        print("Foo done")
    def test_zip(self):
        print("Do Zip")
        self.do_foo()
        print("Zip done")
    def do_foo(self):
        url_bits = (self.itemType._meta.app_label,self.itemType._meta.model_name)
        response = self.client.post(reverse('friendly_login'), {'username': 'eddie', 'password': 'editor'})

        response = self.client.get(reverse("admin:%s_%s_add"%url_bits))

        data = {'name':"admin_page_test_oc",'description':"test","workgroup":self.wg1.id,
                    'statuses-TOTAL_FORMS': 0, 'statuses-INITIAL_FORMS': 0 #no substatuses
                }
        response = self.client.post(reverse("admin:%s_%s_add"%url_bits),data)
        self.item1 = self.itemType.objects.first()
        response = self.client.get(reverse("admin:%s_%s_change"%url_bits,args=[self.item1.id]))

        data['name'] = "updated"
        # Re post the same data
        response = self.client.post(
                reverse("admin:%s_%s_change"%url_bits,args=[self.item1.id]),
                data
                )
        print response
        self.item1 = self.itemType.objects.first() # decache
        self.assertTrue(self.item1.name == "updated")

I'd start by moving around where you load that workgroup. 我将从在您加载该工作组的位置开始移动。 Try moving the workgroup creation out of setUp and into setUpClass . 尝试将工作组创建从setUp移到setUpClass This has the effect of leaving the workgroup in place for all tests in the TestCase, which is probably what you want. 这样可以将工作组保留在TestCase中的所有测试中,这可能就是您想要的。

def setUp(self):
    from django.test import Client
    self.client = Client()

@classmethod
def setUpClass(cls):
    cls.wg1 = models.Workgroup.objects.create(name="Test WG 1") # Editor is member
    super().setUpClass() # Python 3 version

Fixtures are generally a nightmare, but if this doesn't help I'd be interested to see if moving that workgroup to a fixture and seeing if the problem goes away. 灯具通常是一场噩梦,但是如果这无济于事,我很想知道是否将该工作组移到灯具上,看看问题是否消失了。

Wow, what a wild and crazy ride!! 哇,多么疯狂又疯狂!!

It turns out it was an issue in the RelatedListFilter with a custom queryset that was causing problems. 事实证明,这是在RelatedListFilter使用自定义查询集引起的问题。 The comments on this answer point out that: 关于这个答案的评论指出:

The filter lookups are getting cached in some way 过滤器查找以某种方式被缓存

and

field and field.rel objects will persist between requests field和field.rel对象将在请求之间保留

Even across transaction rollbacks in some cases it seems! 甚至在某些情况下,即使跨事务回滚,它似乎也很重要!

Moral of the story, be careful when using ListFilters in the admin! 故事的ListFilters ,在管理员中使用ListFilters时要小心!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM