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.
This commit is contained in:
parent
3ea0c7d35a
commit
d9330d5be2
2
AUTHORS
2
AUTHORS
|
@ -165,6 +165,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Matt Dennenbaum
|
Matt Dennenbaum
|
||||||
deric@monowerks.com
|
deric@monowerks.com
|
||||||
Max Derkachev <mderk@yandex.ru>
|
Max Derkachev <mderk@yandex.ru>
|
||||||
|
Carlos Matías de la Torre <cmdelatorre@gmail.com>
|
||||||
Rajesh Dhawan <rajesh.dhawan@gmail.com>
|
Rajesh Dhawan <rajesh.dhawan@gmail.com>
|
||||||
Sander Dijkhuis <sander.dijkhuis@gmail.com>
|
Sander Dijkhuis <sander.dijkhuis@gmail.com>
|
||||||
Jordan Dimov <s3x3y1@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>
|
Stefane Fermgier <sf@fermigier.com>
|
||||||
J. Pablo Fernandez <pupeno@pupeno.com>
|
J. Pablo Fernandez <pupeno@pupeno.com>
|
||||||
Maciej Fijalkowski
|
Maciej Fijalkowski
|
||||||
|
Juan Pedro Fisanotti <fisadev@gmail.com>
|
||||||
Ben Firshman <ben@firshman.co.uk>
|
Ben Firshman <ben@firshman.co.uk>
|
||||||
Matthew Flanagan <http://wadofstuff.blogspot.com>
|
Matthew Flanagan <http://wadofstuff.blogspot.com>
|
||||||
Eric Floehr <eric@intellovations.com>
|
Eric Floehr <eric@intellovations.com>
|
||||||
|
|
|
@ -157,6 +157,19 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
|
||||||
)
|
)
|
||||||
return db_field.formfield(**kwargs)
|
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):
|
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Get a form Field for a ForeignKey.
|
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
|
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)
|
return db_field.formfield(**kwargs)
|
||||||
|
|
||||||
def formfield_for_manytomany(self, db_field, request=None, **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)):
|
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))
|
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)
|
return db_field.formfield(**kwargs)
|
||||||
|
|
||||||
def _declared_fieldsets(self):
|
def _declared_fieldsets(self):
|
||||||
|
|
|
@ -15,6 +15,7 @@ class Song(models.Model):
|
||||||
band = models.ForeignKey(Band)
|
band = models.ForeignKey(Band)
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=100)
|
||||||
duration = models.IntegerField()
|
duration = models.IntegerField()
|
||||||
|
other_interpreters = models.ManyToManyField(Band, related_name='covers')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('name',)
|
ordering = ('name',)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
from django.test import TestCase, RequestFactory
|
from django.test import TestCase, RequestFactory
|
||||||
|
from django.contrib import admin
|
||||||
from django.contrib.admin.options import ModelAdmin
|
from django.contrib.admin.options import ModelAdmin
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
@ -104,3 +105,50 @@ class TestInlineModelAdminOrdering(TestCase):
|
||||||
inline = SongInlineNewOrdering(self.b, None)
|
inline = SongInlineNewOrdering(self.b, None)
|
||||||
names = [s.name for s in inline.queryset(request)]
|
names = [s.name for s in inline.queryset(request)]
|
||||||
self.assertEqual(['Jaded', 'Pink', 'Dude (Looks Like a Lady)'], names)
|
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)
|
||||||
|
|
Loading…
Reference in New Issue