Django formsets and date/time fields
Just a quick note on something I’ve run into a couple of times now. The forms created from modelformset_factory (and probably other inline forms) will detect spurious changes if you have DateFields, DateTimeFields, or TimeFields and your default value is a Python datetime object.
[toggle code]
-
class Note(models.Model):
- title = models.CharField('Note Title', max_length=80)
- content = models.TextField('Note', max_length=2000)
- live = models.BooleanField(default=True)
- livedate = models.DateTimeField('Go Live', default=datetime.datetime.now)
- deaddate = models.DateTimeField('Go Dead', default=datetime.datetime.now)
What appears to be happening if I create an automatic formset from this model is that the form will check for changes based on whether any of the data in the submitted form differs from the data in the defaults above. But form submissions are always text, and the default for the two DateTimeFields are Python datetime objects1.
The blank line for adding new items thus always looks like it has changed—I’m guessing, because text never equals a datetime object. The is_valid method will always return invalid (unless they actually did add a new item) because it sees those two fields as having changed. They are “no longer” datetime objects. So it assumes the visitor has attempted to add a new item, but the other required items (title, content) are blank. So it generates errors of “This field is required.” for the title and content fields.
The way I fix this is to make the default be a text rendering of the date/time using strftime.
[toggle code]
- #without setting a string format, formsets will always think the value has changed on form submission
-
def defaultLive():
- now = datetime.datetime.now()
- return now.strftime('%Y-%m-%d %H:%M')
-
def defaultDead():
- then = datetime.datetime.now()+datetime.timedelta(days=3)
- return then.strftime('%Y-%m-%d 09:00')
-
class Note(models.Model):
- title = models.CharField('Note Title', max_length=80)
- content = models.TextField('Note', max_length=2000)
- live = models.BooleanField(default=True)
- livedate = models.DateTimeField('Go Live', default=defaultLive)
- deaddate = models.DateTimeField('Go Dead', default=defaultDead)
This issue appears to be unique to Date/Time fields. For example, the BooleanField above works fine, despite the form not sending back a Python boolean. I suspect it’s just a matter of being a lot easier to convert a text value to a boolean than to convert it to a Date/Time field.
It might also be that the comparison is between text versions, but that the conversion to text isn’t the same between the automatic form and the default value check. I didn’t try very hard, but I was unable to find a text version that would match and not be flagged as a change.
↑
More Django
- Converting an existing Django model to Django-MPTT
- Using a SQL database to mimic a filesystem will, eventually, create bottlenecks when it comes to traversing the filesystem. One solution is modified preordered tree traversal, which saves the tree structure in an easily-used manner inside the model.
- Two search bookmarklets for Django
- Bookmarklets—JavaScript code in a bookmark—can make working with big Django databases much easier.
- Fixing Django’s feed generator without hacking Django
- It looks like it’s going to be a while before the RSS feed generator in Django is going to get fixed, so I looked into subclassing as a way of getting a working guid in my Django RSS feeds.
- ModelForms and FormViews
- This is just a notice because when I did a search, nothing came up. Don’t use ModelForm with FormView, use UpdateView instead.
- Django: fix_ampersands and abbreviations
- The fix_ampersands filter will miss some cases where ampersands need to be replaced.
- 29 more pages with the topic Django, and other related pages
What version or revision of Django do you run?
Jannis Leidel at 3:21 p.m. December 29th, 2009
Av4Hr
At the time I wrote that I was using 1.1.1 on that server (still am), though I'm looking forward to 1.2.
capvideo at 8:54 p.m. December 29th, 2009
tVAhq
yes, this will be fixed in version 1.2
there was a changeset this week, which fixed this bug -> http://code.djangoproject.com/changeset/12029
Bernd at 9:44 a.m. December 31st, 2009
Ss3Dp