/tests/regressiontests/admin_changelist/tests.py

https://code.google.com/p/mango-py/ · Python · 351 lines · 307 code · 15 blank · 29 comment · 0 complexity · 8bfe154938b3502a6ae90241c9def877 MD5 · raw file

  1. from django.contrib import admin
  2. from django.contrib.admin.options import IncorrectLookupParameters
  3. from django.contrib.admin.views.main import ChangeList, SEARCH_VAR
  4. from django.core.paginator import Paginator
  5. from django.template import Context, Template
  6. from django.test import TransactionTestCase
  7. from models import (Child, Parent, Genre, Band, Musician, Group, Quartet,
  8. Membership, ChordsMusician, ChordsBand, Invitation)
  9. class ChangeListTests(TransactionTestCase):
  10. def test_select_related_preserved(self):
  11. """
  12. Regression test for #10348: ChangeList.get_query_set() shouldn't
  13. overwrite a custom select_related provided by ModelAdmin.queryset().
  14. """
  15. m = ChildAdmin(Child, admin.site)
  16. cl = ChangeList(MockRequest(), Child, m.list_display, m.list_display_links,
  17. m.list_filter, m.date_hierarchy, m.search_fields,
  18. m.list_select_related, m.list_per_page, m.list_editable, m)
  19. self.assertEqual(cl.query_set.query.select_related, {'parent': {'name': {}}})
  20. def test_result_list_empty_changelist_value(self):
  21. """
  22. Regression test for #14982: EMPTY_CHANGELIST_VALUE should be honored
  23. for relationship fields
  24. """
  25. new_child = Child.objects.create(name='name', parent=None)
  26. request = MockRequest()
  27. m = ChildAdmin(Child, admin.site)
  28. cl = ChangeList(request, Child, m.list_display, m.list_display_links,
  29. m.list_filter, m.date_hierarchy, m.search_fields,
  30. m.list_select_related, m.list_per_page, m.list_editable, m)
  31. cl.formset = None
  32. template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
  33. context = Context({'cl': cl})
  34. table_output = template.render(context)
  35. row_html = '<tbody><tr class="row1"><td class="action-checkbox"><input type="checkbox" class="action-select" value="%d" name="_selected_action" /></td><th><a href="%d/">name</a></th><td class="nowrap">(None)</td></tr></tbody>' % (new_child.id, new_child.id)
  36. self.assertFalse(table_output.find(row_html) == -1,
  37. 'Failed to find expected row element: %s' % table_output)
  38. def test_result_list_html(self):
  39. """
  40. Verifies that inclusion tag result_list generates a table when with
  41. default ModelAdmin settings.
  42. """
  43. new_parent = Parent.objects.create(name='parent')
  44. new_child = Child.objects.create(name='name', parent=new_parent)
  45. request = MockRequest()
  46. m = ChildAdmin(Child, admin.site)
  47. cl = ChangeList(request, Child, m.list_display, m.list_display_links,
  48. m.list_filter, m.date_hierarchy, m.search_fields,
  49. m.list_select_related, m.list_per_page, m.list_editable, m)
  50. cl.formset = None
  51. template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
  52. context = Context({'cl': cl})
  53. table_output = template.render(context)
  54. row_html = '<tbody><tr class="row1"><td class="action-checkbox"><input type="checkbox" class="action-select" value="%d" name="_selected_action" /></td><th><a href="%d/">name</a></th><td class="nowrap">Parent object</td></tr></tbody>' % (new_child.id, new_child.id)
  55. self.assertFalse(table_output.find(row_html) == -1,
  56. 'Failed to find expected row element: %s' % table_output)
  57. def test_result_list_editable_html(self):
  58. """
  59. Regression tests for #11791: Inclusion tag result_list generates a
  60. table and this checks that the items are nested within the table
  61. element tags.
  62. Also a regression test for #13599, verifies that hidden fields
  63. when list_editable is enabled are rendered in a div outside the
  64. table.
  65. """
  66. new_parent = Parent.objects.create(name='parent')
  67. new_child = Child.objects.create(name='name', parent=new_parent)
  68. request = MockRequest()
  69. m = ChildAdmin(Child, admin.site)
  70. # Test with list_editable fields
  71. m.list_display = ['id', 'name', 'parent']
  72. m.list_display_links = ['id']
  73. m.list_editable = ['name']
  74. cl = ChangeList(request, Child, m.list_display, m.list_display_links,
  75. m.list_filter, m.date_hierarchy, m.search_fields,
  76. m.list_select_related, m.list_per_page, m.list_editable, m)
  77. FormSet = m.get_changelist_formset(request)
  78. cl.formset = FormSet(queryset=cl.result_list)
  79. template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
  80. context = Context({'cl': cl})
  81. table_output = template.render(context)
  82. # make sure that hidden fields are in the correct place
  83. hiddenfields_div = '<div class="hiddenfields"><input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /></div>' % new_child.id
  84. self.assertFalse(table_output.find(hiddenfields_div) == -1,
  85. 'Failed to find hidden fields in: %s' % table_output)
  86. # make sure that list editable fields are rendered in divs correctly
  87. editable_name_field = '<input name="form-0-name" value="name" class="vTextField" maxlength="30" type="text" id="id_form-0-name" />'
  88. self.assertFalse('<td>%s</td>' % editable_name_field == -1,
  89. 'Failed to find "name" list_editable field in: %s' % table_output)
  90. def test_result_list_editable(self):
  91. """
  92. Regression test for #14312: list_editable with pagination
  93. """
  94. new_parent = Parent.objects.create(name='parent')
  95. for i in range(200):
  96. new_child = Child.objects.create(name='name %s' % i, parent=new_parent)
  97. request = MockRequest()
  98. request.GET['p'] = -1 # Anything outside range
  99. m = ChildAdmin(Child, admin.site)
  100. # Test with list_editable fields
  101. m.list_display = ['id', 'name', 'parent']
  102. m.list_display_links = ['id']
  103. m.list_editable = ['name']
  104. self.assertRaises(IncorrectLookupParameters, lambda: \
  105. ChangeList(request, Child, m.list_display, m.list_display_links,
  106. m.list_filter, m.date_hierarchy, m.search_fields,
  107. m.list_select_related, m.list_per_page, m.list_editable, m))
  108. def test_custom_paginator(self):
  109. new_parent = Parent.objects.create(name='parent')
  110. for i in range(200):
  111. new_child = Child.objects.create(name='name %s' % i, parent=new_parent)
  112. request = MockRequest()
  113. m = ChildAdmin(Child, admin.site)
  114. m.list_display = ['id', 'name', 'parent']
  115. m.list_display_links = ['id']
  116. m.list_editable = ['name']
  117. m.paginator = CustomPaginator
  118. cl = ChangeList(request, Child, m.list_display, m.list_display_links,
  119. m.list_filter, m.date_hierarchy, m.search_fields,
  120. m.list_select_related, m.list_per_page, m.list_editable, m)
  121. cl.get_results(request)
  122. self.assertIsInstance(cl.paginator, CustomPaginator)
  123. def test_distinct_for_m2m_in_list_filter(self):
  124. """
  125. Regression test for #13902: When using a ManyToMany in list_filter,
  126. results shouldn't apper more than once. Basic ManyToMany.
  127. """
  128. blues = Genre.objects.create(name='Blues')
  129. band = Band.objects.create(name='B.B. King Review', nr_of_members=11)
  130. band.genres.add(blues)
  131. band.genres.add(blues)
  132. m = BandAdmin(Band, admin.site)
  133. request = MockFilterRequest('genres', blues.pk)
  134. cl = ChangeList(request, Band, m.list_display,
  135. m.list_display_links, m.list_filter, m.date_hierarchy,
  136. m.search_fields, m.list_select_related, m.list_per_page,
  137. m.list_editable, m)
  138. cl.get_results(request)
  139. # There's only one Group instance
  140. self.assertEqual(cl.result_count, 1)
  141. def test_distinct_for_through_m2m_in_list_filter(self):
  142. """
  143. Regression test for #13902: When using a ManyToMany in list_filter,
  144. results shouldn't apper more than once. With an intermediate model.
  145. """
  146. lead = Musician.objects.create(name='Vox')
  147. band = Group.objects.create(name='The Hype')
  148. Membership.objects.create(group=band, music=lead, role='lead voice')
  149. Membership.objects.create(group=band, music=lead, role='bass player')
  150. m = GroupAdmin(Group, admin.site)
  151. request = MockFilterRequest('members', lead.pk)
  152. cl = ChangeList(request, Group, m.list_display,
  153. m.list_display_links, m.list_filter, m.date_hierarchy,
  154. m.search_fields, m.list_select_related, m.list_per_page,
  155. m.list_editable, m)
  156. cl.get_results(request)
  157. # There's only one Group instance
  158. self.assertEqual(cl.result_count, 1)
  159. def test_distinct_for_inherited_m2m_in_list_filter(self):
  160. """
  161. Regression test for #13902: When using a ManyToMany in list_filter,
  162. results shouldn't apper more than once. Model managed in the
  163. admin inherits from the one that defins the relationship.
  164. """
  165. lead = Musician.objects.create(name='John')
  166. four = Quartet.objects.create(name='The Beatles')
  167. Membership.objects.create(group=four, music=lead, role='lead voice')
  168. Membership.objects.create(group=four, music=lead, role='guitar player')
  169. m = QuartetAdmin(Quartet, admin.site)
  170. request = MockFilterRequest('members', lead.pk)
  171. cl = ChangeList(request, Quartet, m.list_display,
  172. m.list_display_links, m.list_filter, m.date_hierarchy,
  173. m.search_fields, m.list_select_related, m.list_per_page,
  174. m.list_editable, m)
  175. cl.get_results(request)
  176. # There's only one Quartet instance
  177. self.assertEqual(cl.result_count, 1)
  178. def test_distinct_for_m2m_to_inherited_in_list_filter(self):
  179. """
  180. Regression test for #13902: When using a ManyToMany in list_filter,
  181. results shouldn't apper more than once. Target of the relationship
  182. inherits from another.
  183. """
  184. lead = ChordsMusician.objects.create(name='Player A')
  185. three = ChordsBand.objects.create(name='The Chords Trio')
  186. Invitation.objects.create(band=three, player=lead, instrument='guitar')
  187. Invitation.objects.create(band=three, player=lead, instrument='bass')
  188. m = ChordsBandAdmin(ChordsBand, admin.site)
  189. request = MockFilterRequest('members', lead.pk)
  190. cl = ChangeList(request, ChordsBand, m.list_display,
  191. m.list_display_links, m.list_filter, m.date_hierarchy,
  192. m.search_fields, m.list_select_related, m.list_per_page,
  193. m.list_editable, m)
  194. cl.get_results(request)
  195. # There's only one ChordsBand instance
  196. self.assertEqual(cl.result_count, 1)
  197. def test_distinct_for_non_unique_related_object_in_list_filter(self):
  198. """
  199. Regressions tests for #15819: If a field listed in list_filters
  200. is a non-unique related object, distinct() must be called.
  201. """
  202. parent = Parent.objects.create(name='Mary')
  203. # Two children with the same name
  204. Child.objects.create(parent=parent, name='Daniel')
  205. Child.objects.create(parent=parent, name='Daniel')
  206. m = ParentAdmin(Parent, admin.site)
  207. request = MockFilterRequest('child__name', 'Daniel')
  208. cl = ChangeList(request, Parent, m.list_display, m.list_display_links,
  209. m.list_filter, m.date_hierarchy, m.search_fields,
  210. m.list_select_related, m.list_per_page,
  211. m.list_editable, m)
  212. # Make sure distinct() was called
  213. self.assertEqual(cl.query_set.count(), 1)
  214. def test_distinct_for_non_unique_related_object_in_search_fields(self):
  215. """
  216. Regressions tests for #15819: If a field listed in search_fields
  217. is a non-unique related object, distinct() must be called.
  218. """
  219. parent = Parent.objects.create(name='Mary')
  220. Child.objects.create(parent=parent, name='Danielle')
  221. Child.objects.create(parent=parent, name='Daniel')
  222. m = ParentAdmin(Parent, admin.site)
  223. request = MockSearchRequest('daniel')
  224. cl = ChangeList(request, Parent, m.list_display, m.list_display_links,
  225. m.list_filter, m.date_hierarchy, m.search_fields,
  226. m.list_select_related, m.list_per_page,
  227. m.list_editable, m)
  228. # Make sure distinct() was called
  229. self.assertEqual(cl.query_set.count(), 1)
  230. def test_pagination(self):
  231. """
  232. Regression tests for #12893: Pagination in admins changelist doesn't
  233. use queryset set by modeladmin.
  234. """
  235. parent = Parent.objects.create(name='anything')
  236. for i in range(30):
  237. Child.objects.create(name='name %s' % i, parent=parent)
  238. Child.objects.create(name='filtered %s' % i, parent=parent)
  239. request = MockRequest()
  240. # Test default queryset
  241. m = ChildAdmin(Child, admin.site)
  242. cl = ChangeList(request, Child, m.list_display, m.list_display_links,
  243. m.list_filter, m.date_hierarchy, m.search_fields,
  244. m.list_select_related, m.list_per_page, m.list_editable, m)
  245. self.assertEqual(cl.query_set.count(), 60)
  246. self.assertEqual(cl.paginator.count, 60)
  247. self.assertEqual(cl.paginator.page_range, [1, 2, 3, 4, 5, 6])
  248. # Test custom queryset
  249. m = FilteredChildAdmin(Child, admin.site)
  250. cl = ChangeList(request, Child, m.list_display, m.list_display_links,
  251. m.list_filter, m.date_hierarchy, m.search_fields,
  252. m.list_select_related, m.list_per_page, m.list_editable, m)
  253. self.assertEqual(cl.query_set.count(), 30)
  254. self.assertEqual(cl.paginator.count, 30)
  255. self.assertEqual(cl.paginator.page_range, [1, 2, 3])
  256. class ParentAdmin(admin.ModelAdmin):
  257. list_filter = ['child__name']
  258. search_fields = ['child__name']
  259. class ChildAdmin(admin.ModelAdmin):
  260. list_display = ['name', 'parent']
  261. list_per_page = 10
  262. def queryset(self, request):
  263. return super(ChildAdmin, self).queryset(request).select_related("parent__name")
  264. class FilteredChildAdmin(admin.ModelAdmin):
  265. list_display = ['name', 'parent']
  266. list_per_page = 10
  267. def queryset(self, request):
  268. return super(FilteredChildAdmin, self).queryset(request).filter(
  269. name__contains='filtered')
  270. class MockRequest(object):
  271. GET = {}
  272. class CustomPaginator(Paginator):
  273. def __init__(self, queryset, page_size, orphans=0, allow_empty_first_page=True):
  274. super(CustomPaginator, self).__init__(queryset, 5, orphans=2,
  275. allow_empty_first_page=allow_empty_first_page)
  276. class BandAdmin(admin.ModelAdmin):
  277. list_filter = ['genres']
  278. class GroupAdmin(admin.ModelAdmin):
  279. list_filter = ['members']
  280. class QuartetAdmin(admin.ModelAdmin):
  281. list_filter = ['members']
  282. class ChordsBandAdmin(admin.ModelAdmin):
  283. list_filter = ['members']
  284. class MockFilterRequest(object):
  285. def __init__(self, filter, q):
  286. self.GET = {filter: q}
  287. class MockSearchRequest(object):
  288. def __init__(self, q):
  289. self.GET = {SEARCH_VAR: q}