[英]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
属于Workgroup
, Item
也可以属于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: 所以这仍然是失败的,我能说什么不能解决问题:
What I know: 我知道的:
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.