from django.contrib import admin from django.contrib.admin.options import ModelAdmin from django.contrib.auth.models import User from django.db.models import F from django.test import RequestFactory, TestCase from .models import ( Band, DynOrderingBandAdmin, Song, SongInlineDefaultOrdering, SongInlineNewOrdering, ) class MockRequest: pass class MockSuperUser: def has_perm(self, perm): return True def has_module_perms(self, module): return True request = MockRequest() request.user = MockSuperUser() site = admin.AdminSite() class TestAdminOrdering(TestCase): """ Let's make sure that ModelAdmin.get_queryset uses the ordering we define in ModelAdmin rather that ordering defined in the model's inner Meta class. """ @classmethod def setUpTestData(cls): Band.objects.bulk_create([ Band(name='Aerosmith', bio='', rank=3), Band(name='Radiohead', bio='', rank=1), Band(name='Van Halen', bio='', rank=2), ]) def setUp(self): self.request_factory = RequestFactory() def test_default_ordering(self): """ The default ordering should be by name, as specified in the inner Meta class. """ ma = ModelAdmin(Band, site) names = [b.name for b in ma.get_queryset(request)] self.assertEqual(['Aerosmith', 'Radiohead', 'Van Halen'], names) def test_specified_ordering(self): """ Let's use a custom ModelAdmin that changes the ordering, and make sure it actually changes. """ class BandAdmin(ModelAdmin): ordering = ('rank',) # default ordering is ('name',) ma = BandAdmin(Band, site) names = [b.name for b in ma.get_queryset(request)] self.assertEqual(['Radiohead', 'Van Halen', 'Aerosmith'], names) def test_specified_ordering_by_f_expression(self): class BandAdmin(ModelAdmin): ordering = (F('rank').desc(nulls_last=True),) band_admin = BandAdmin(Band, site) names = [b.name for b in band_admin.get_queryset(request)] self.assertEqual(['Aerosmith', 'Van Halen', 'Radiohead'], names) def test_dynamic_ordering(self): """ Let's use a custom ModelAdmin that changes the ordering dynamically. """ super_user = User.objects.create(username='admin', is_superuser=True) other_user = User.objects.create(username='other') request = self.request_factory.get('/') request.user = super_user ma = DynOrderingBandAdmin(Band, site) names = [b.name for b in ma.get_queryset(request)] self.assertEqual(['Radiohead', 'Van Halen', 'Aerosmith'], names) request.user = other_user names = [b.name for b in ma.get_queryset(request)] self.assertEqual(['Aerosmith', 'Radiohead', 'Van Halen'], names) class TestInlineModelAdminOrdering(TestCase): """ Let's make sure that InlineModelAdmin.get_queryset uses the ordering we define in InlineModelAdmin. """ @classmethod def setUpTestData(cls): cls.band = Band.objects.create(name='Aerosmith', bio='', rank=3) Song.objects.bulk_create([ Song(band=cls.band, name='Pink', duration=235), Song(band=cls.band, name='Dude (Looks Like a Lady)', duration=264), Song(band=cls.band, name='Jaded', duration=214), ]) def test_default_ordering(self): """ The default ordering should be by name, as specified in the inner Meta class. """ inline = SongInlineDefaultOrdering(self.band, site) names = [s.name for s in inline.get_queryset(request)] self.assertEqual(['Dude (Looks Like a Lady)', 'Jaded', 'Pink'], names) def test_specified_ordering(self): """ Let's check with ordering set to something different than the default. """ inline = SongInlineNewOrdering(self.band, site) names = [s.name for s in inline.get_queryset(request)] self.assertEqual(['Jaded', 'Pink', 'Dude (Looks Like a Lady)'], names) class TestRelatedFieldsAdminOrdering(TestCase): @classmethod def setUpTestData(cls): cls.b1 = Band.objects.create(name='Pink Floyd', bio='', rank=1) cls.b2 = Band.objects.create(name='Foo Fighters', bio='', rank=5) def setUp(self): # we need to register a custom ModelAdmin (instead of just using # ModelAdmin) because the field creator tries to find the ModelAdmin # for the related model class SongAdmin(admin.ModelAdmin): pass site.register(Song, SongAdmin) def tearDown(self): site.unregister(Song) if Band in site._registry: site.unregister(Band) def check_ordering_of_field_choices(self, correct_ordering): fk_field = site._registry[Song].formfield_for_foreignkey(Song.band.field, request=None) m2m_field = site._registry[Song].formfield_for_manytomany(Song.other_interpreters.field, request=None) self.assertEqual(list(fk_field.queryset), correct_ordering) self.assertEqual(list(m2m_field.queryset), correct_ordering) def test_no_admin_fallback_to_model_ordering(self): # should be ordered by name (as defined by the model) self.check_ordering_of_field_choices([self.b2, self.b1]) def test_admin_with_no_ordering_fallback_to_model_ordering(self): class NoOrderingBandAdmin(admin.ModelAdmin): pass site.register(Band, NoOrderingBandAdmin) # should be ordered by name (as defined by the model) self.check_ordering_of_field_choices([self.b2, self.b1]) def test_admin_ordering_beats_model_ordering(self): class StaticOrderingBandAdmin(admin.ModelAdmin): ordering = ('rank',) site.register(Band, StaticOrderingBandAdmin) # should be ordered by rank (defined by the ModelAdmin) self.check_ordering_of_field_choices([self.b1, self.b2]) def test_custom_queryset_still_wins(self): """Custom queryset has still precedence (#21405)""" class SongAdmin(admin.ModelAdmin): # Exclude one of the two Bands from the querysets def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == 'band': kwargs["queryset"] = Band.objects.filter(rank__gt=2) return super().formfield_for_foreignkey(db_field, request, **kwargs) def formfield_for_manytomany(self, db_field, request, **kwargs): if db_field.name == 'other_interpreters': kwargs["queryset"] = Band.objects.filter(rank__gt=2) return super().formfield_for_foreignkey(db_field, request, **kwargs) class StaticOrderingBandAdmin(admin.ModelAdmin): ordering = ('rank',) site.unregister(Song) site.register(Song, SongAdmin) site.register(Band, StaticOrderingBandAdmin) self.check_ordering_of_field_choices([self.b2])