diff options
| author | Juan Pedro Fisanotti <fisadev@gmail.com> | 2013-02-23 15:02:01 -0300 |
|---|---|---|
| committer | Ramiro Morales <cramm0@gmail.com> | 2013-03-04 18:08:53 -0300 |
| commit | d9330d5be2ee60b208dcab2616eb164ea2e6bf36 (patch) | |
| tree | 576f70d784d3ac01e72f7f5aad48704ffb59d249 | |
| parent | 3ea0c7d35ac461cb469170486683d10732eb534b (diff) | |
Fixed #6585 -- Admin relationship widgets: Respect ordering defined by target model's ModelAdmin.
Thanks Gary Wilson for the report and Juan Pedro Fisanotti, Carlos
Matías de la Torre for the fix.
| -rw-r--r-- | AUTHORS | 2 | ||||
| -rw-r--r-- | django/contrib/admin/options.py | 21 | ||||
| -rw-r--r-- | tests/admin_ordering/models.py | 1 | ||||
| -rw-r--r-- | tests/admin_ordering/tests.py | 48 |
4 files changed, 72 insertions, 0 deletions
@@ -165,6 +165,7 @@ answer newbie questions, and generally made Django that much better: Matt Dennenbaum deric@monowerks.com Max Derkachev <mderk@yandex.ru> + Carlos Matías de la Torre <cmdelatorre@gmail.com> Rajesh Dhawan <rajesh.dhawan@gmail.com> Sander Dijkhuis <sander.dijkhuis@gmail.com> Jordan Dimov <s3x3y1@gmail.com> @@ -205,6 +206,7 @@ answer newbie questions, and generally made Django that much better: Stefane Fermgier <sf@fermigier.com> J. Pablo Fernandez <pupeno@pupeno.com> Maciej Fijalkowski + Juan Pedro Fisanotti <fisadev@gmail.com> Ben Firshman <ben@firshman.co.uk> Matthew Flanagan <http://wadofstuff.blogspot.com> Eric Floehr <eric@intellovations.com> diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 8f78aaf8dd..cce92c6a95 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -157,6 +157,19 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)): ) return db_field.formfield(**kwargs) + def get_field_queryset(self, db, db_field, request): + """ + If the ModelAdmin specifies ordering, the queryset should respect that + ordering. Otherwise don't specify the queryset, let the field decide + (returns None in that case). + """ + related_admin = self.admin_site._registry.get(db_field.rel.to, None) + if related_admin is not None: + ordering = related_admin.get_ordering(request) + if ordering is not None and ordering != (): + return db_field.rel.to._default_manager.using(db).order_by(*ordering).complex_filter(db_field.rel.limit_choices_to) + return None + def formfield_for_foreignkey(self, db_field, request=None, **kwargs): """ Get a form Field for a ForeignKey. @@ -171,6 +184,10 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)): }) kwargs['empty_label'] = db_field.blank and _('None') or None + queryset = self.get_field_queryset(db, db_field, request) + if queryset is not None: + kwargs['queryset'] = queryset + return db_field.formfield(**kwargs) def formfield_for_manytomany(self, db_field, request=None, **kwargs): @@ -190,6 +207,10 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)): elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)): kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical)) + queryset = self.get_field_queryset(db, db_field, request) + if queryset is not None: + kwargs['queryset'] = queryset + return db_field.formfield(**kwargs) def _declared_fieldsets(self): diff --git a/tests/admin_ordering/models.py b/tests/admin_ordering/models.py index 195b3c3694..3da52b1b00 100644 --- a/tests/admin_ordering/models.py +++ b/tests/admin_ordering/models.py @@ -15,6 +15,7 @@ class Song(models.Model): band = models.ForeignKey(Band) name = models.CharField(max_length=100) duration = models.IntegerField() + other_interpreters = models.ManyToManyField(Band, related_name='covers') class Meta: ordering = ('name',) diff --git a/tests/admin_ordering/tests.py b/tests/admin_ordering/tests.py index faae834f93..10faa9533f 100644 --- a/tests/admin_ordering/tests.py +++ b/tests/admin_ordering/tests.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, unicode_literals from django.test import TestCase, RequestFactory +from django.contrib import admin from django.contrib.admin.options import ModelAdmin from django.contrib.auth.models import User @@ -104,3 +105,50 @@ class TestInlineModelAdminOrdering(TestCase): inline = SongInlineNewOrdering(self.b, None) names = [s.name for s in inline.queryset(request)] self.assertEqual(['Jaded', 'Pink', 'Dude (Looks Like a Lady)'], names) + + +class TestRelatedFieldsAdminOrdering(TestCase): + def setUp(self): + self.b1 = Band(name='Pink Floyd', bio='', rank=1) + self.b1.save() + self.b2 = Band(name='Foo Fighters', bio='', rank=5) + self.b2.save() + + # 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 + admin.site.register(Song, SongAdmin) + + def check_ordering_of_field_choices(self, correct_ordering): + fk_field = admin.site._registry[Song].formfield_for_foreignkey(Song.band.field) + m2m_field = admin.site._registry[Song].formfield_for_manytomany(Song.other_interpreters.field) + + 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 + admin.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', ) + admin.site.register(Band, StaticOrderingBandAdmin) + + # should be ordered by rank (defined by the ModelAdmin) + self.check_ordering_of_field_choices([self.b1, self.b2]) + + def tearDown(self): + admin.site.unregister(Song) + if Band in admin.site._registry: + admin.site.unregister(Band) |
