Django

Code

Ticket #4412: 4412.2.diff

File 4412.2.diff, 11.9 kB (added by SmileyChris, 3 months ago)

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

  • django/db/models/base.py

    old new  
    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

    old new  
    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

    old new  
    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

    old new  
    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

    old new  
    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

    old new  
    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

    old new  
    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