from django import test from django.contrib.contenttypes.fields import ( GenericForeignKey, GenericRelation, ) from django.db import models from django.db.models.fields.related import ( ForeignKey, ForeignObject, ForeignObjectRel, ManyToManyField, ManyToOneRel, OneToOneField, ) from .models import AllFieldsModel NON_CONCRETE_FIELDS = ( ForeignObject, GenericForeignKey, GenericRelation, ) NON_EDITABLE_FIELDS = ( models.BinaryField, GenericForeignKey, GenericRelation, ) RELATION_FIELDS = ( ForeignKey, ForeignObject, ManyToManyField, OneToOneField, GenericForeignKey, GenericRelation, ) MANY_TO_MANY_CLASSES = { ManyToManyField, } MANY_TO_ONE_CLASSES = { ForeignObject, ForeignKey, GenericForeignKey, } ONE_TO_MANY_CLASSES = { ForeignObjectRel, ManyToOneRel, GenericRelation, } ONE_TO_ONE_CLASSES = { OneToOneField, } FLAG_PROPERTIES = ( 'concrete', 'editable', 'is_relation', 'model', 'hidden', 'one_to_many', 'many_to_one', 'many_to_many', 'one_to_one', 'related_model', ) FLAG_PROPERTIES_FOR_RELATIONS = ( 'one_to_many', 'many_to_one', 'many_to_many', 'one_to_one', ) class FieldFlagsTests(test.SimpleTestCase): @classmethod def setUpClass(cls): super().setUpClass() cls.fields = ( list(AllFieldsModel._meta.fields) + list(AllFieldsModel._meta.private_fields) ) cls.all_fields = ( cls.fields + list(AllFieldsModel._meta.many_to_many) + list(AllFieldsModel._meta.private_fields) ) cls.fields_and_reverse_objects = ( cls.all_fields + list(AllFieldsModel._meta.related_objects) ) def test_each_field_should_have_a_concrete_attribute(self): self.assertTrue(all(f.concrete.__class__ == bool for f in self.fields)) def test_each_field_should_have_an_editable_attribute(self): self.assertTrue(all(f.editable.__class__ == bool for f in self.all_fields)) def test_each_field_should_have_a_has_rel_attribute(self): self.assertTrue(all(f.is_relation.__class__ == bool for f in self.all_fields)) def test_each_object_should_have_auto_created(self): self.assertTrue( all(f.auto_created.__class__ == bool for f in self.fields_and_reverse_objects) ) def test_non_concrete_fields(self): for field in self.fields: if type(field) in NON_CONCRETE_FIELDS: self.assertFalse(field.concrete) else: self.assertTrue(field.concrete) def test_non_editable_fields(self): for field in self.all_fields: if type(field) in NON_EDITABLE_FIELDS: self.assertFalse(field.editable) else: self.assertTrue(field.editable) def test_related_fields(self): for field in self.all_fields: if type(field) in RELATION_FIELDS: self.assertTrue(field.is_relation) else: self.assertFalse(field.is_relation) def test_field_names_should_always_be_available(self): for field in self.fields_and_reverse_objects: self.assertTrue(field.name) def test_all_field_types_should_have_flags(self): for field in self.fields_and_reverse_objects: for flag in FLAG_PROPERTIES: self.assertTrue(hasattr(field, flag), "Field %s does not have flag %s" % (field, flag)) if field.is_relation: true_cardinality_flags = sum( getattr(field, flag) is True for flag in FLAG_PROPERTIES_FOR_RELATIONS ) # If the field has a relation, there should be only one of the # 4 cardinality flags available. self.assertEqual(1, true_cardinality_flags) def test_cardinality_m2m(self): m2m_type_fields = [ f for f in self.all_fields if f.is_relation and f.many_to_many ] # Test classes are what we expect self.assertEqual(MANY_TO_MANY_CLASSES, {f.__class__ for f in m2m_type_fields}) # Ensure all m2m reverses are m2m for field in m2m_type_fields: reverse_field = field.remote_field self.assertTrue(reverse_field.is_relation) self.assertTrue(reverse_field.many_to_many) self.assertTrue(reverse_field.related_model) def test_cardinality_o2m(self): o2m_type_fields = [ f for f in self.fields_and_reverse_objects if f.is_relation and f.one_to_many ] # Test classes are what we expect self.assertEqual(ONE_TO_MANY_CLASSES, {f.__class__ for f in o2m_type_fields}) # Ensure all o2m reverses are m2o for field in o2m_type_fields: if field.concrete: reverse_field = field.remote_field self.assertTrue(reverse_field.is_relation and reverse_field.many_to_one) def test_cardinality_m2o(self): m2o_type_fields = [ f for f in self.fields_and_reverse_objects if f.is_relation and f.many_to_one ] # Test classes are what we expect self.assertEqual(MANY_TO_ONE_CLASSES, {f.__class__ for f in m2o_type_fields}) # Ensure all m2o reverses are o2m for obj in m2o_type_fields: if hasattr(obj, 'field'): reverse_field = obj.field self.assertTrue(reverse_field.is_relation and reverse_field.one_to_many) def test_cardinality_o2o(self): o2o_type_fields = [ f for f in self.all_fields if f.is_relation and f.one_to_one ] # Test classes are what we expect self.assertEqual(ONE_TO_ONE_CLASSES, {f.__class__ for f in o2o_type_fields}) # Ensure all o2o reverses are o2o for obj in o2o_type_fields: if hasattr(obj, 'field'): reverse_field = obj.field self.assertTrue(reverse_field.is_relation and reverse_field.one_to_one) def test_hidden_flag(self): incl_hidden = set(AllFieldsModel._meta.get_fields(include_hidden=True)) no_hidden = set(AllFieldsModel._meta.get_fields()) fields_that_should_be_hidden = (incl_hidden - no_hidden) for f in incl_hidden: self.assertEqual(f in fields_that_should_be_hidden, f.hidden) def test_model_and_reverse_model_should_equal_on_relations(self): for field in AllFieldsModel._meta.get_fields(): is_concrete_forward_field = field.concrete and field.related_model if is_concrete_forward_field: reverse_field = field.remote_field self.assertEqual(field.model, reverse_field.related_model) self.assertEqual(field.related_model, reverse_field.model) def test_null(self): # null isn't well defined for a ManyToManyField, but changing it to # True causes backwards compatibility problems (#25320). self.assertFalse(AllFieldsModel._meta.get_field('m2m').null) self.assertTrue(AllFieldsModel._meta.get_field('reverse2').null)