Keeping dates in a database as text is bad practice. It is not portable, not django query api friendly, not index friendly.
You can store the entire date in the database, not even the whole date is needed. For example, Oracle stores date and time in dates , even you only need a date.
You can then use DateField + " ChoiceField " to store the partial date and related part information. Example: 2015-02-01 is truncated at the month level. 2015-02-28 Full date. 2015-01-01 is truncated at the year level. Code:
class Item(models.Model): PARTIAL_YEAR='%Y' PARTIAL_MONTH='%Y-%m' PARTIAL_DAY='%Y-%m-%d' PARTIAL_CHOICES = ( (PARTIAL_YEAR, 'Year'), (PARTIAL_MONTH, 'Month'), (PARTIAL_DAY, 'Day'), ) partial_date_date = models.DateField() partial_date_part = models.CharField('Date part', choices=PARTIAL_CHOICES, max_length=10, )
Confirm . Both fields will be checked by each own widget. You can add a clean form ( Cleaning and checking fields that depend on each other ) or a clean, clean verification level.
Request . It is easy to create conditional queries using Q objects :
q_is_year = q( partial_date_part = Item.PARTIAL_YEAR ) q_by_year = q( partial_date_date__year = 2015 ) q_is_month = q( partial_date_part = Item.PARTIAL_MONTH ) q_by_month = q( partial_date_date__year = 2105 ) q_by_month &= q( partial_date_date__month = 2 ) qs = Item.objects.filter( q_is_year&q_by_year | q_is_month&q_by_month )
Display mapping . To do in the template:
<p>{{item.partial_date_date|date:item.partial_date_part}}</p>
Rendering form . To visualize form elements, you can use JavaScript to change the user interface and help the user with data entry:
date type: (*) Year ( ) Month ( ) Day date: [ change form widget dynamically ]
You can send three widget controls to form and show only one at a time. Change in visibility when changing the choice of radio date type. I am using MultiWidget .
EDITED on August 13, 2015 with all the sample code:
models.py
from django.db import models from datetime import date class Item(models.Model): PARTIAL_YEAR='%Y' PARTIAL_MONTH='%Y-%m' PARTIAL_DAY='%Y-%m-%d' PARTIAL_CHOICES = ( (PARTIAL_YEAR, 'Year'), (PARTIAL_MONTH, 'Month'), (PARTIAL_DAY, 'Day'), ) partial_date_part = models.CharField('Date part', choices=PARTIAL_CHOICES, max_length=10, ) partial_date_date = models.DateField() some_comment = models.CharField('Comment', max_length=100, ) def save(self, *args, **kwargs): if self.partial_date_part==self.PARTIAL_YEAR: self.partial_date_date = date( self.partial_date_date.year, 1, 1 ) elif self.partial_date_part==self.PARTIAL_MONTH: self.partial_date_date = date( self.partial_date_date.year, self.partial_date_date.month, 1 ) super(Item, self).save(*args, **kwargs)
forms.py
from django import forms from django.forms import widgets from datetime import date class DateSelectorWidget(widgets.MultiWidget): def __init__(self, attrs=None): days = [(d, d) for d in range(1,32)] months = [(m, m) for m in range(1,13)] years = [(year, year) for year in (2011, 2012, 2013)] _widgets = ( widgets.Select(attrs=attrs, choices=days), widgets.Select(attrs=attrs, choices=months), widgets.Select(attrs=attrs, choices=years), ) super(DateSelectorWidget, self).__init__(_widgets, attrs) def decompress(self, value): if value: return [value.day, value.month, value.year] return [None, None, None] def format_output(self, rendered_widgets): return ''.join(rendered_widgets) def value_from_datadict(self, data, files, name): datelist = [ widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)] D = date( day=int(datelist[0]),month=int(datelist[1]),year=int(datelist[2]), ) return D class ItemForm(forms.Form): partial_date_part = forms.CharField(widget=forms.RadioSelect) partial_date_date = DateSelectorWidget( )
view.py
from django.http import HttpResponseRedirect from django.views.generic import View from models import Item from django.forms.models import modelform_factory from .forms import DateSelectorWidget from django import forms from django.forms import widgets class MyDatAppView(View): form_class = modelform_factory(Item , exclude=[], widgets={ 'partial_date_date': DateSelectorWidget() ,}) initial = {'some_comment': '-*-', } template_name = 'form.html' def get(self, request, *args, **kwargs): form = self.form_class(initial=self.initial) return render(request, self.template_name, {'form': form}) def post(self, request, *args, **kwargs): form = self.form_class(request.POST) if form.is_valid(): m=form.save() return HttpResponseRedirect('/') return render(request, self.template_name, {'form': form})
You must add javascript to the template to hide / show the details fields of the date when the selected changes to details.