diff options
| author | Daniel Pyrathon <pirosb3@gmail.com> | 2015-01-06 19:16:35 -0500 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2015-01-06 19:25:12 -0500 |
| commit | fb48eb05816b1ac87d58696cdfe48be18c901f16 (patch) | |
| tree | 3d2e981b6f3fafdeb7310d0734fb148ebb7f6aef /tests/model_fields | |
| parent | 749d23251bbd6564341405e6f8c1da129b8307e7 (diff) | |
Fixed #12663 -- Formalized the Model._meta API for retrieving fields.
Thanks to Russell Keith-Magee for mentoring this Google Summer of
Code 2014 project and everyone else who helped with the patch!
Diffstat (limited to 'tests/model_fields')
| -rw-r--r-- | tests/model_fields/models.py | 51 | ||||
| -rw-r--r-- | tests/model_fields/test_field_flags.py | 220 | ||||
| -rw-r--r-- | tests/model_fields/tests.py | 2 |
3 files changed, 272 insertions, 1 deletions
diff --git a/tests/model_fields/models.py b/tests/model_fields/models.py index 2a7bebc9dd..8927ae8830 100644 --- a/tests/model_fields/models.py +++ b/tests/model_fields/models.py @@ -8,6 +8,11 @@ except ImportError: Image = None from django.core.files.storage import FileSystemStorage +from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation +from django.contrib.contenttypes.models import ContentType +from django.db.models.fields.related import ( + ForeignObject, ForeignKey, ManyToManyField, OneToOneField, +) from django.db import models from django.db.models.fields.files import ImageFieldFile, ImageField from django.utils import six @@ -295,6 +300,52 @@ if Image: height_field='headshot_height', width_field='headshot_width') + +class AllFieldsModel(models.Model): + big_integer = models.BigIntegerField() + binary = models.BinaryField() + boolean = models.BooleanField(default=False) + char = models.CharField(max_length=10) + csv = models.CommaSeparatedIntegerField(max_length=10) + date = models.DateField() + datetime = models.DateTimeField() + decimal = models.DecimalField(decimal_places=2, max_digits=2) + duration = models.DurationField() + email = models.EmailField() + file_path = models.FilePathField() + floatf = models.FloatField() + integer = models.IntegerField() + ip_address = models.IPAddressField() + generic_ip = models.GenericIPAddressField() + null_boolean = models.NullBooleanField() + positive_integer = models.PositiveIntegerField() + positive_small_integer = models.PositiveSmallIntegerField() + slug = models.SlugField() + small_integer = models.SmallIntegerField() + text = models.TextField() + time = models.TimeField() + url = models.URLField() + uuid = models.UUIDField() + + fo = ForeignObject( + 'self', + from_fields=['abstract_non_concrete_id'], + to_fields=['id'], + related_name='reverse' + ) + fk = ForeignKey( + 'self', + related_name='reverse2' + ) + m2m = ManyToManyField('self') + oto = OneToOneField('self') + + object_id = models.PositiveIntegerField() + content_type = models.ForeignKey(ContentType) + gfk = GenericForeignKey() + gr = GenericRelation(DataModel) + + ############################################################################### diff --git a/tests/model_fields/test_field_flags.py b/tests/model_fields/test_field_flags.py new file mode 100644 index 0000000000..08a57db501 --- /dev/null +++ b/tests/model_fields/test_field_flags.py @@ -0,0 +1,220 @@ +from django import test + +from django.contrib.contenttypes.fields import ( + GenericForeignKey, GenericRelation, +) +from django.db import models +from django.db.models.fields.related import ( + ForeignObject, ForeignKey, OneToOneField, ManyToManyField, + ManyToOneRel, ForeignObjectRel, +) + +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, +) + +ONE_TO_MANY_CLASSES = { + ForeignObject, + ForeignKey, + GenericForeignKey, +} + +MANY_TO_ONE_CLASSES = { + ForeignObjectRel, + ManyToOneRel, + GenericRelation, +} + +MANY_TO_MANY_CLASSES = { + ManyToManyField, +} + +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.TestCase): + @classmethod + def setUpClass(cls): + super(FieldFlagsTests, cls).setUpClass() + cls.fields = ( + list(AllFieldsModel._meta.fields) + + list(AllFieldsModel._meta.virtual_fields) + ) + + cls.all_fields = ( + cls.fields + + list(AllFieldsModel._meta.many_to_many) + + list(AllFieldsModel._meta.virtual_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.rel + 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.rel + 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.rel + self.assertEqual(field.model, reverse_field.related_model) + self.assertEqual(field.related_model, reverse_field.model) diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py index e63cc007dd..d891645ca0 100644 --- a/tests/model_fields/tests.py +++ b/tests/model_fields/tests.py @@ -198,7 +198,7 @@ class ForeignKeyTests(test.TestCase): self.assertEqual(warnings, expected_warnings) def test_related_name_converted_to_text(self): - rel_name = Bar._meta.get_field_by_name('a')[0].rel.related_name + rel_name = Bar._meta.get_field('a').rel.related_name self.assertIsInstance(rel_name, six.text_type) |
