Ticket #4412: 4412.2.diff

File 4412.2.diff, 11.9 KB (added by Chris Beaven, 10 years ago)

Updated patch, fixed to work with multiple select and old admin

  • django/db/models/base.py

     
    429429
    430430    def _get_FIELD_display(self, field):
    431431        value = getattr(self, field.attname)
    432         return force_unicode(dict(field.choices).get(value, value), strings_only=True)
     432        flatchoices = []
     433        for choice in field.choices:
     434            if type(choice[1]) in (list, tuple):
     435                flatchoices.extend(choice[1])
     436            else:
     437                flatchoices.append(choice)
     438        return force_unicode(dict(flatchoices).get(value, value), strings_only=True)
    433439
    434440    def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs):
    435441        op = is_next and 'gt' or 'lt'
  • django/db/models/fields/__init__.py

     
    299299            else:
    300300                field_objs = [oldforms.SelectField]
    301301
    302             params['choices'] = self.get_choices_default()
     302            flatchoices = []
     303            for choice in self.get_choices_default():
     304                if type(choice[1]) in (list, tuple):
     305                    flatchoices.extend(choice[1])
     306                else:
     307                    flatchoices.append(choice)
     308
     309            params['choices'] = flatchoices
    303310        else:
    304311            field_objs = self.get_manipulator_field_objs()
    305312        return (field_objs, params)
  • django/newforms/fields.py

     
    615615        value = smart_unicode(value)
    616616        if value == u'':
    617617            return value
    618         valid_values = set([smart_unicode(k) for k, v in self.choices])
     618        valid_values = []
     619        for k, v in self.choices:
     620            if type(v) in (tuple, list):
     621                valid_values.extend([k2[0] for k2 in v])
     622            else:
     623                valid_values.append(k)
     624        valid_values = set([smart_unicode(v) for v in valid_values])       
    619625        if value not in valid_values:
    620626            raise ValidationError(self.error_messages['invalid_choice'] % {'value': value})
    621627        return value
     
    640646            raise ValidationError(self.error_messages['invalid_list'])
    641647        new_value = [smart_unicode(val) for val in value]
    642648        # Validate that each value in the value list is in self.choices.
    643         valid_values = set([smart_unicode(k) for k, v in self.choices])
     649        valid_values = []
     650        for k, v in self.choices:
     651            if type(v) in (tuple, list):
     652                valid_values.extend([k2[0] for k2 in v])
     653            else:
     654                valid_values.append(k)
     655        valid_values = set([smart_unicode(v) for v in valid_values])       
    644656        for val in new_value:
    645657            if val not in valid_values:
    646658                raise ValidationError(self.error_messages['invalid_choice'] % {'value': val})
  • django/newforms/widgets.py

     
    212212        if value is None: value = ''
    213213        final_attrs = self.build_attrs(attrs, name=name)
    214214        output = [u'<select%s>' % flatatt(final_attrs)]
    215         # Normalize to string.
    216         str_value = force_unicode(value)
    217         for option_value, option_label in chain(self.choices, choices):
    218             option_value = force_unicode(option_value)
    219             selected_html = (option_value == str_value) and u' selected="selected"' or ''
    220             output.append(u'<option value="%s"%s>%s</option>' % (
    221                     escape(option_value), selected_html,
    222                     conditional_escape(force_unicode(option_label))))
    223         output.append(u'</select>')
     215        options = self.render_options(choices, [value])
     216        if options:
     217            output.append(options)
     218        output.append('</select>')
    224219        return mark_safe(u'\n'.join(output))
    225220
     221    def render_options(self, choices, selected_choices):
     222        def render_option(option_value, option_label):
     223            option_value = force_unicode(option_value)
     224            selected_html = (option_value in selected_choices) and u' selected="selected"' or ''
     225            return u'<option value="%s"%s>%s</option>' % (
     226                escape(option_value), selected_html,
     227                conditional_escape(force_unicode(option_label)))
     228        # Normalize to strings.
     229        selected_choices = set([force_unicode(v) for v in selected_choices])
     230        output = []
     231        for option_value, option_label in chain(self.choices, choices):
     232            if isinstance(option_label, (list, tuple)):
     233                output.append(u'<optgroup label="%s">' % escape(force_unicode(option_value)))
     234                for option in option_label:
     235                    output.append(render_option(*option))
     236                output.append(u'</optgroup>')
     237            else:
     238                output.append(render_option(option_value, option_label))
     239        return u'\n'.join(output)
     240
    226241class NullBooleanSelect(Select):
    227242    """
    228243    A Select Widget intended to be used with NullBooleanField.
     
    242257        value = data.get(name, None)
    243258        return {u'2': True, u'3': False, True: True, False: False}.get(value, None)
    244259
    245 class SelectMultiple(Widget):
    246     def __init__(self, attrs=None, choices=()):
    247         super(SelectMultiple, self).__init__(attrs)
    248         # choices can be any iterable
    249         self.choices = choices
    250 
     260class SelectMultiple(Select):
    251261    def render(self, name, value, attrs=None, choices=()):
    252262        if value is None: value = []
    253263        final_attrs = self.build_attrs(attrs, name=name)
    254264        output = [u'<select multiple="multiple"%s>' % flatatt(final_attrs)]
    255         str_values = set([force_unicode(v) for v in value]) # Normalize to strings.
    256         for option_value, option_label in chain(self.choices, choices):
    257             option_value = force_unicode(option_value)
    258             selected_html = (option_value in str_values) and ' selected="selected"' or ''
    259             output.append(u'<option value="%s"%s>%s</option>' % (
    260                     escape(option_value), selected_html,
    261                     conditional_escape(force_unicode(option_label))))
    262         output.append(u'</select>')
     265        options = self.render_options(choices, value)
     266        if options:
     267            output.append(options)
     268        output.append('</select>')
    263269        return mark_safe(u'\n'.join(output))
    264270
    265271    def value_from_datadict(self, data, files, name):
  • tests/regressiontests/forms/widgets.py

     
    419419<option value="4">4</option>
    420420</select>
    421421
     422Choices can be nested one level in order to create HTML optgroups:
     423>>> w.choices=(('outer1', 'Outer 1'), ('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2'))))
     424>>> print w.render('nestchoice', None)
     425<select name="nestchoice">
     426<option value="outer1">Outer 1</option>
     427<optgroup label="Group &quot;1&quot;">
     428<option value="inner1">Inner 1</option>
     429<option value="inner2">Inner 2</option>
     430</optgroup>
     431</select>
     432>>> print w.render('nestchoice', 'outer1')
     433<select name="nestchoice">
     434<option value="outer1" selected="selected">Outer 1</option>
     435<optgroup label="Group &quot;1&quot;">
     436<option value="inner1">Inner 1</option>
     437<option value="inner2">Inner 2</option>
     438</optgroup>
     439</select>
     440>>> print w.render('nestchoice', 'inner1')
     441<select name="nestchoice">
     442<option value="outer1">Outer 1</option>
     443<optgroup label="Group &quot;1&quot;">
     444<option value="inner1" selected="selected">Inner 1</option>
     445<option value="inner2">Inner 2</option>
     446</optgroup>
     447</select>
     448
    422449# NullBooleanSelect Widget ####################################################
    423450
    424451>>> w = NullBooleanSelect()
     
    573600>>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
    574601u'<select multiple="multiple" name="nums">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>'
    575602
     603# Choices can be nested one level in order to create HTML optgroups:
     604>>> w.choices = (('outer1', 'Outer 1'), ('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2'))))
     605>>> print w.render('nestchoice', None)
     606<select multiple="multiple" name="nestchoice">
     607<option value="outer1">Outer 1</option>
     608<optgroup label="Group &quot;1&quot;">
     609<option value="inner1">Inner 1</option>
     610<option value="inner2">Inner 2</option>
     611</optgroup>
     612</select>
     613>>> print w.render('nestchoice', ['outer1'])
     614<select multiple="multiple" name="nestchoice">
     615<option value="outer1" selected="selected">Outer 1</option>
     616<optgroup label="Group &quot;1&quot;">
     617<option value="inner1">Inner 1</option>
     618<option value="inner2">Inner 2</option>
     619</optgroup>
     620</select>
     621>>> print w.render('nestchoice', ['inner1'])
     622<select multiple="multiple" name="nestchoice">
     623<option value="outer1">Outer 1</option>
     624<optgroup label="Group &quot;1&quot;">
     625<option value="inner1" selected="selected">Inner 1</option>
     626<option value="inner2">Inner 2</option>
     627</optgroup>
     628</select>
     629>>> print w.render('nestchoice', ['outer1', 'inner2'])
     630<select multiple="multiple" name="nestchoice">
     631<option value="outer1" selected="selected">Outer 1</option>
     632<optgroup label="Group &quot;1&quot;">
     633<option value="inner1">Inner 1</option>
     634<option value="inner2" selected="selected">Inner 2</option>
     635</optgroup>
     636</select>
     637
    576638# RadioSelect Widget ##########################################################
    577639
    578640>>> w = RadioSelect()
  • docs/model-api.txt

     
    568568    class Foo(models.Model):
    569569        gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
    570570
     571You can also collect your available choices into named groups, which will
     572be used for display purposes::
     573
     574    MEDIA_CHOICES = (
     575        ('Audio', (
     576                ('vinyl', 'Vinyl'),
     577                ('cd', 'CD'),
     578            )
     579        ),
     580        ('Video', (
     581                ('vhs', 'VHS Tape'),
     582                ('dvd', 'DVD'),
     583            )
     584        ),
     585        ('unknown', 'Unknown'),
     586    )
     587
     588The first element in each tuple is the name to apply to the group. The
     589second element is an iterable of 2-tuples, with each 2-tuple containing
     590a value and a human-readable name for an option. Grouped options may be
     591combined with ungrouped options within a single list (such as the
     592`unknown` option in this example).
     593
    571594For each model field that has ``choices`` set, Django will add a method to
    572595retrieve the human-readable name for the field's current value. See
    573596`get_FOO_display`_ in the database API documentation.
  • docs/newforms.txt

     
    12331233    * Error message keys: ``required``, ``invalid_choice``
    12341234
    12351235Takes one extra argument, ``choices``, which is an iterable (e.g., a list or
    1236 tuple) of 2-tuples to use as choices for this field.
     1236tuple) of 2-tuples to use as choices for this field. This argument accepts
     1237the same formats as the ``choices`` argument to a model field. See the
     1238`model API documentation on choices`_ for more details.
    12371239
     1240.. _model API documentation on choices: ../model-api#choices
     1241
    12381242``DateField``
    12391243~~~~~~~~~~~~~
    12401244
Back to Top