From 1eeba2398d9b8b96eb3d53a5fdd452589b429e37 Mon Sep 17 00:00:00 2001 From: Bob Thomas Date: Wed, 1 Nov 2017 12:44:07 -0400 Subject: [PATCH] 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). --- core/models.py | 31 +++++++++++++++++++++++++++++++ core/tests/tests_forms.py | 18 ++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/core/models.py b/core/models.py index 065f4eae..fb4aa809 100644 --- a/core/models.py +++ b/core/models.py @@ -1,9 +1,28 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +from datetime import timedelta + +from django.core.exceptions import ValidationError from django.db import models from django.template.defaultfilters import slugify 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): @@ -98,6 +117,9 @@ class Feeding(models.Model): self.duration = self.end - self.start super(Feeding, self).save(*args, **kwargs) + def clean(self): + validate_duration(self) + class Note(models.Model): model_name = 'note' @@ -137,6 +159,9 @@ class Sleep(models.Model): self.duration = self.end - self.start super(Sleep, self).save(*args, **kwargs) + def clean(self): + validate_duration(self) + class Timer(models.Model): model_name = 'timer' @@ -191,6 +216,9 @@ class Timer(models.Model): self.duration = None super(Timer, self).save(*args, **kwargs) + def clean(self): + validate_duration(self) + class TummyTime(models.Model): model_name = 'tummytime' @@ -213,3 +241,6 @@ class TummyTime(models.Model): if self.start and self.end: self.duration = self.end - self.start super(TummyTime, self).save(*args, **kwargs) + + def clean(self): + validate_duration(self) diff --git a/core/tests/tests_forms.py b/core/tests/tests_forms.py index d8fe0d93..cdff19f4 100644 --- a/core/tests/tests_forms.py +++ b/core/tests/tests_forms.py @@ -71,6 +71,12 @@ class FormsTestCase(TestCase): page = self.c.post('/feedings/{}/'.format(entry.id), params) 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): params = { 'child': 1, @@ -87,6 +93,12 @@ class FormsTestCase(TestCase): page = self.c.post('/sleep/{}/'.format(entry.id), params) 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): timer = models.Timer.objects.create(user=self.user) timer.save() @@ -126,3 +138,9 @@ class FormsTestCase(TestCase): entry = models.TummyTime.objects.first() page = self.c.post('/tummy-time/{}/'.format(entry.id), params) 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')