~ubuntu-branches/debian/sid/python-django/sid

« back to all changes in this revision

Viewing changes to tests/admin_changelist/tests.py

  • Committer: Package Import Robot
  • Author(s): Luke Faraone
  • Date: 2013-11-07 15:33:49 UTC
  • mfrom: (1.3.12)
  • Revision ID: package-import@ubuntu.com-20131107153349-e31sc149l2szs3jb
Tags: 1.6-1
* New upstream version. Closes: #557474, #724637.
* python-django now also suggests the installation of ipython,
  bpython, python-django-doc, and libgdal1.
  Closes: #636511, #686333, #704203
* Set package maintainer to Debian Python Modules Team.
* Bump standards version to 3.9.5, no changes needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from __future__ import absolute_import, unicode_literals
 
2
 
 
3
import datetime
 
4
 
 
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
 
16
 
 
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)
 
25
 
 
26
 
 
27
class ChangeListTests(TestCase):
 
28
    urls = "admin_changelist.urls"
 
29
 
 
30
    def setUp(self):
 
31
        self.factory = RequestFactory()
 
32
 
 
33
    def _create_superuser(self, username):
 
34
        return User.objects.create(username=username, is_superuser=True)
 
35
 
 
36
    def _mocked_authenticated_request(self, url, user):
 
37
        request = self.factory.get(url)
 
38
        request.user = user
 
39
        return request
 
40
 
 
41
    def test_select_related_preserved(self):
 
42
        """
 
43
        Regression test for #10348: ChangeList.get_queryset() shouldn't
 
44
        overwrite a custom select_related provided by ModelAdmin.get_queryset().
 
45
        """
 
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': {}}
 
54
        })
 
55
 
 
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': {}})
 
64
 
 
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)
 
74
 
 
75
 
 
76
    def test_result_list_empty_changelist_value(self):
 
77
        """
 
78
        Regression test for #14982: EMPTY_CHANGELIST_VALUE should be honored
 
79
        for relationship fields
 
80
        """
 
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)
 
89
        cl.formset = None
 
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)
 
97
 
 
98
    def test_result_list_html(self):
 
99
        """
 
100
        Verifies that inclusion tag result_list generates a table when with
 
101
        default ModelAdmin settings.
 
102
        """
 
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)
 
112
        cl.formset = None
 
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)
 
120
 
 
121
    def test_result_list_editable_html(self):
 
122
        """
 
123
        Regression tests for #11791: Inclusion tag result_list generates a
 
124
        table and this checks that the items are nested within the table
 
125
        element tags.
 
126
        Also a regression test for #13599, verifies that hidden fields
 
127
        when list_editable is enabled are rendered in a div outside the
 
128
        table.
 
129
        """
 
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)
 
134
 
 
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')
 
150
 
 
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')
 
154
 
 
155
    def test_result_list_editable(self):
 
156
        """
 
157
        Regression test for #14312: list_editable with pagination
 
158
        """
 
159
 
 
160
        new_parent = Parent.objects.create(name='parent')
 
161
        for i in range(200):
 
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)
 
165
 
 
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))
 
174
 
 
175
    def test_custom_paginator(self):
 
176
        new_parent = Parent.objects.create(name='parent')
 
177
        for i in range(200):
 
178
            new_child = Child.objects.create(name='name %s' % i, parent=new_parent)
 
179
 
 
180
        request = self.factory.get('/child/')
 
181
        m = CustomPaginationAdmin(Child, admin.site)
 
182
 
 
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)
 
186
 
 
187
        cl.get_results(request)
 
188
        self.assertIsInstance(cl.paginator, CustomPaginator)
 
189
 
 
190
    def test_distinct_for_m2m_in_list_filter(self):
 
191
        """
 
192
        Regression test for #13902: When using a ManyToMany in list_filter,
 
193
        results shouldn't apper more than once. Basic ManyToMany.
 
194
        """
 
195
        blues = Genre.objects.create(name='Blues')
 
196
        band = Band.objects.create(name='B.B. King Review', nr_of_members=11)
 
197
 
 
198
        band.genres.add(blues)
 
199
        band.genres.add(blues)
 
200
 
 
201
        m = BandAdmin(Band, admin.site)
 
202
        request = self.factory.get('/band/', data={'genres': blues.pk})
 
203
 
 
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)
 
208
 
 
209
        cl.get_results(request)
 
210
 
 
211
        # There's only one Group instance
 
