summaryrefslogtreecommitdiff
path: root/tests/model_fields
diff options
context:
space:
mode:
authorDaniel Pyrathon <pirosb3@gmail.com>2015-01-06 19:16:35 -0500
committerTim Graham <timograham@gmail.com>2015-01-06 19:25:12 -0500
commitfb48eb05816b1ac87d58696cdfe48be18c901f16 (patch)
tree3d2e981b6f3fafdeb7310d0734fb148ebb7f6aef /tests/model_fields
parent749d23251bbd6564341405e6f8c1da129b8307e7 (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.py51
-rw-r--r--tests/model_fields/test_field_flags.py220
-rw-r--r--tests/model_fields/tests.py2
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)