test_unique.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import datetime
  2. import unittest
  3. from django.apps.registry import Apps
  4. from django.core.exceptions import ValidationError
  5. from django.db import models
  6. from django.test import TestCase
  7. from .models import (
  8. CustomPKModel, FlexibleDatePost, ModelToValidate, Post, UniqueErrorsModel,
  9. UniqueFieldsModel, UniqueForDateModel, UniqueFuncConstraintModel,
  10. UniqueTogetherModel,
  11. )
  12. class GetUniqueCheckTests(unittest.TestCase):
  13. def test_unique_fields_get_collected(self):
  14. m = UniqueFieldsModel()
  15. self.assertEqual(
  16. ([(UniqueFieldsModel, ('id',)),
  17. (UniqueFieldsModel, ('unique_charfield',)),
  18. (UniqueFieldsModel, ('unique_integerfield',))],
  19. []),
  20. m._get_unique_checks()
  21. )
  22. def test_unique_together_gets_picked_up_and_converted_to_tuple(self):
  23. m = UniqueTogetherModel()
  24. self.assertEqual(
  25. ([(UniqueTogetherModel, ('ifield', 'cfield')),
  26. (UniqueTogetherModel, ('ifield', 'efield')),
  27. (UniqueTogetherModel, ('id',))],
  28. []),
  29. m._get_unique_checks()
  30. )
  31. def test_unique_together_normalization(self):
  32. """
  33. Test the Meta.unique_together normalization with different sorts of
  34. objects.
  35. """
  36. data = {
  37. '2-tuple': (('foo', 'bar'), (('foo', 'bar'),)),
  38. 'list': (['foo', 'bar'], (('foo', 'bar'),)),
  39. 'already normalized': ((('foo', 'bar'), ('bar', 'baz')),
  40. (('foo', 'bar'), ('bar', 'baz'))),
  41. 'set': ({('foo', 'bar'), ('bar', 'baz')}, # Ref #21469
  42. (('foo', 'bar'), ('bar', 'baz'))),
  43. }
  44. for unique_together, normalized in data.values():
  45. class M(models.Model):
  46. foo = models.IntegerField()
  47. bar = models.IntegerField()
  48. baz = models.IntegerField()
  49. Meta = type('Meta', (), {
  50. 'unique_together': unique_together,
  51. 'apps': Apps()
  52. })
  53. checks, _ = M()._get_unique_checks()
  54. for t in normalized:
  55. check = (M, t)
  56. self.assertIn(check, checks)
  57. def test_primary_key_is_considered_unique(self):
  58. m = CustomPKModel()
  59. self.assertEqual(([(CustomPKModel, ('my_pk_field',))], []), m._get_unique_checks())
  60. def test_unique_for_date_gets_picked_up(self):
  61. m = UniqueForDateModel()
  62. self.assertEqual((
  63. [(UniqueForDateModel, ('id',))],
  64. [(UniqueForDateModel, 'date', 'count', 'start_date'),
  65. (UniqueForDateModel, 'year', 'count', 'end_date'),
  66. (UniqueForDateModel, 'month', 'order', 'end_date')]
  67. ), m._get_unique_checks()
  68. )
  69. def test_unique_for_date_exclusion(self):
  70. m = UniqueForDateModel()
  71. self.assertEqual((
  72. [(UniqueForDateModel, ('id',))],
  73. [(UniqueForDateModel, 'year', 'count', 'end_date'),
  74. (UniqueForDateModel, 'month', 'order', 'end_date')]
  75. ), m._get_unique_checks(exclude='start_date')
  76. )
  77. def test_func_unique_constraint_ignored(self):
  78. m = UniqueFuncConstraintModel()
  79. self.assertEqual(
  80. m._get_unique_checks(),
  81. ([(UniqueFuncConstraintModel, ('id',))], []),
  82. )
  83. class PerformUniqueChecksTest(TestCase):
  84. def test_primary_key_unique_check_not_performed_when_adding_and_pk_not_specified(self):
  85. # Regression test for #12560
  86. with self.assertNumQueries(0):
  87. mtv = ModelToValidate(number=10, name='Some Name')
  88. setattr(mtv, '_adding', True)
  89. mtv.full_clean()
  90. def test_primary_key_unique_check_performed_when_adding_and_pk_specified(self):
  91. # Regression test for #12560
  92. with self.assertNumQueries(1):
  93. mtv = ModelToValidate(number=10, name='Some Name', id=123)
  94. setattr(mtv, '_adding', True)
  95. mtv.full_clean()
  96. def test_primary_key_unique_check_not_performed_when_not_adding(self):
  97. # Regression test for #12132
  98. with self.assertNumQueries(0):
  99. mtv = ModelToValidate(number=10, name='Some Name')
  100. mtv.full_clean()
  101. def test_func_unique_check_not_performed(self):
  102. with self.assertNumQueries(0):
  103. UniqueFuncConstraintModel(field='some name').full_clean()
  104. def test_unique_for_date(self):
  105. Post.objects.create(
  106. title="Django 1.0 is released", slug="Django 1.0",
  107. subtitle="Finally", posted=datetime.date(2008, 9, 3),
  108. )
  109. p = Post(title="Django 1.0 is released", posted=datetime.date(2008, 9, 3))
  110. with self.assertRaises(ValidationError) as cm:
  111. p.full_clean()
  112. self.assertEqual(cm.exception.message_dict, {'title': ['Title must be unique for Posted date.']})
  113. # Should work without errors
  114. p = Post(title="Work on Django 1.1 begins", posted=datetime.date(2008, 9, 3))
  115. p.full_clean()
  116. # Should work without errors
  117. p = Post(title="Django 1.0 is released", posted=datetime.datetime(2008, 9, 4))
  118. p.full_clean()
  119. p = Post(slug="Django 1.0", posted=datetime.datetime(2008, 1, 1))
  120. with self.assertRaises(ValidationError) as cm:
  121. p.full_clean()
  122. self.assertEqual(cm.exception.message_dict, {'slug': ['Slug must be unique for Posted year.']})
  123. p = Post(subtitle="Finally", posted=datetime.datetime(2008, 9, 30))
  124. with self.assertRaises(ValidationError) as cm:
  125. p.full_clean()
  126. self.assertEqual(cm.exception.message_dict, {'subtitle': ['Subtitle must be unique for Posted month.']})
  127. p = Post(title="Django 1.0 is released")
  128. with self.assertRaises(ValidationError) as cm:
  129. p.full_clean()
  130. self.assertEqual(cm.exception.message_dict, {'posted': ['This field cannot be null.']})
  131. def test_unique_for_date_with_nullable_date(self):
  132. """
  133. unique_for_date/year/month checks shouldn't trigger when the
  134. associated DateField is None.
  135. """
  136. FlexibleDatePost.objects.create(
  137. title="Django 1.0 is released", slug="Django 1.0",
  138. subtitle="Finally", posted=datetime.date(2008, 9, 3),
  139. )
  140. p = FlexibleDatePost(title="Django 1.0 is released")
  141. p.full_clean()
  142. p = FlexibleDatePost(slug="Django 1.0")
  143. p.full_clean()
  144. p = FlexibleDatePost(subtitle="Finally")
  145. p.full_clean()
  146. def test_unique_errors(self):
  147. UniqueErrorsModel.objects.create(name='Some Name', no=10)
  148. m = UniqueErrorsModel(name='Some Name', no=11)
  149. with self.assertRaises(ValidationError) as cm:
  150. m.full_clean()
  151. self.assertEqual(cm.exception.message_dict, {'name': ['Custom unique name message.']})
  152. m = UniqueErrorsModel(name='Some Other Name', no=10)
  153. with self.assertRaises(ValidationError) as cm:
  154. m.full_clean()
  155. self.assertEqual(cm.exception.message_dict, {'no': ['Custom unique number message.']})