1
from __future__ import absolute_import, unicode_literals
5
from django.contrib import admin
6
from django.contrib.admin.options import IncorrectLookupParameters
7
from django.contrib.admin.templatetags.admin_list import pagination
8
from django.contrib.admin.views.main import ChangeList, SEARCH_VAR, ALL_VAR
9
from django.contrib.auth.models import User
10
from django.core.urlresolvers import reverse
11
from django.template import Context, Template
12
from django.test import TestCase
13
from django.test.client import RequestFactory
14
from django.utils import formats
15
from django.utils import six
17
from .admin import (ChildAdmin, QuartetAdmin, BandAdmin, ChordsBandAdmin,
18
GroupAdmin, ParentAdmin, DynamicListDisplayChildAdmin,
19
DynamicListDisplayLinksChildAdmin, CustomPaginationAdmin,
20
FilteredChildAdmin, CustomPaginator, site as custom_site,
21
SwallowAdmin, DynamicListFilterChildAdmin, InvitationAdmin)
22
from .models import (Event, Child, Parent, Genre, Band, Musician, Group,
23
Quartet, Membership, ChordsMusician, ChordsBand, Invitation, Swallow,
24
UnorderedObject, OrderedObject, CustomIdUser)
27
class ChangeListTests(TestCase):
28
urls = "admin_changelist.urls"
31
self.factory = RequestFactory()
33
def _create_superuser(self, username):
34
return User.objects.create(username=username, is_superuser=True)
36
def _mocked_authenticated_request(self, url, user):
37
request = self.factory.get(url)
41
def test_select_related_preserved(self):
43
Regression test for #10348: ChangeList.get_queryset() shouldn't
44
overwrite a custom select_related provided by ModelAdmin.get_queryset().
46
m = ChildAdmin(Child, admin.site)
47
request = self.factory.get('/child/')
48
cl = ChangeList(request, Child, m.list_display, m.list_display_links,
49
m.list_filter, m.date_hierarchy, m.search_fields,
50
m.list_select_related, m.list_per_page,
51
m.list_max_show_all, m.list_editable, m)
52
self.assertEqual(cl.queryset.query.select_related, {
53
'parent': {'name': {}}
56
def test_select_related_as_tuple(self):
57
ia = InvitationAdmin(Invitation, admin.site)
58
request = self.factory.get('/invitation/')
59
cl = ChangeList(request, Child, ia.list_display, ia.list_display_links,
60
ia.list_filter, ia.date_hierarchy, ia.search_fields,
61
ia.list_select_related, ia.list_per_page,
62
ia.list_max_show_all, ia.list_editable, ia)
63
self.assertEqual(cl.queryset.query.select_related, {'player': {}})
65
def test_select_related_as_empty_tuple(self):
66
ia = InvitationAdmin(Invitation, admin.site)
67
ia.list_select_related = ()
68
request = self.factory.get('/invitation/')
69
cl = ChangeList(request, Child, ia.list_display, ia.list_display_links,
70
ia.list_filter, ia.date_hierarchy, ia.search_fields,
71
ia.list_select_related, ia.list_per_page,
72
ia.list_max_show_all, ia.list_editable, ia)
73
self.assertEqual(cl.queryset.query.select_related, False)
76
def test_result_list_empty_changelist_value(self):
78
Regression test for #14982: EMPTY_CHANGELIST_VALUE should be honored
79
for relationship fields
81
new_child = Child.objects.create(name='name', parent=None)
82
request = self.factory.get('/child/')
83
m = ChildAdmin(Child, admin.site)
84
list_display = m.get_list_display(request)
85
list_display_links = m.get_list_display_links(request, list_display)
86
cl = ChangeList(request, Child, list_display, list_display_links,
87
m.list_filter, m.date_hierarchy, m.search_fields,
88
m.list_select_related, m.list_per_page, m.list_max_show_all, m.list_editable, m)
90
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
91
context = Context({'cl': cl})
92
table_output = template.render(context)
93
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
94
row_html = '<tbody><tr class="row1"><th><a href="%s">name</a></th><td class="nowrap">(None)</td></tr></tbody>' % link
95
self.assertFalse(table_output.find(row_html) == -1,
96
'Failed to find expected row element: %s' % table_output)
98
def test_result_list_html(self):
100
Verifies that inclusion tag result_list generates a table when with
101
default ModelAdmin settings.
103
new_parent = Parent.objects.create(name='parent')
104
new_child = Child.objects.create(name='name', parent=new_parent)
105
request = self.factory.get('/child/')
106
m = ChildAdmin(Child, admin.site)
107
list_display = m.get_list_display(request)
108
list_display_links = m.get_list_display_links(request, list_display)
109
cl = ChangeList(request, Child, list_display, list_display_links,
110
m.list_filter, m.date_hierarchy, m.search_fields,
111
m.list_select_related, m.list_per_page, m.list_max_show_all, m.list_editable, m)
113
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
114
context = Context({'cl': cl})
115
table_output = template.render(context)
116
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
117
row_html = '<tbody><tr class="row1"><th><a href="%s">name</a></th><td class="nowrap">Parent object</td></tr></tbody>' % link
118
self.assertFalse(table_output.find(row_html) == -1,
119
'Failed to find expected row element: %s' % table_output)
121
def test_result_list_editable_html(self):
123
Regression tests for #11791: Inclusion tag result_list generates a
124
table and this checks that the items are nested within the table
126
Also a regression test for #13599, verifies that hidden fields
127
when list_editable is enabled are rendered in a div outside the
130
new_parent = Parent.objects.create(name='parent')
131
new_child = Child.objects.create(name='name', parent=new_parent)
132
request = self.factory.get('/child/')
133
m = ChildAdmin(Child, admin.site)
135
# Test with list_editable fields
136
m.list_display = ['id', 'name', 'parent']
137
m.list_display_links = ['id']
138
m.list_editable = ['name']
139
cl = ChangeList(request, Child, m.list_display, m.list_display_links,
140
m.list_filter, m.date_hierarchy, m.search_fields,
141
m.list_select_related, m.list_per_page, m.list_max_show_all, m.list_editable, m)
142
FormSet = m.get_changelist_formset(request)
143
cl.formset = FormSet(queryset=cl.result_list)
144
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
145
context = Context({'cl': cl})
146
table_output = template.render(context)
147
# make sure that hidden fields are in the correct place
148
hiddenfields_div = '<div class="hiddenfields"><input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /></div>' % new_child.id
149
self.assertInHTML(hiddenfields_div, table_output, msg_prefix='Failed to find hidden fields')
151
# make sure that list editable fields are rendered in divs correctly
152
editable_name_field = '<input name="form-0-name" value="name" class="vTextField" maxlength="30" type="text" id="id_form-0-name" />'
153
self.assertInHTML('<td>%s</td>' % editable_name_field, table_output, msg_prefix='Failed to find "name" list_editable field')
155
def test_result_list_editable(self):
157
Regression test for #14312: list_editable with pagination
160
new_parent = Parent.objects.create(name='parent')
162
new_child = Child.objects.create(name='name %s' % i, parent=new_parent)
163
request = self.factory.get('/child/', data={'p': -1}) # Anything outside range
164
m = ChildAdmin(Child, admin.site)
166
# Test with list_editable fields
167
m.list_display = ['id', 'name', 'parent']
168
m.list_display_links = ['id']
169
m.list_editable = ['name']
170
self.assertRaises(IncorrectLookupParameters, lambda: \
171
ChangeList(request, Child, m.list_display, m.list_display_links,
172
m.list_filter, m.date_hierarchy, m.search_fields,
173
m.list_select_related, m.list_per_page, m.list_max_show_all, m.list_editable, m))
175
def test_custom_paginator(self):
176
new_parent = Parent.objects.create(name='parent')
178
new_child = Child.objects.create(name='name %s' % i, parent=new_parent)
180
request = self.factory.get('/child/')
181
m = CustomPaginationAdmin(Child, admin.site)
183
cl = ChangeList(request, Child, m.list_display, m.list_display_links,
184
m.list_filter, m.date_hierarchy, m.search_fields,
185
m.list_select_related, m.list_per_page, m.list_max_show_all, m.list_editable, m)
187
cl.get_results(request)
188
self.assertIsInstance(cl.paginator, CustomPaginator)
190
def test_distinct_for_m2m_in_list_filter(self):
192
Regression test for #13902: When using a ManyToMany in list_filter,
193
results shouldn't apper more than once. Basic ManyToMany.
195
blues = Genre.objects.create(name='Blues')
196
band = Band.objects.create(name='B.B. King Review', nr_of_members=11)
198
band.genres.add(blues)
199
band.genres.add(blues)
201
m = BandAdmin(Band, admin.site)
202
request = self.factory.get('/band/', data={'genres': blues.pk})
204
cl = ChangeList(request, Band, m.list_display,
205
m.list_display_links, m.list_filter, m.date_hierarchy,
206
m.search_fields, m.list_select_related, m.list_per_page,
207
m.list_max_show_all, m.list_editable, m)
209
cl.get_results(request)
211
# There's only one Group instance
212
self.assertEqual(cl.result_count, 1)
214
def test_distinct_for_through_m2m_in_list_filter(self):
216
Regression test for #13902: When using a ManyToMany in list_filter,
217
results shouldn't apper more than once. With an intermediate model.
219
lead = Musician.objects.create(name='Vox')
220
band = Group.objects.create(name='The Hype')
221
Membership.objects.create(group=band, music=lead, role='lead voice')
222
Membership.objects.create(group=band, music=lead, role='bass player')
224
m = GroupAdmin(Group, admin.site)
225
request = self.factory.get('/group/', data={'members': lead.pk})
227
cl = ChangeList(request, Group, m.list_display,
228
m.list_display_links, m.list_filter, m.date_hierarchy,
229
m.search_fields, m.list_select_related, m.list_per_page,
230
m.list_max_show_all, m.list_editable, m)
232
cl.get_results(request)
234
# There's only one Group instance
235
self.assertEqual(cl.result_count, 1)
237
def test_distinct_for_inherited_m2m_in_list_filter(self):
239
Regression test for #13902: When using a ManyToMany in list_filter,
240
results shouldn't apper more than once. Model managed in the
241
admin inherits from the one that defins the relationship.
243
lead = Musician.objects.create(name='John')
244
four = Quartet.objects.create(name='The Beatles')
245
Membership.objects.create(group=four, music=lead, role='lead voice')
246
Membership.objects.create(group=four, music=lead, role='guitar player')
248
m = QuartetAdmin(Quartet, admin.site)
249
request = self.factory.get('/quartet/', data={'members': lead.pk})
251
cl = ChangeList(request, Quartet, m.list_display,
252
m.list_display_links, m.list_filter, m.date_hierarchy,
253
m.search_fields, m.list_select_related, m.list_per_page,
254
m.list_max_show_all, m.list_editable, m)
256
cl.get_results(request)
258
# There's only one Quartet instance
259
self.assertEqual(cl.result_count, 1)
261
def test_distinct_for_m2m_to_inherited_in_list_filter(self):
263
Regression test for #13902: When using a ManyToMany in list_filter,
264
results shouldn't apper more than once. Target of the relationship
265
inherits from another.
267
lead = ChordsMusician.objects.create(name='Player A')
268
three = ChordsBand.objects.create(name='The Chords Trio')
269
Invitation.objects.create(band=three, player=lead, instrument='guitar')
270
Invitation.objects.create(band=three, player=lead, instrument='bass')
272
m = ChordsBandAdmin(ChordsBand, admin.site)
273
request = self.factory.get('/chordsband/', data={'members': lead.pk})
275
cl = ChangeList(request, ChordsBand, m.list_display,
276
m.list_display_links, m.list_filter, m.date_hierarchy,
277
m.search_fields, m.list_select_related, m.list_per_page,
278
m.list_max_show_all, m.list_editable, m)
280
cl.get_results(request)
282
# There's only one ChordsBand instance
283
self.assertEqual(cl.result_count, 1)
285
def test_distinct_for_non_unique_related_object_in_list_filter(self):
287
Regressions tests for #15819: If a field listed in list_filters
288
is a non-unique related object, distinct() must be called.
290
parent = Parent.objects.create(name='Mary')
291
# Two children with the same name
292
Child.objects.create(parent=parent, name='Daniel')
293
Child.objects.create(parent=parent, name='Daniel')
295
m = ParentAdmin(Parent, admin.site)
296
request = self.factory.get('/parent/', data={'child__name': 'Daniel'})
298
cl = ChangeList(request, Parent, m.list_display, m.list_display_links,
299
m.list_filter, m.date_hierarchy, m.search_fields,
300
m.list_select_related, m.list_per_page,
301
m.list_max_show_all, m.list_editable, m)
303
# Make sure distinct() was called
304
self.assertEqual(cl.queryset.count(), 1)
306
def test_distinct_for_non_unique_related_object_in_search_fields(self):
308
Regressions tests for #15819: If a field listed in search_fields
309
is a non-unique related object, distinct() must be called.
311
parent = Parent.objects.create(name='Mary')
312
Child.objects.create(parent=parent, name='Danielle')
313
Child.objects.create(parent=parent, name='Daniel')
315
m = ParentAdmin(Parent, admin.site)
316
request = self.factory.get('/parent/', data={SEARCH_VAR: 'daniel'})
318
cl = ChangeList(request, Parent, m.list_display, m.list_display_links,
319
m.list_filter, m.date_hierarchy, m.search_fields,
320
m.list_select_related, m.list_per_page,
321
m.list_max_show_all, m.list_editable, m)
323
# Make sure distinct() was called
324
self.assertEqual(cl.queryset.count(), 1)
326
def test_pagination(self):
328
Regression tests for #12893: Pagination in admins changelist doesn't
329
use queryset set by modeladmin.
331
parent = Parent.objects.create(name='anything')
333
Child.objects.create(name='name %s' % i, parent=parent)
334
Child.objects.create(name='filtered %s' % i, parent=parent)
336
request = self.factory.get('/child/')
338
# Test default queryset
339
m = ChildAdmin(Child, admin.site)
340
cl = ChangeList(request, Child, m.list_display, m.list_display_links,
341
m.list_filter, m.date_hierarchy, m.search_fields,
342
m.list_select_related, m.list_per_page, m.list_max_show_all,
344
self.assertEqual(cl.queryset.count(), 60)
345
self.assertEqual(cl.paginator.count, 60)
346
self.assertEqual(list(cl.paginator.page_range), [1, 2, 3, 4, 5, 6])
348
# Test custom queryset
349
m = FilteredChildAdmin(Child, admin.site)
350
cl = ChangeList(request, Child, m.list_display, m.list_display_links,
351
m.list_filter, m.date_hierarchy, m.search_fields,
352
m.list_select_related, m.list_per_page, m.list_max_show_all,
354
self.assertEqual(cl.queryset.count(), 30)
355
self.assertEqual(cl.paginator.count, 30)
356
self.assertEqual(list(cl.paginator.page_range), [1, 2, 3])
358
def test_computed_list_display_localization(self):
360
Regression test for #13196: output of functions should be localized
363
User.objects.create_superuser(
364
username='super', email='super@localhost', password='secret')
365
self.client.login(username='super', password='secret')
366
event = Event.objects.create(date=datetime.date.today())
367
response = self.client.get('/admin/admin_changelist/event/')
368
self.assertContains(response, formats.localize(event.date))
369
self.assertNotContains(response, six.text_type(event.date))
371
def test_dynamic_list_display(self):
373
Regression tests for #14206: dynamic list_display support.
375
parent = Parent.objects.create(name='parent')
377
Child.objects.create(name='child %s' % i, parent=parent)
379
user_noparents = self._create_superuser('noparents')
380
user_parents = self._create_superuser('parents')
382
# Test with user 'noparents'
383
m = custom_site._registry[Child]
384
request = self._mocked_authenticated_request('/child/', user_noparents)
385
response = m.changelist_view(request)
386
self.assertNotContains(response, 'Parent object')
388
list_display = m.get_list_display(request)
389
list_display_links = m.get_list_display_links(request, list_display)
390
self.assertEqual(list_display, ['name', 'age'])
391
self.assertEqual(list_display_links, ['name'])
393
# Test with user 'parents'
394
m = DynamicListDisplayChildAdmin(Child, admin.site)
395
request = self._mocked_authenticated_request('/child/', user_parents)
396
response = m.changelist_view(request)
397
self.assertContains(response, 'Parent object')
399
custom_site.unregister(Child)
401
list_display = m.get_list_display(request)
402
list_display_links = m.get_list_display_links(request, list_display)
403
self.assertEqual(list_display, ('parent', 'name', 'age'))
404
self.assertEqual(list_display_links, ['parent'])
406
# Test default implementation
407
custom_site.register(Child, ChildAdmin)
408
m = custom_site._registry[Child]
409
request = self._mocked_authenticated_request('/child/', user_noparents)
410
response = m.changelist_view(request)
411
self.assertContains(response, 'Parent object')
413
def test_show_all(self):
414
parent = Parent.objects.create(name='anything')
416
Child.objects.create(name='name %s' % i, parent=parent)
417
Child.objects.create(name='filtered %s' % i, parent=parent)
419
# Add "show all" parameter to request
420
request = self.factory.get('/child/', data={ALL_VAR: ''})
422
# Test valid "show all" request (number of total objects is under max)
423
m = ChildAdmin(Child, admin.site)
424
# 200 is the max we'll pass to ChangeList
425
cl = ChangeList(request, Child, m.list_display, m.list_display_links,
426
m.list_filter, m.date_hierarchy, m.search_fields,
427
m.list_select_related, m.list_per_page, 200, m.list_editable, m)
428
cl.get_results(request)
429
self.assertEqual(len(cl.result_list), 60)
431
# Test invalid "show all" request (number of total objects over max)
432
# falls back to paginated pages
433
m = ChildAdmin(Child, admin.site)
434
# 30 is the max we'll pass to ChangeList for this test
435
cl = ChangeList(request, Child, m.list_display, m.list_display_links,
436
m.list_filter, m.date_hierarchy, m.search_fields,
437
m.list_select_related, m.list_per_page, 30, m.list_editable, m)
438
cl.get_results(request)
439
self.assertEqual(len(cl.result_list), 10)
441
def test_dynamic_list_display_links(self):
443
Regression tests for #16257: dynamic list_display_links support.
445
parent = Parent.objects.create(name='parent')
446
for i in range(1, 10):
447
Child.objects.create(id=i, name='child %s' % i, parent=parent, age=i)
449
m = DynamicListDisplayLinksChildAdmin(Child, admin.site)
450
superuser = self._create_superuser('superuser')
451
request = self._mocked_authenticated_request('/child/', superuser)
452
response = m.changelist_view(request)
453
for i in range(1, 10):
454
link = reverse('admin:admin_changelist_child_change', args=(i,))
455
self.assertContains(response, '<a href="%s">%s</a>' % (link, i))
457
list_display = m.get_list_display(request)
458
list_display_links = m.get_list_display_links(request, list_display)
459
self.assertEqual(list_display, ('parent', 'name', 'age'))
460
self.assertEqual(list_display_links, ['age'])
462
def test_tuple_list_display(self):
464
Regression test for #17128
465
(ChangeList failing under Python 2.5 after r16319)
467
swallow = Swallow.objects.create(
468
origin='Africa', load='12.34', speed='22.2')
469
model_admin = SwallowAdmin(Swallow, admin.site)
470
superuser = self._create_superuser('superuser')
471
request = self._mocked_authenticated_request('/swallow/', superuser)
472
response = model_admin.changelist_view(request)
473
# just want to ensure it doesn't blow up during rendering
474
self.assertContains(response, six.text_type(swallow.origin))
475
self.assertContains(response, six.text_type(swallow.load))
476
self.assertContains(response, six.text_type(swallow.speed))
478
def test_deterministic_order_for_unordered_model(self):
480
Ensure that the primary key is systematically used in the ordering of
481
the changelist's results to guarantee a deterministic order, even
482
when the Model doesn't have any default ordering defined.
485
superuser = self._create_superuser('superuser')
487
for counter in range(1, 51):
488
UnorderedObject.objects.create(id=counter, bool=True)
490
class UnorderedObjectAdmin(admin.ModelAdmin):
493
def check_results_order(ascending=False):
494
admin.site.register(UnorderedObject, UnorderedObjectAdmin)
495
model_admin = UnorderedObjectAdmin(UnorderedObject, admin.site)
496
counter = 0 if ascending else 51
497
for page in range (0, 5):
498
request = self._mocked_authenticated_request('/unorderedobject/?p=%s' % page, superuser)
499
response = model_admin.changelist_view(request)
500
for result in response.context_data['cl'].result_list:
501
counter += 1 if ascending else -1
502
self.assertEqual(result.id, counter)
503
admin.site.unregister(UnorderedObject)
505
# When no order is defined at all, everything is ordered by '-pk'.
506
check_results_order()
508
# When an order field is defined but multiple records have the same
509
# value for that field, make sure everything gets ordered by -pk as well.
510
UnorderedObjectAdmin.ordering = ['bool']
511
check_results_order()
513
# When order fields are defined, including the pk itself, use them.
514
UnorderedObjectAdmin.ordering = ['bool', '-pk']
515
check_results_order()
516
UnorderedObjectAdmin.ordering = ['bool', 'pk']
517
check_results_order(ascending=True)
518
UnorderedObjectAdmin.ordering = ['-id', 'bool']
519
check_results_order()
520
UnorderedObjectAdmin.ordering = ['id', 'bool']
521
check_results_order(ascending=True)
523
def test_deterministic_order_for_model_ordered_by_its_manager(self):
525
Ensure that the primary key is systematically used in the ordering of
526
the changelist's results to guarantee a deterministic order, even
527
when the Model has a manager that defines a default ordering.
530
superuser = self._create_superuser('superuser')
532
for counter in range(1, 51):
533
OrderedObject.objects.create(id=counter, bool=True, number=counter)
535
class OrderedObjectAdmin(admin.ModelAdmin):
538
def check_results_order(ascending=False):
539
admin.site.register(OrderedObject, OrderedObjectAdmin)
540
model_admin = OrderedObjectAdmin(OrderedObject, admin.site)
541
counter = 0 if ascending else 51
542
for page in range (0, 5):
543
request = self._mocked_authenticated_request('/orderedobject/?p=%s' % page, superuser)
544
response = model_admin.changelist_view(request)
545
for result in response.context_data['cl'].result_list:
546
counter += 1 if ascending else -1
547
self.assertEqual(result.id, counter)
548
admin.site.unregister(OrderedObject)
550
# When no order is defined at all, use the model's default ordering (i.e. 'number')
551
check_results_order(ascending=True)
553
# When an order field is defined but multiple records have the same
554
# value for that field, make sure everything gets ordered by -pk as well.
555
OrderedObjectAdmin.ordering = ['bool']
556
check_results_order()
558
# When order fields are defined, including the pk itself, use them.
559
OrderedObjectAdmin.ordering = ['bool', '-pk']
560
check_results_order()
561
OrderedObjectAdmin.ordering = ['bool', 'pk']
562
check_results_order(ascending=True)
563
OrderedObjectAdmin.ordering = ['-id', 'bool']
564
check_results_order()
565
OrderedObjectAdmin.ordering = ['id', 'bool']
566
check_results_order(ascending=True)
568
def test_dynamic_list_filter(self):
570
Regression tests for ticket #17646: dynamic list_filter support.
572
parent = Parent.objects.create(name='parent')
574
Child.objects.create(name='child %s' % i, parent=parent)
576
user_noparents = self._create_superuser('noparents')
577
user_parents = self._create_superuser('parents')
579
# Test with user 'noparents'
580
m = DynamicListFilterChildAdmin(Child, admin.site)
581
request = self._mocked_authenticated_request('/child/', user_noparents)
582
response = m.changelist_view(request)
583
self.assertEqual(response.context_data['cl'].list_filter, ['name', 'age'])
585
# Test with user 'parents'
586
m = DynamicListFilterChildAdmin(Child, admin.site)
587
request = self._mocked_authenticated_request('/child/', user_parents)
588
response = m.changelist_view(request)
589
self.assertEqual(response.context_data['cl'].list_filter, ('parent', 'name', 'age'))
591
def test_pagination_page_range(self):
593
Regression tests for ticket #15653: ensure the number of pages
594
generated for changelist views are correct.
596
# instantiating and setting up ChangeList object
597
m = GroupAdmin(Group, admin.site)
598
request = self.factory.get('/group/')
599
cl = ChangeList(request, Group, m.list_display,
600
m.list_display_links, m.list_filter, m.date_hierarchy,
601
m.search_fields, m.list_select_related, m.list_per_page,
602
m.list_max_show_all, m.list_editable, m)
603
per_page = cl.list_per_page = 10
605
for page_num, objects_count, expected_page_range in [
607
(0, per_page * 2, list(range(2))),
608
(5, per_page * 11, list(range(11))),
609
(5, per_page * 12, [0, 1, 2, 3, 4, 5, 6, 7, 8, '.', 10, 11]),
610
(6, per_page * 12, [0, 1, '.', 3, 4, 5, 6, 7, 8, 9, 10, 11]),
611
(6, per_page * 13, [0, 1, '.', 3, 4, 5, 6, 7, 8, 9, '.', 11, 12]),
613
# assuming we have exactly `objects_count` objects
614
Group.objects.all().delete()
615
for i in range(objects_count):
616
Group.objects.create(name='test band')
618
# setting page number and calculating page range
619
cl.page_num = page_num
620
cl.get_results(request)
621
real_page_range = pagination(cl)['page_range']
623
self.assertListEqual(
625
list(real_page_range),
629
class AdminLogNodeTestCase(TestCase):
631
def test_get_admin_log_templatetag_custom_user(self):
633
Regression test for ticket #20088: admin log depends on User model
634
having id field as primary key.
636
The old implementation raised an AttributeError when trying to use
639
context = Context({'user': CustomIdUser()})
640
template_string = '{% load log %}{% get_admin_log 10 as admin_log for_user user %}'
642
template = Template(template_string)
644
# Rendering should be u'' since this templatetag just logs,
645
# it doesn't render any string.
646
self.assertEqual(template.render(context), '')