This is an adaption of
django.forms.extras.widgets.SelectDateWidget
which has no day dropdown - it still produces a date but with the day set to 1.
Example use
class myForm(forms.Form):
# ...
date = forms.DateField(
required=False,
widget=MonthYearWidget(years=xrange(2004,2010))
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | import datetime
import re
from django.forms.widgets import Widget, Select
from django.utils.dates import MONTHS
from django.utils.safestring import mark_safe
__all__ = ('MonthYearWidget',)
RE_DATE = re.compile(r'(\d{4})-(\d\d?)-(\d\d?)$')
class MonthYearWidget(Widget):
"""
A Widget that splits date input into two <select> boxes for month and year,
with 'day' defaulting to the first of the month.
Based on SelectDateWidget, in
django/trunk/django/forms/extras/widgets.py
"""
none_value = (0, '---')
month_field = '%s_month'
year_field = '%s_year'
def __init__(self, attrs=None, years=None, required=True):
# years is an optional list/tuple of years to use in the "year" select box.
self.attrs = attrs or {}
self.required = required
if years:
self.years = years
else:
this_year = datetime.date.today().year
self.years = range(this_year, this_year+10)
def render(self, name, value, attrs=None):
try:
year_val, month_val = value.year, value.month
except AttributeError:
year_val = month_val = None
if isinstance(value, basestring):
match = RE_DATE.match(value)
if match:
year_val, month_val, day_val = [int(v) for v in match.groups()]
output = []
if 'id' in self.attrs:
id_ = self.attrs['id']
else:
id_ = 'id_%s' % name
month_choices = MONTHS.items()
if not (self.required and value):
month_choices.append(self.none_value)
month_choices.sort()
local_attrs = self.build_attrs(id=self.month_field % id_)
s = Select(choices=month_choices)
select_html = s.render(self.month_field % name, month_val, local_attrs)
output.append(select_html)
year_choices = [(i, i) for i in self.years]
if not (self.required and value):
year_choices.insert(0, self.none_value)
local_attrs['id'] = self.year_field % id_
s = Select(choices=year_choices)
select_html = s.render(self.year_field % name, year_val, local_attrs)
output.append(select_html)
return mark_safe(u'\n'.join(output))
def id_for_label(self, id_):
return '%s_month' % id_
id_for_label = classmethod(id_for_label)
def value_from_datadict(self, data, files, name):
y = data.get(self.year_field % name)
m = data.get(self.month_field % name)
if y == m == "0":
return None
if y and m:
return '%s-%s-%s' % (y, m, 1)
return data.get(name, None)
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 8 months, 4 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 9 months ago
- Serializer factory with Django Rest Framework by julio 1 year, 3 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 4 months ago
- Help text hyperlinks by sa2812 1 year, 5 months ago
Comments
There's a bug in this snippet. Line 56 should read:
And line 65 should read:
Otherwise you get strange behavior when only one of them is filled in.
#
The second last line
will only work if you didn't customize your DATE_FORMAT/DATE_INPUT_FORMATS (the former is usually the first item of the latter, i.e. the "preferred" input format). In order for this to always work, add
at the top, and replace that second last line with:
P.S.: django.utils.formats.localize() is for rendering output, not input, and expects a setting in the format of DATE_FORMAT, i.e. without percentage signs... That's why I'm using time.strptime()
P.P.S.: Theoretically, the int() conversion could be try'd but the widget doesn't allow free text input so I find it ok to leave it out
#
"That's why I'm using time.strptime()" - uhm, make that "date.strftime()" :-)
#
I've added a Python 3 version here https://djangosnippets.org/snippets/10522/ (it was too long to put in the comments).
#
Please login first before commenting.