From 49293cd11edd3ec3dd58794c989524d8b764978f Mon Sep 17 00:00:00 2001 From: Charles Connell Date: Tue, 17 Dec 2013 14:36:29 -0500 Subject: [PATCH] Enforce course uniqueness --- karmaworld/apps/courses/models.py | 10 ++++ karmaworld/apps/courses/test/test.py | 6 ++- karmaworld/settings/manual_unique_together.py | 53 +++++++++++++++++++ karmaworld/templates/partial/add_course.html | 2 +- 4 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 karmaworld/settings/manual_unique_together.py diff --git a/karmaworld/apps/courses/models.py b/karmaworld/apps/courses/models.py index 273dc69..7c82802 100644 --- a/karmaworld/apps/courses/models.py +++ b/karmaworld/apps/courses/models.py @@ -12,6 +12,8 @@ import datetime from django.db import models from django.template import defaultfilters +from karmaworld.settings.manual_unique_together import auto_add_check_unique_together + class School(models.Model): """ A grouping that contains many courses """ @@ -84,6 +86,9 @@ class Course(models.Model): class Meta: ordering = ['-file_count', 'school', 'name'] + unique_together = ('school', 'name', 'instructor_name') + verbose_name = 'course' + verbose_name_plural = 'courses' def __unicode__(self): return u"{0}: {1}".format(self.name, self.school) @@ -111,3 +116,8 @@ class Course(models.Model): """ Update self.file_count by summing the note_set """ self.file_count = self.note_set.count() self.save() + +# Enforce unique constraints even when we're using a database like +# SQLite that doesn't understand them +auto_add_check_unique_together(Course) + diff --git a/karmaworld/apps/courses/test/test.py b/karmaworld/apps/courses/test/test.py index cd577a0..1157152 100644 --- a/karmaworld/apps/courses/test/test.py +++ b/karmaworld/apps/courses/test/test.py @@ -1,3 +1,4 @@ +from django.db import IntegrityError from django.test import TestCase from karmaworld.apps.courses.models import * from django.test.client import Client @@ -15,8 +16,9 @@ class CoursesTests(TestCase): def testCourseUniqueness(self): """Make sure we can't create multiple courses with the same school + course name + instructor name combination.""" - with self.assertRaises(Exception): - Course.objects.create(name="Underwater Basketweaving", instructor_name="Alice Janney", school=self.harvard) + with self.assertRaises(IntegrityError): + Course.objects.create(name=self.course1.name, instructor_name=self.course1.instructor_name, school=self.course1.school) + self.assertEqual(Course.objects.count(), 1) def testSchoolSlug(self): self.assertEqual(self.harvard.slug, defaultfilters.slugify(self.harvard.name)) diff --git a/karmaworld/settings/manual_unique_together.py b/karmaworld/settings/manual_unique_together.py new file mode 100644 index 0000000..294d15b --- /dev/null +++ b/karmaworld/settings/manual_unique_together.py @@ -0,0 +1,53 @@ +from django.conf import settings +from django.db.models import signals, FieldDoesNotExist +from django.utils.text import get_text_list +from django.db import IntegrityError +from django.utils.translation import ugettext as _ + +# This little gem is borrowed from +# https://djangosnippets.org/snippets/1628/ + +def check_unique_together(sender, **kwargs): + """ + Check models unique_together manually. Django enforced unique together only the database level, but + some databases (e.g. SQLite) doesn't support this. + + usage: + from django.db.models import signals + signals.pre_save.connect(check_unique_together, sender=MyModelClass) + + or use auto_add_check_unique_together(), see below + """ + instance = kwargs["instance"] + for field_names in sender._meta.unique_together: + model_kwargs = {} + for field_name in field_names: + try: + data = getattr(instance, field_name) + except FieldDoesNotExist: + # e.g.: a missing field, which is however necessary. + # The real exception on model creation should be raised. + continue + model_kwargs[field_name] = data + + query_set = sender.objects.filter(**model_kwargs) + if instance.pk != None: + # Exclude the instance if it was saved in the past + query_set = query_set.exclude(pk=instance.pk) + + count = query_set.count() + if count > 0: + field_names = get_text_list(field_names, _('and')) + msg = _(u"%(model_name)s with this %(field_names)s already exists.") % { + 'model_name': unicode(instance.__class__.__name__), + 'field_names': unicode(field_names) + } + raise IntegrityError(msg) + +def auto_add_check_unique_together(model_class): + """ + Add only the signal handler check_unique_together, if a database without UNIQUE support is used. + """ + if 'sqlite' in settings.DATABASES['default']['ENGINE']: + signals.pre_save.connect(check_unique_together, sender=model_class) + diff --git a/karmaworld/templates/partial/add_course.html b/karmaworld/templates/partial/add_course.html index 0ecd82a..590f356 100644 --- a/karmaworld/templates/partial/add_course.html +++ b/karmaworld/templates/partial/add_course.html @@ -13,7 +13,7 @@
- {{ course_form.non_field.errors }} + {{ course_form.non_field_errors }}
-- 2.25.1