212
        self.assertEqual(cl.result_count, 1)
 
213
 
 
214
    def test_distinct_for_through_m2m_in_list_filter(self):
 
215
        """
 
216
        Regression test for #13902: When using a ManyToMany in list_filter,
 
217
        results shouldn't apper more than once. With an intermediate model.
 
218
        """
 
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')
 
223
 
 
224
        m = GroupAdmin(Group, admin.site)
 
225
        request = self.factory.get('/group/', data={'members': lead.pk})
 
226
 
 
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)
 
231
 
 
232
        cl.get_results(request)
 
233
 
 
234
        # There's only one Group instance
 
235
        self.assertEqual(cl.result_count, 1)
 
236
 
 
237
    def test_distinct_for_inherited_m2m_in_list_filter(self):
 
238
        """
 
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.
 
242
        """
 
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')
 
247
 
 
248
        m = QuartetAdmin(Quartet, admin.site)
 
249
        request = self.factory.get('/quartet/', data={'members': lead.pk})
 
250
 
 
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)
 
255
 
 
256
        cl.get_results(request)
 
257
 
 
258
        # There's only one Quartet instance
 
259
        self.assertEqual(cl.result_count, 1)
 
260
 
 
261
    def test_distinct_for_m2m_to_inherited_in_list_filter(self):
 
262
        """
 
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.
 
266
        """
 
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')
 
271
 
 
272
        m = ChordsBandAdmin(ChordsBand, admin.site)
 
273
        request = self.factory.get('/chordsband/', data={'members': lead.pk})
 
274
 
 
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)
 
279
 
 
280
        cl.get_results(request)
 
281
 
 
282
        # There's only one ChordsBand instance
 
283
        self.assertEqual(cl.result_count, 1)
 
284
 
 
285
    def test_distinct_for_non_unique_related_object_in_list_filter(self):
 
286
        """
 
287
        Regressions tests for #15819: If a field listed in list_filters
 
288
        is a non-unique related object, distinct() must be called.
 
289
        """
 
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')
 
294
 
 
295
        m = ParentAdmin(Parent, admin.site)
 
296
        request = self.factory.get('/parent/', data={'child__name': 'Daniel'})
 
297
 
 
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)
 
302
 
 
303
        # Make sure distinct() was called
 
304
        self.assertEqual(cl.queryset.count(), 1)
 
305
 
 
306
    def test_distinct_for_non_unique_related_object_in_search_fields(self):
 
307
        """
 
308
        Regressions tests for #15819: If a field listed in search_fields
 
309
        is a non-unique related object, distinct() must be called.
 
310
        """
 
311
        parent = Parent.objects.create(name='Mary')
 
312
        Child.objects.create(parent=parent, name='Danielle')
 
313
        Child.objects.create(parent=parent, name='Daniel')
 
314
 
 
315
        m = ParentAdmin(Parent, admin.site)
 
316
        request = self.factory.get('/parent/', data={SEARCH_VAR: 'daniel'})
 
317
 
 
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)
 
322
 
 
323
        # Make sure distinct() was called
 
324
        self.assertEqual(cl.queryset.count(), 1)
 
325
 
 
326
    def test_pagination(self):
 
327
        """
 
328
        Regression tests for #12893: Pagination in admins changelist doesn't
 
329
        use queryset set by modeladmin.
 
330
        """
 
331
        parent = Parent.objects.create(name='anything')
 
332
        for i in range(30):
 
333
            Child.objects.create(name='name %s' % i, parent=parent)
 
334
            Child.objects.create(name='filtered %s' % i, parent=parent)
 
335
 
 
336
        request = self.factory.get('/child/')
 
337
 
 
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,
 
343
                m.list_editable, m)
 
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])
 
347
 
 
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,
 
353
                m.list_editable, m)
 
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])
 
357
 
 
358
    def test_computed_list_display_localization(self):
 
359
        """
 
360
        Regression test for #13196: output of functions should be  localized
 
361
        in the changelist.
 
362
        """
 
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))
 
370
 
 
371
    def test_dynamic_list_display(self):
 
372
        """
 
373
        Regression tests for #14206: dynamic list_display support.
 
374
        """
 
375
        parent = Parent.objects.create(name='parent')
 
376
        for i in range(10):
 
377
            Child.objects.create(name='child %s' % i, parent=parent)
 
378
 
 
379
        user_noparents = self._create_superuser('noparents')
 
380
        user_parents = self._create_superuser('parents')
 
