Add validation for model durations

It was previously possible to accidentally enter obviously wrong values
for start/end dates. Add some basic validation to catch input errors
when creating or editing various models (when not using a timer).
This commit is contained in:
Bob Thomas 2017-11-01 12:44:07 -04:00
parent 40f8a511ba
commit 1eeba2398d
2 changed files with 49 additions and 0 deletions

View File

@ -1,9 +1,28 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from datetime import timedelta
from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.template.defaultfilters import slugify from django.template.defaultfilters import slugify
from django.utils import timezone from django.utils import timezone
from django.utils.timesince import timesince
def validate_duration(model, max_duration=timedelta(hours=24)):
"""
Basic sanity checks for models with a duration
:param model: a model instance with 'start' and 'end' attributes
:param max_duration: maximum allowed duration between start and end time
:return:
"""
if model.start and model.end:
if model.start > model.end:
raise ValidationError('Start time must come before end time')
if model.end - model.start > max_duration:
raise ValidationError('Duration too long (%(timesince)s)', params={
'timesince': timesince(model.start, model.end)})
class Child(models.Model): class Child(models.Model):
@ -98,6 +117,9 @@ class Feeding(models.Model):
self.duration = self.end - self.start self.duration = self.end - self.start
super(Feeding, self).save(*args, **kwargs) super(Feeding, self).save(*args, **kwargs)
def clean(self):
validate_duration(self)
class Note(models.Model): class Note(models.Model):
model_name = 'note' model_name = 'note'
@ -137,6 +159,9 @@ class Sleep(models.Model):
self.duration = self.end - self.start self.duration = self.end - self.start
super(Sleep, self).save(*args, **kwargs) super(Sleep, self).save(*args, **kwargs)
def clean(self):
validate_duration(self)
class Timer(models.Model): class Timer(models.Model):
model_name = 'timer' model_name = 'timer'
@ -191,6 +216,9 @@ class Timer(models.Model):
self.duration = None self.duration = None
super(Timer, self).save(*args, **kwargs) super(Timer, self).save(*args, **kwargs)
def clean(self):
validate_duration(self)
class TummyTime(models.Model): class TummyTime(models.Model):
model_name = 'tummytime' model_name = 'tummytime'
@ -213,3 +241,6 @@ class TummyTime(models.Model):
if self.start and self.end: if self.start and self.end:
self.duration = self.end - self.start self.duration = self.end - self.start
super(TummyTime, self).save(*args, **kwargs) super(TummyTime, self).save(*args, **kwargs)
def clean(self):
validate_duration(self)

View File

@ -71,6 +71,12 @@ class FormsTestCase(TestCase):
page = self.c.post('/feedings/{}/'.format(entry.id), params) page = self.c.post('/feedings/{}/'.format(entry.id), params)
self.assertEqual(page.status_code, 302) self.assertEqual(page.status_code, 302)
params['start'] = '2001-01-01 1:01'
page = self.c.post('/feedings/{}/'.format(entry.id), params)
self.assertEqual(page.status_code, 200)
self.assertFormError(page, 'form', None,
'Start time must come before end time')
def test_sleeping_forms(self): def test_sleeping_forms(self):
params = { params = {
'child': 1, 'child': 1,
@ -87,6 +93,12 @@ class FormsTestCase(TestCase):
page = self.c.post('/sleep/{}/'.format(entry.id), params) page = self.c.post('/sleep/{}/'.format(entry.id), params)
self.assertEqual(page.status_code, 302) self.assertEqual(page.status_code, 302)
params['start'] = '2001-01-01 1:01'
page = self.c.post('/sleep/{}/'.format(entry.id), params)
self.assertEqual(page.status_code, 200)
self.assertFormError(page, 'form', None,
'Start time must come before end time')
def test_timer_forms(self): def test_timer_forms(self):
timer = models.Timer.objects.create(user=self.user) timer = models.Timer.objects.create(user=self.user)
timer.save() timer.save()
@ -126,3 +138,9 @@ class FormsTestCase(TestCase):
entry = models.TummyTime.objects.first() entry = models.TummyTime.objects.first()
page = self.c.post('/tummy-time/{}/'.format(entry.id), params) page = self.c.post('/tummy-time/{}/'.format(entry.id), params)
self.assertEqual(page.status_code, 302) self.assertEqual(page.status_code, 302)
params['start'] = '2001-01-01 1:01'
page = self.c.post('/tummy-time/{}/'.format(entry.id), params)
self.assertEqual(page.status_code, 200)
self.assertFormError(page, 'form', None,
'Start time must come before end time')