Ticket #16575: date_format-select_date_widget.diff

File date_format-select_date_widget.diff, 8.2 KB (added by calebs, 3 years ago)
  • django/forms/extras/widgets.py

    diff --git a/django/forms/extras/widgets.py b/django/forms/extras/widgets.py
    index 4fb4869..efac85f 100644
    a b __all__ = ('SelectDateWidget',) 
    1616
    1717RE_DATE = re.compile(r'(\d{4})-(\d\d?)-(\d\d?)$')
    1818
    19 def _parse_date_fmt():
    20     fmt = get_format('DATE_FORMAT')
    21     escaped = False
    22     output = []
    23     for char in fmt:
    24         if escaped:
    25             escaped = False
    26         elif char == '\\':
    27             escaped = True
    28         elif char in 'Yy':
    29             output.append('year')
    30             #if not self.first_select: self.first_select = 'year'
    31         elif char in 'bEFMmNn':
    32             output.append('month')
    33             #if not self.first_select: self.first_select = 'month'
    34         elif char in 'dj':
    35             output.append('day')
    36             #if not self.first_select: self.first_select = 'day'
    37     return output
     19
     20def _parse_format(fmt=None):
     21    """
     22    Sort the order of date selectors according to the date_format attribute.
     23    If date_format is not given, default to get_format('DATE_FORMAT')[0]
     24    """
     25    if not fmt:
     26        return _parse_format(re.subn(r'\\.', '',
     27                             get_format('DATE_INPUT_FORMATS')[0])[0])
     28    def pos(s):
     29        match = re.search('[' + s + ']', fmt)
     30        return match.start() + 1 if match else 0
     31    dic = {'day': pos('dj'), 'month': pos('bEFMmNn'), 'year': pos('yY')}
     32    return sorted(filter(None, dic.keys()), key=dic.get)
     33
    3834
    3935class SelectDateWidget(Widget):
    4036    """
    class SelectDateWidget(Widget): 
    4339    This also serves as an example of a Widget that has more than one HTML
    4440    element and hence implements value_from_datadict.
    4541    """
    46     none_value = (0, '---')
    47     month_field = '%s_month'
    48     day_field = '%s_day'
    49     year_field = '%s_year'
     42    names = ('day', 'month', 'year',)
     43    none_values_dafault = '---'
     44    field_values = ['%s_' + n for n in names]
     45    fields = dict(zip(names, field_values))
     46    day_field, month_field, year_field = field_values
    5047
    51     def __init__(self, attrs=None, years=None, required=True):
    52         # years is an optional list/tuple of years to use in the "year" select box.
     48    def __init__(self, attrs=None, years=None, required=True,
     49            date_format=None, none_values=None):
     50        # years is an optional list/tuple of years
     51        # to use in the "year" select box.
    5352        self.attrs = attrs or {}
    5453        self.required = required
    55         if years:
    56             self.years = years
    57         else:
     54        if not years:
    5855            this_year = datetime.date.today().year
    59             self.years = range(this_year, this_year+10)
     56            years = range(this_year, this_year + 10)
     57        self.years = years
     58        self.date_format = _parse_format(date_format)
     59        self.none_values = none_values \
     60            or dict([(i, self.none_values_dafault) for i in 'dmy'])
    6061
    6162    def render(self, name, value, attrs=None):
     63        get_names_dict = lambda l: dict(zip(self.names, l))
     64
    6265        try:
    63             year_val, month_val, day_val = value.year, value.month, value.day
     66            values_list = [getattr(value, n) for n in self.names]
    6467        except AttributeError:
    65             year_val = month_val = day_val = None
     68            values_list = []
    6669            if isinstance(value, basestring):
    6770                if settings.USE_L10N:
    6871                    try:
    6972                        input_format = get_format('DATE_INPUT_FORMATS')[0]
    7073                        v = datetime.datetime.strptime(value, input_format)
    71                         year_val, month_val, day_val = v.year, v.month, v.day
     74                        values_list = [getattr(v, n) for n in self.names]
    7275                    except ValueError:
    7376                        pass
    7477                else:
    7578                    match = RE_DATE.match(value)
    7679                    if match:
    77                         year_val, month_val, day_val = [int(v) for v in match.groups()]
    78         choices = [(i, i) for i in self.years]
    79         year_html = self.create_select(name, self.year_field, value, year_val, choices)
    80         choices = MONTHS.items()
    81         month_html = self.create_select(name, self.month_field, value, month_val, choices)
    82         choices = [(i, i) for i in range(1, 32)]
    83         day_html = self.create_select(name, self.day_field, value, day_val,  choices)
    84 
    85         output = []
    86         for field in _parse_date_fmt():
    87             if field == 'year':
    88                 output.append(year_html)
    89             elif field == 'month':
    90                 output.append(month_html)
    91             elif field == 'day':
    92                 output.append(day_html)
     80                        values_list = (int(v) for v in match.groups())
     81        values = get_names_dict(values_list)
     82
     83        choices = get_names_dict([
     84            [(i, i) for i in range(1, 32)],
     85            MONTHS.items(),
     86            [(i, i) for i in self.years],
     87        ])
     88        create_select_short = lambda n: self.create_select(
     89            name, self.fields[n], value, values.get(n), choices[n], n
     90        )
     91
     92        htmls = get_names_dict([create_select_short(n) for n in self.names])
     93        date_format = self.date_format or self._parse_format()
     94        output = [htmls[field] for field in date_format]
    9395        return mark_safe(u'\n'.join(output))
    9496
    95     def id_for_label(self, id_):
    96         first_select = None
    97         field_list = _parse_date_fmt()
     97    def id_for_label(cls, id_):
     98        field_list = _parse_format()
    9899        if field_list:
    99100            first_select = field_list[0]
    100         if first_select is not None:
    101             return '%s_%s' % (id_, first_select)
    102101        else:
    103             return '%s_month' % id_
     102            first_select = 'month'
     103        return '%s_%s' % (id_, first_select)
    104104    id_for_label = classmethod(id_for_label)
    105105
    106106    def value_from_datadict(self, data, files, name):
    107         y = data.get(self.year_field % name)
    108         m = data.get(self.month_field % name)
    109         d = data.get(self.day_field % name)
     107        d, m, y = (data.get(self.fields[n] % name) for n in self.names)
    110108        if y == m == d == "0":
    111109            return None
    112         if y and m and d:
    113             if settings.USE_L10N:
    114                 input_format = get_format('DATE_INPUT_FORMATS')[0]
    115                 try:
    116                     date_value = datetime.date(int(y), int(m), int(d))
    117                 except ValueError:
    118                     return '%s-%s-%s' % (y, m, d)
    119                 else:
    120                     date_value = datetime_safe.new_date(date_value)
    121                     return date_value.strftime(input_format)
    122             else:
    123                 return '%s-%s-%s' % (y, m, d)
    124         return data.get(name, None)
     110        if not (y and m and d):
     111            return data.get(name)
     112        if not settings.USE_L10N:
     113            return '%s-%s-%s' % (y, m, d)
     114        input_format = get_format('DATE_INPUT_FORMATS')[0]
     115        try:
     116            date_value = datetime.date(int(y), int(m), int(d))
     117            date_value = datetime_safe.new_date(date_value)
     118            return date_value.strftime(input_format)
     119        except ValueError:
     120            return '%s-%s-%s' % (y, m, d)
    125121
    126     def create_select(self, name, field, value, val, choices):
     122    def create_select(self, name, field, value, val, choices, date_part='d'):
    127123        if 'id' in self.attrs:
    128124            id_ = self.attrs['id']
    129125        else:
    130126            id_ = 'id_%s' % name
    131127        if not (self.required and val):
    132             choices.insert(0, self.none_value)
     128            choices.insert(0, (0, self.none_values[date_part[0]]))
    133129        local_attrs = self.build_attrs(id=field % id_)
    134130        s = Select(choices=choices)
    135131        select_html = s.render(field % name, val, local_attrs)
    136132        return select_html
    137133
    138134    def _has_changed(self, initial, data):
    139         input_format = get_format('DATE_INPUT_FORMATS')[0]
    140         data = datetime_safe.datetime.strptime(data, input_format).date()
    141         return super(SelectDateWidget, self)._has_changed(initial, data)
    142  No newline at end of file
     135        try:
     136            input_format = get_format('DATE_INPUT_FORMATS')[0]
     137            data = datetime_safe.datetime.strptime(data, input_format).date()
     138        except (TypeError, ValueError):
     139            pass
     140        return super(SelectDateWidget, self)._has_changed(initial, data)
Back to Top