Ticket #25: 25_patch.2.diff
File 25_patch.2.diff, 8.9 KB (added by , 14 years ago) |
---|
-
AUTHORS
diff --git a/AUTHORS b/AUTHORS index 047ba91..8f7ed43 100644
a b answer newbie questions, and generally made Django that much better: 206 206 Scot Hacker <shacker@birdhouse.org> 207 207 dAniel hAhler 208 208 hambaloney 209 Chuck Harmston <chuck@chuckharmston.com> 209 210 Brian Harring <ferringb@gmail.com> 210 211 Brant Harris 211 212 Ronny Haryanto <http://ronny.haryan.to/> -
django/contrib/admin/media/css/widgets.css
diff --git a/django/contrib/admin/media/css/widgets.css b/django/contrib/admin/media/css/widgets.css index 620e082..b1c7768 100644
a b 5 5 float: left; 6 6 } 7 7 8 .selector-single{ 9 width: 282px !important; 10 } 11 8 12 .selector select { 9 13 width: 270px; 10 14 height: 17.2em; -
new file django/contrib/admin/media/js/fkfilter.js
diff --git a/django/contrib/admin/media/js/fkfilter.js b/django/contrib/admin/media/js/fkfilter.js new file mode 100644 index 0000000..07bd84c
- + 1 (function($){ 2 3 $.fn.fk_filter = function(verbose_name) { 4 5 if(verbose_name === 'undefined') var verbose_name = ''; 6 7 return this.each(function(){ 8 9 $(this).attr('size', 2); // Force a multi-row <select> 10 $(this).find('option[value=""]').remove(); // Remove the '-----' 11 12 // Create the wrappers 13 var outerwrapper = $('<div />', { 14 'class': 'selector selector-single' 15 }); 16 $(this).wrap(outerwrapper); 17 var innerwrapper = $('<div />', { 18 'class': 'selector-available' 19 }); 20 $(this).wrap(innerwrapper); 21 22 // Creates Header 23 var header = $('<h2 />', { 24 'text': interpolate(gettext('Available %s'), [verbose_name]) 25 }).insertBefore(this); 26 27 // Creates search bar 28 var searchbar = $('<p />', { 29 'html': '<img src="/media/img/admin/selector-search.gif"> <input type="text" id="' + $(this).attr('id') + '_input">', 30 'class': 'selector-filter' 31 }).insertBefore(this); 32 33 var select = $(this); 34 var filter = $('#' + $(this).attr('id') + '_input'); 35 36 // Creates cache 37 var cache = []; 38 select.find('option').each(function(index, element){ 39 cache.push({ 40 'text': $(element).text(), 41 'value': $(element).val(), 42 'selected': $(element).attr('selected') 43 }); 44 }); 45 select.data('django-cache', cache); 46 47 // Adjust cache on change 48 select.bind('change.fkfilter', function(evt){ 49 var thisval = $(this).val(); 50 var cache = select.data('django-cache'); 51 52 // for() used over $.each() for performance on long lists 53 for(var i = 0; i < cache.length; i++){ 54 cache[i]['selected'] = (cache[i]['value'] == thisval); 55 } 56 }); 57 58 // Repopulate from cache on keyup 59 filter.bind('keyup.fkfilter', function(evt){ 60 select.children().remove(); 61 var regex = new RegExp(filter.val(), 'gi'); 62 var cache = select.data('django-cache'); 63 64 // for() used over $.each() for performance on long lists 65 for(var i = 0; i < cache.length; i++){ 66 if(cache[i]['text'].match(regex)){ 67 $('<option />', { 68 'selected': cache[i]['selected'], 69 'value': cache[i]['value'], 70 'text': cache[i]['text'] 71 }).appendTo(select); 72 } 73 } 74 }); 75 76 }); 77 }; 78 79 })(django.jQuery); 80 No newline at end of file -
django/contrib/admin/options.py
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 3b6e2b7..1a0e7a6 100644
a b class BaseModelAdmin(object): 61 61 exclude = None 62 62 fieldsets = None 63 63 form = forms.ModelForm 64 fk_filter = () 64 65 filter_vertical = () 65 66 filter_horizontal = () 66 67 radio_fields = {} … … class BaseModelAdmin(object): 157 158 'class': get_ul_class(self.radio_fields[db_field.name]), 158 159 }) 159 160 kwargs['empty_label'] = db_field.blank and _('None') or None 161 elif db_field.name in self.fk_filter: 162 kwargs['widget'] = widgets.FilteredSelectSingle( 163 db_field.verbose_name, (db_field.name in self.filter_vertical)) 160 164 161 165 return db_field.formfield(**kwargs) 162 166 … … class InlineModelAdmin(BaseModelAdmin): 1237 1241 js.append('js/prepopulate.min.js') 1238 1242 if self.filter_vertical or self.filter_horizontal: 1239 1243 js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js']) 1244 if self.fk_filter: 1245 js.append('js/fkfilter.js') 1240 1246 return forms.Media(js=['%s%s' % (settings.ADMIN_MEDIA_PREFIX, url) for url in js]) 1241 1247 media = property(_media) 1242 1248 -
django/contrib/admin/widgets.py
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index 134effc..72e4dcb 100644
a b from django.utils.encoding import force_unicode 15 15 from django.conf import settings 16 16 from django.core.urlresolvers import reverse, NoReverseMatch 17 17 18 19 class FilteredSelectSingle(forms.Select): 20 """ 21 A Select with a JavaScript filter interface. 22 23 Note that the resulting JavaScript assumes that the js18n catalog has been 24 loaded in the page. 25 """ 26 class Media: 27 js = (settings.ADMIN_MEDIA_PREFIX + "js/fkfilter.js",) 28 29 def __init__(self, verbose_name, is_stacked, attrs=None, choices=()): 30 self.verbose_name = verbose_name 31 self.is_stacked = is_stacked 32 super(FilteredSelectSingle, self).__init__(attrs, choices) 33 34 def render(self, name, value, attrs=None, choices=()): 35 if attrs is None: attrs = {} 36 attrs['class'] = 'selectfilter' 37 if self.is_stacked: attrs['class'] += 'stacked' 38 output = [super(FilteredSelectSingle, self).render( 39 name, value, attrs, choices)] 40 output.append(( 41 '<script type="text/javascript">' 42 'django.jQuery(document).ready(function(){' 43 'django.jQuery("#id_%s").fk_filter("%s")' 44 '});' 45 '</script>' 46 ) % (name, self.verbose_name.replace('"', '\\"'),)); 47 return mark_safe(u''.join(output)) 48 49 18 50 class FilteredSelectMultiple(forms.SelectMultiple): 19 51 """ 20 52 A SelectMultiple with a JavaScript filter interface. -
docs/ref/contrib/admin/index.txt
diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 0550576..7c68806 100644
a b Since the Author model only has three fields, ``name``, ``title``, and 247 247 ``birth_date``, the forms resulting from the above declarations will contain 248 248 exactly the same fields. 249 249 250 .. attribute:: ModelAdmin.fk_filter 251 252 Use a nifty unobtrusive JavaScript "filter" interface instead of the usability-challenged ``<select>`` in the admin form. The value is a list of ForeignKey fields that should be displayed as a filter interface. See ``filter_horizontal`` and ``filter_vertical`` to use the same filtering interface on a ManyToManyField. 253 250 254 .. attribute:: ModelAdmin.filter_horizontal 251 255 252 Use a nifty unobtrusive JavaScript "filter" interface instead of the 253 usability-challenged ``<select multiple>`` in the admin form. The value is a 254 list of fields that should be displayed as a horizontal filter interface. See 256 Uses the same JavaScript "filter" interface as ``fk_filter``, replacing the usability-challenged ``<select multiple>`` in the admin form. The value is a 257 list of ManyToMany fields that should be displayed as a horizontal filter interface. See 255 258 ``filter_vertical`` to use a vertical interface. 256 259 257 260 .. attribute:: ModelAdmin.filter_vertical -
tests/regressiontests/modeladmin/models.py
diff --git a/tests/regressiontests/modeladmin/models.py b/tests/regressiontests/modeladmin/models.py index 36ea416..297ea1c 100644
a b ImproperlyConfigured: 'ValidationTestModelAdmin.filter_vertical[0]' must be a Ma 516 516 ... filter_vertical = ("users",) 517 517 >>> validate(ValidationTestModelAdmin, ValidationTestModel) 518 518 519 # fk_filter 520 521 >>> class ValidationTestModelAdmin(ModelAdmin): 522 ... fk_filter = 42 523 >>> validate(ValidationTestModelAdmin, ValidationTestModel) 524 Traceback (most recent call last): 525 ... 526 ImproperlyConfigured: 'ValidationTestModelAdmin.filter_horizontal' must be a list or tuple. 527 528 >>> class ValidationTestModelAdmin(ModelAdmin): 529 ... fk_filter = ("non_existent_field",) 530 >>> validate(ValidationTestModelAdmin, ValidationTestModel) 531 Traceback (most recent call last): 532 ... 533 ImproperlyConfigured: 'ValidationTestModelAdmin.fk_filter' refers to field 'non_existent_field' that is missing from model 'ValidationTestModel'. 534 535 >>> class ValidationTestModelAdmin(ModelAdmin): 536 ... fk_filter = ("name",) 537 >>> validate(ValidationTestModelAdmin, ValidationTestModel) 538 Traceback (most recent call last): 539 ... 540 ImproperlyConfigured: 'ValidationTestModelAdmin.fk_filter[0]' must be a ForeignKey. 541 542 >>> class ValidationTestModelAdmin(ModelAdmin): 543 ... fk_filter = ("band",) 544 >>> validate(ValidationTestModelAdmin, ValidationTestModel) 545 519 546 # filter_horizontal 520 547 521 548 >>> class ValidationTestModelAdmin(ModelAdmin):