381
 
 
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')
 
387
 
 
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'])
 
392
 
 
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')
 
398
 
 
399
        custom_site.unregister(Child)
 
400
 
 
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'])
 
405
 
 
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')
 
412
 
 
413
    def test_show_all(self):
 
414
        parent = Parent.objects.create(name='anything')
 
415
        for i in range(30):
 
416
            Child.objects.create(name='name %s' % i, parent=parent)
 
417
            Child.objects.create(name='filtered %s' % i, parent=parent)
 
418
 
 
419
        # Add "show all" parameter to request
 
420
        request = self.factory.get('/child/', data={ALL_VAR: ''})
 
421
 
 
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)
 
430
 
 
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)
 
440
 
 
441
    def test_dynamic_list_display_links(self):
 
442
        """
 
443
        Regression tests for #16257: dynamic list_display_links support.
 
444
        """
 
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)
 
448
 
 
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))
 
456
 
 
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'])
 
461
 
 
462
    def test_tuple_list_display(self):
 
463
        """
 
464
        Regression test for #17128
 
465
        (ChangeList failing under Python 2.5 after r16319)
 
466
        """
 
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))
 
477
 
 
478
    def test_deterministic_order_for_unordered_model(self):
 
479
        """
 
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.
 
483
        Refs #17198.
 
484
        """
 
485
        superuser = self._create_superuser('superuser')
 
486
 
 
487
        for counter in range(1, 51):
 
488
            UnorderedObject.objects.create(id=counter, bool=True)
 
489
 
 
490
        class UnorderedObjectAdmin(admin.ModelAdmin):
 
491
            list_per_page = 10
 
492
 
 
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)
 
504
 
 
505
        # When no order is defined at all, everything is ordered by '-pk'.
 
506
        check_results_order()
 
507
 
 
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()
 
512
 
 
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)
 
522
 
 
523
    def test_deterministic_order_for_model_ordered_by_its_manager(self):
 
524
        """
 
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.
 
528
        Refs #17198.
 
529
        """
 
530
        superuser = self._create_superuser('superuser')
 
531
 
 
532
        for counter in range(1, 51):
 
533
            OrderedObject.objects.create(id=counter, bool=True, number=counter)
 
534
 
 
535
        class OrderedObjectAdmin(admin.ModelAdmin):
 
536
            list_per_page = 10
 
537
 
 
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)
 
549
 
 
550
        # When no order is defined at all, use the model's default ordering (i.e. 'number')
 
551
        check_results_order(ascending=True)
 
552
 
 
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()
 
557
 
 
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)
 
567
 
 
568
    def test_dynamic_list_filter(self):
 
569
        """
 
570
        Regression tests for ticket #17646: dynamic list_filter support.
 
571
        """
 
572
        parent = Parent.objects.create(name='parent')
 
573
        for i in range(10):
 
574
            Child.objects.create(name='child %s' % i, parent=parent)
 
575
 
 
576
        user_noparents = self._create_superuser('noparents')
 
577
        user_parents = self._create_superuser('parents')
 
578
 
 
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'])
 
584
 
 
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'))
 
590
 
 
591
    def test_pagination_page_range(self):
 
592
        """
 
593
        Regression tests for ticket #15653: ensure the number of pages
 
594
        generated for changelist views are correct.
 
595
        """
 
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
 
604
 
 
605
        for page_num, objects_count, expected_page_range in [
 
606
            (0, per_page, []),
 
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]),
 
612
        ]:
 
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')
 
617
 
 
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']
 
622
 
 
623
            self.assertListEqual(
 
624
                expected_page_range,
 
625
                list(real_page_range),
 
626
            )
 
627
 
 
628
 
 
629
class AdminLogNodeTestCase(TestCase):
 
630
 
 
631
    def test_get_admin_log_templatetag_custom_user(self):
 
632
        """
 
633
        Regression test for ticket #20088: admin log depends on User model
 
634
        having id field as primary key.
 
635
 
 
636
        The old implementation raised an AttributeError when trying to use
 
637
        the id field.
 
638
        """
 
639
        context = Context({'user': CustomIdUser()})
 
640
        template_string = '{% load log %}{% get_admin_log 10 as admin_log for_user user %}'
 
641
 
 
642
        template = Template(template_string)
 
643
 
 
644
        # Rendering should be u'' since this templatetag just logs,
 
645
        # it doesn't render any string.
 
646
        self.assertEqual(template.render(context), '')