Ticket #8103: 8103.diff

File 8103.diff, 7.8 KB (added by SmileyChris, 7 years ago)
  • django/forms/widgets.py

     
    3535            media_attrs = media.__dict__
    3636        else:
    3737            media_attrs = kwargs
    38            
     38
    3939        self._css = {}
    4040        self._js = []
    41        
     41
    4242        for name in MEDIA_TYPES:
    4343            getattr(self, 'add_' + name)(media_attrs.get(name, None))
    4444
    4545        # Any leftover attributes must be invalid.
    4646        # if media_attrs != {}:
    4747        #     raise TypeError, "'class Media' has invalid attribute(s): %s" % ','.join(media_attrs.keys())
    48        
     48
    4949    def __unicode__(self):
    5050        return self.render()
    51        
     51
    5252    def render(self):
    5353        return u'\n'.join(chain(*[getattr(self, 'render_' + name)() for name in MEDIA_TYPES]))
    54        
     54
    5555    def render_js(self):
    5656        return [u'<script type="text/javascript" src="%s"></script>' % self.absolute_path(path) for path in self._js]
    57        
     57
    5858    def render_css(self):
    5959        # To keep rendering order consistent, we can't just iterate over items().
    6060        # We need to sort the keys, and iterate over the sorted list.
    6161        media = self._css.keys()
    6262        media.sort()
    6363        return chain(*[
    64             [u'<link href="%s" type="text/css" media="%s" rel="stylesheet" />' % (self.absolute_path(path), medium) 
    65                     for path in self._css[medium]] 
     64            [u'<link href="%s" type="text/css" media="%s" rel="stylesheet" />' % (self.absolute_path(path), medium)
     65                    for path in self._css[medium]]
    6666                for medium in media])
    67        
     67
    6868    def absolute_path(self, path):
    6969        if path.startswith(u'http://') or path.startswith(u'https://') or path.startswith(u'/'):
    7070            return path
     
    7777        raise KeyError('Unknown media type "%s"' % name)
    7878
    7979    def add_js(self, data):
    80         if data:   
     80        if data:
    8181            self._js.extend([path for path in data if path not in self._js])
    82            
     82
    8383    def add_css(self, data):
    8484        if data:
    8585            for medium, paths in data.items():
     
    9999            base = super(cls, self).media
    100100        else:
    101101            base = Media()
    102        
    103         # Get the media definition for this class   
     102
     103        # Get the media definition for this class
    104104        definition = getattr(cls, 'Media', None)
    105105        if definition:
    106106            extend = getattr(definition, 'extend', True)
     
    117117        else:
    118118            return base
    119119    return property(_media)
    120    
     120
    121121class MediaDefiningClass(type):
    122122    "Metaclass for classes that can have media definitions"
    123     def __new__(cls, name, bases, attrs):           
     123    def __new__(cls, name, bases, attrs):
    124124        new_class = super(MediaDefiningClass, cls).__new__(cls, name, bases,
    125125                                                           attrs)
    126126        if 'media' not in attrs:
    127127            new_class.media = media_property(new_class)
    128128        return new_class
    129        
     129
    130130class Widget(object):
    131131    __metaclass__ = MediaDefiningClass
    132132    is_hidden = False          # Determines whether this corresponds to an <input type="hidden">.
     
    264264    def value_from_datadict(self, data, files, name):
    265265        "File widgets take data from FILES, not POST"
    266266        return files.get(name, None)
    267    
     267
    268268    def _has_changed(self, initial, data):
    269269        if data is None:
    270270            return False
     
    334334        return bool(initial) != bool(data)
    335335
    336336class Select(Widget):
     337    allow_multiple_selected = False
     338
    337339    def __init__(self, attrs=None, choices=()):
    338340        super(Select, self).__init__(attrs)
    339341        # choices can be any iterable, but we may need to render this widget
     
    354356    def render_options(self, choices, selected_choices):
    355357        def render_option(option_value, option_label):
    356358            option_value = force_unicode(option_value)
    357             selected_html = (option_value in selected_choices) and u' selected="selected"' or ''
     359            if not getattr(render_option, 'selected', None)\
     360                            and option_value in selected_choices:
     361                if not self.allow_multiple_selected:
     362                    # Only allow for a single selection.
     363                    render_option.selected = True
     364                selected_html = u' selected="selected"'
     365            else:
     366                selected_html = ''
    358367            return u'<option value="%s"%s>%s</option>' % (
    359368                escape(option_value), selected_html,
    360369                conditional_escape(force_unicode(option_label)))
     
    396405        return bool(initial) != bool(data)
    397406
    398407class SelectMultiple(Select):
     408    allow_multiple_selected = True
     409
    399410    def render(self, name, value, attrs=None, choices=()):
    400411        if value is None: value = []
    401412        final_attrs = self.build_attrs(attrs, name=name)
     
    410421        if isinstance(data, MultiValueDict):
    411422            return data.getlist(name)
    412423        return data.get(name, None)
    413    
     424
    414425    def _has_changed(self, initial, data):
    415426        if initial is None:
    416427            initial = []
     
    527538                label_for = u' for="%s"' % final_attrs['id']
    528539            else:
    529540                label_for = ''
    530                
     541
    531542            cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
    532543            option_value = force_unicode(option_value)
    533544            rendered_cb = cb.render(name, option_value)
     
    601612
    602613    def value_from_datadict(self, data, files, name):
    603614        return [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)]
    604    
     615
    605616    def _has_changed(self, initial, data):
    606617        if initial is None:
    607618            initial = [u'' for x in range(0, len(data))]
     
    637648            media = media + w.media
    638649        return media
    639650    media = property(_get_media)
    640    
     651
    641652class SplitDateTimeWidget(MultiWidget):
    642653    """
    643654    A Widget that splits datetime input into two <input type="text"> boxes.
  • tests/regressiontests/forms/widgets.py

     
    458458<option value="4">4</option>
    459459</select>
    460460
     461Only one option can be selected:
     462>>> print w.render('choices', 0, choices=(('0', 'extra'),))
     463<select name="choices">
     464<option value="0" selected="selected">0</option>
     465<option value="1">1</option>
     466<option value="2">2</option>
     467<option value="3">3</option>
     468<option value="4">4</option>
     469<option value="0">extra</option>
     470</select>
     471
     472Ensure that it still selects the first element next time round:
     473>>> print w.render('choices', 0, choices=(('0', 'extra'),))
     474<select name="choices">
     475<option value="0" selected="selected">0</option>
     476<option value="1">1</option>
     477<option value="2">2</option>
     478<option value="3">3</option>
     479<option value="4">4</option>
     480<option value="0">extra</option>
     481</select>
     482
    461483Choices can be nested one level in order to create HTML optgroups:
    462484>>> w.choices=(('outer1', 'Outer 1'), ('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2'))))
    463485>>> print w.render('nestchoice', None)
     
    655677>>> w._has_changed([1, 2], [u'1', u'3'])
    656678True
    657679
     680Multiple options (with the same value) can be selected:
     681>>> print w.render('choices', [1], choices=(('1', 'extra'),))
     682<select multiple="multiple" name="choices">
     683<option value="1" selected="selected">1</option>
     684<option value="2">2</option>
     685<option value="3">3</option>
     686<option value="1" selected="selected">extra</option>
     687</select>
     688
    658689# Choices can be nested one level in order to create HTML optgroups:
    659690>>> w.choices = (('outer1', 'Outer 1'), ('Group "1"', (('inner1', 'Inner 1'), ('inner2', 'Inner 2'))))
    660691>>> print w.render('nestchoice', None)
Back to Top