Skip to content

Commit

Permalink
Use Django's standard uniqueness error formatter and add field key
Browse files Browse the repository at this point in the history
This improves things in the following ways:
- The exception object contains a key to indicate which field is causing the problem.
- The exception object contains a "code"/slug which can be used to programmatically deal with the error.  Very useful in JSON APIs.
- We now use a standard text message that is more in line with the rest of Django (which also means it is translatable)
  • Loading branch information
Peter Bex committed Jul 22, 2019
1 parent 666bd58 commit 213bebe
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 10 deletions.
11 changes: 6 additions & 5 deletions partial_index/mixins.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.core.exceptions import ImproperlyConfigured, ValidationError, NON_FIELD_ERRORS
from django.db.models import Q

from .index import PartialIndex
Expand Down Expand Up @@ -92,7 +92,8 @@ def validate_partial_unique(self):
conflict = conflict.exclude(pk=self.pk) # Step 4

if conflict.exists():
raise PartialUniqueValidationError('%s with the same values for %s already exists.' % (
self.__class__.__name__,
', '.join(sorted(idx.fields)),
))
if len(idx.fields) == 1:
key = idx.fields[0]
else:
key = NON_FIELD_ERRORS
raise PartialUniqueValidationError({key: self.unique_error_message(self.__class__, sorted(idx.fields))})
4 changes: 2 additions & 2 deletions tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class FormTestCase(object):
"""Base class for form tests.
"""
formclass = None
conflict_error = 'RoomBookingQ with the same values for room, user already exists.'
conflict_error = 'Room booking q with this Room and User already exists.'

def setUp(self):
self.user1 = User.objects.create(name='User1')
Expand Down Expand Up @@ -98,7 +98,7 @@ class SingleFieldFormTest(FormTestCase, TransactionTestCase):
class NullableFieldFormTest(TransactionTestCase):
"""Test that partial unique validation on a ModelForm treats null values as non-unique."""
formclass = NullableRoomNumberAllFieldsForm
conflict_error = 'NullableRoomNumberQ with the same values for room, room_number already exists.'
conflict_error = 'Nullable room number q with this Room and Room number already exists.'

def setUp(self):
self.room1 = Room.objects.create(name='Room1')
Expand Down
21 changes: 19 additions & 2 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.db import IntegrityError
from django.test import TransactionTestCase
from django.utils import timezone
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError

from testapp.models import User, Room, RoomBookingText, JobText, ComparisonText, NullableRoomNumberText, RoomBookingQ, JobQ, ComparisonQ, NullableRoomNumberQ

Expand Down Expand Up @@ -57,8 +58,16 @@ def test_roombooking_text_same_conflict(self):

def test_roombooking_q_same_conflict(self):
RoomBookingQ.objects.create(user=self.user1, room=self.room1)

room_booking = RoomBookingQ(user=self.user1, room=self.room1)
with self.assertRaises(ValidationError) as cm:
room_booking.full_clean()

self.assertSetEqual({NON_FIELD_ERRORS}, set(cm.exception.message_dict.keys()))
self.assertEqual('unique_together', cm.exception.error_dict[NON_FIELD_ERRORS][0].code)

with self.assertRaises(IntegrityError):
RoomBookingQ.objects.create(user=self.user1, room=self.room1)
room_booking.save()


class PartialIndexJobTest(TransactionTestCase):
Expand All @@ -83,8 +92,16 @@ def test_job_text_same_group(self):

def test_job_q_same_group(self):
JobQ.objects.create(order=1, group=1)

job = JobQ(order=2, group=1)
with self.assertRaises(ValidationError) as cm:
job.full_clean()

self.assertSetEqual({'group'}, set(cm.exception.message_dict.keys()))
self.assertEqual('unique', cm.exception.error_dict['group'][0].code)

with self.assertRaises(IntegrityError):
JobQ.objects.create(order=2, group=1)
job.save()

def test_job_text_complete_same_group(self):
job1 = JobText.objects.create(order=1, group=1, is_complete=True)
Expand Down
2 changes: 1 addition & 1 deletion tests/testapp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class Meta:
]


class JobQ(models.Model):
class JobQ(ValidatePartialUniqueMixin, models.Model):
order = models.IntegerField()
group = models.IntegerField()
is_complete = models.BooleanField(default=False)
Expand Down

0 comments on commit 213bebe

Please sign in to comment.