test_field_flags.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. from django import test
  2. from django.contrib.contenttypes.fields import (
  3. GenericForeignKey, GenericRelation,
  4. )
  5. from django.db import models
  6. from django.db.models.fields.related import (
  7. ForeignKey, ForeignObject, ForeignObjectRel, ManyToManyField, ManyToOneRel,
  8. OneToOneField,
  9. )
  10. from .models import AllFieldsModel
  11. NON_CONCRETE_FIELDS = (
  12. ForeignObject,
  13. GenericForeignKey,
  14. GenericRelation,
  15. )
  16. NON_EDITABLE_FIELDS = (
  17. models.BinaryField,
  18. GenericForeignKey,
  19. GenericRelation,
  20. )
  21. RELATION_FIELDS = (
  22. ForeignKey,
  23. ForeignObject,
  24. ManyToManyField,
  25. OneToOneField,
  26. GenericForeignKey,
  27. GenericRelation,
  28. )
  29. MANY_TO_MANY_CLASSES = {
  30. ManyToManyField,
  31. }
  32. MANY_TO_ONE_CLASSES = {
  33. ForeignObject,
  34. ForeignKey,
  35. GenericForeignKey,
  36. }
  37. ONE_TO_MANY_CLASSES = {
  38. ForeignObjectRel,
  39. ManyToOneRel,
  40. GenericRelation,
  41. }
  42. ONE_TO_ONE_CLASSES = {
  43. OneToOneField,
  44. }
  45. FLAG_PROPERTIES = (
  46. 'concrete',
  47. 'editable',
  48. 'is_relation',
  49. 'model',
  50. 'hidden',
  51. 'one_to_many',
  52. 'many_to_one',
  53. 'many_to_many',
  54. 'one_to_one',
  55. 'related_model',
  56. )
  57. FLAG_PROPERTIES_FOR_RELATIONS = (
  58. 'one_to_many',
  59. 'many_to_one',
  60. 'many_to_many',
  61. 'one_to_one',
  62. )
  63. class FieldFlagsTests(test.SimpleTestCase):
  64. @classmethod
  65. def setUpClass(cls):
  66. super().setUpClass()
  67. cls.fields = [
  68. *AllFieldsModel._meta.fields,
  69. *AllFieldsModel._meta.private_fields,
  70. ]
  71. cls.all_fields = [
  72. *cls.fields,
  73. *AllFieldsModel._meta.many_to_many,
  74. *AllFieldsModel._meta.private_fields,
  75. ]
  76. cls.fields_and_reverse_objects = [
  77. *cls.all_fields,
  78. *AllFieldsModel._meta.related_objects,
  79. ]
  80. def test_each_field_should_have_a_concrete_attribute(self):
  81. self.assertTrue(all(f.concrete.__class__ == bool for f in self.fields))
  82. def test_each_field_should_have_an_editable_attribute(self):
  83. self.assertTrue(all(f.editable.__class__ == bool for f in self.all_fields))
  84. def test_each_field_should_have_a_has_rel_attribute(self):
  85. self.assertTrue(all(f.is_relation.__class__ == bool for f in self.all_fields))
  86. def test_each_object_should_have_auto_created(self):
  87. self.assertTrue(
  88. all(f.auto_created.__class__ == bool for f in self.fields_and_reverse_objects)
  89. )
  90. def test_non_concrete_fields(self):
  91. for field in self.fields:
  92. if type(field) in NON_CONCRETE_FIELDS:
  93. self.assertFalse(field.concrete)
  94. else:
  95. self.assertTrue(field.concrete)
  96. def test_non_editable_fields(self):
  97. for field in self.all_fields:
  98. if type(field) in NON_EDITABLE_FIELDS:
  99. self.assertFalse(field.editable)
  100. else:
  101. self.assertTrue(field.editable)
  102. def test_related_fields(self):
  103. for field in self.all_fields:
  104. if type(field) in RELATION_FIELDS:
  105. self.assertTrue(field.is_relation)
  106. else:
  107. self.assertFalse(field.is_relation)
  108. def test_field_names_should_always_be_available(self):
  109. for field in self.fields_and_reverse_objects:
  110. self.assertTrue(field.name)
  111. def test_all_field_types_should_have_flags(self):
  112. for field in self.fields_and_reverse_objects:
  113. for flag in FLAG_PROPERTIES:
  114. self.assertTrue(hasattr(field, flag), "Field %s does not have flag %s" % (field, flag))
  115. if field.is_relation:
  116. true_cardinality_flags = sum(
  117. getattr(field, flag) is True
  118. for flag in FLAG_PROPERTIES_FOR_RELATIONS
  119. )
  120. # If the field has a relation, there should be only one of the
  121. # 4 cardinality flags available.
  122. self.assertEqual(1, true_cardinality_flags)
  123. def test_cardinality_m2m(self):
  124. m2m_type_fields = [
  125. f for f in self.all_fields
  126. if f.is_relation and f.many_to_many
  127. ]
  128. # Test classes are what we expect
  129. self.assertEqual(MANY_TO_MANY_CLASSES, {f.__class__ for f in m2m_type_fields})
  130. # Ensure all m2m reverses are m2m
  131. for field in m2m_type_fields:
  132. reverse_field = field.remote_field
  133. self.assertTrue(reverse_field.is_relation)
  134. self.assertTrue(reverse_field.many_to_many)
  135. self.assertTrue(reverse_field.related_model)
  136. def test_cardinality_o2m(self):
  137. o2m_type_fields = [
  138. f for f in self.fields_and_reverse_objects
  139. if f.is_relation and f.one_to_many
  140. ]
  141. # Test classes are what we expect
  142. self.assertEqual(ONE_TO_MANY_CLASSES, {f.__class__ for f in o2m_type_fields})
  143. # Ensure all o2m reverses are m2o
  144. for field in o2m_type_fields:
  145. if field.concrete:
  146. reverse_field = field.remote_field
  147. self.assertTrue(reverse_field.is_relation and reverse_field.many_to_one)
  148. def test_cardinality_m2o(self):
  149. m2o_type_fields = [
  150. f for f in self.fields_and_reverse_objects
  151. if f.is_relation and f.many_to_one
  152. ]
  153. # Test classes are what we expect
  154. self.assertEqual(MANY_TO_ONE_CLASSES, {f.__class__ for f in m2o_type_fields})
  155. # Ensure all m2o reverses are o2m
  156. for obj in m2o_type_fields:
  157. if hasattr(obj, 'field'):
  158. reverse_field = obj.field
  159. self.assertTrue(reverse_field.is_relation and reverse_field.one_to_many)
  160. def test_cardinality_o2o(self):
  161. o2o_type_fields = [
  162. f for f in self.all_fields
  163. if f.is_relation and f.one_to_one
  164. ]
  165. # Test classes are what we expect
  166. self.assertEqual(ONE_TO_ONE_CLASSES, {f.__class__ for f in o2o_type_fields})
  167. # Ensure all o2o reverses are o2o
  168. for obj in o2o_type_fields:
  169. if hasattr(obj, 'field'):
  170. reverse_field = obj.field
  171. self.assertTrue(reverse_field.is_relation and reverse_field.one_to_one)
  172. def test_hidden_flag(self):
  173. incl_hidden = set(AllFieldsModel._meta.get_fields(include_hidden=True))
  174. no_hidden = set(AllFieldsModel._meta.get_fields())
  175. fields_that_should_be_hidden = (incl_hidden - no_hidden)
  176. for f in incl_hidden:
  177. self.assertEqual(f in fields_that_should_be_hidden, f.hidden)
  178. def test_model_and_reverse_model_should_equal_on_relations(self):
  179. for field in AllFieldsModel._meta.get_fields():
  180. is_concrete_forward_field = field.concrete and field.related_model
  181. if is_concrete_forward_field:
  182. reverse_field = field.remote_field
  183. self.assertEqual(field.model, reverse_field.related_model)
  184. self.assertEqual(field.related_model, reverse_field.model)
  185. def test_null(self):
  186. # null isn't well defined for a ManyToManyField, but changing it to
  187. # True causes backwards compatibility problems (#25320).
  188. self.assertFalse(AllFieldsModel._meta.get_field('m2m').null)
  189. self.assertTrue(AllFieldsModel._meta.get_field('reverse2').null)