Kaydet (Commit) d9330d5b authored tarafından Juan Pedro Fisanotti's avatar Juan Pedro Fisanotti Kaydeden (comit) Ramiro Morales

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.
üst 3ea0c7d3
...@@ -165,6 +165,7 @@ answer newbie questions, and generally made Django that much better: ...@@ -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: ...@@ -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)): ...@@ -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)): ...@@ -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)): ...@@ -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): ...@@ -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',)
......
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): ...@@ -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)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment