Date functions (validation, display, etc.) For partial dates in Django

I am trying to store dates in my database, which are often missing a month and a day. My current approach (there seem to be many different ways to do this) is to use three fields for this:

dob_year dob_month dob_day 

I would like to get as many benefits of DateFields as possible. The most important thing I can think of right now is checking. Really:

 2010 2010,02 2010,02,28 

Invalid:

 2010,02,30 

There is also the problem of converting ints to a human-readable form in templates. It's great to say:

 <p>{{my_date|date:"%Y"}}</p> 

But at the same time, I will need to do something very strange, because I will need support for partial dates and regular ones. I think I can accomplish this with the @property method, but I haven't figured it out yet.

I am sure that there are other amenities that I also give up (for example, date widgets in the admin panel). Ideas are also welcome here, since I know that partial dates are a common problem, but I'm mostly interested in checking.

Refresh . A friend on Twitter pointed out that using three fields for this creates terrible date requests . Do not use the three fields for partial dates unless you want to think about how to handle requests of the type β€œfrom July 2011 to June 2012”.

+6
source share
1 answer

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.

+11
source

Source: https://habr.com/ru/post/986852/


All Articles