2015-01-07 01:16:35 +01:00
|
|
|
from django import test
|
|
|
|
from django.contrib.contenttypes.fields import (
|
|
|
|
GenericForeignKey, GenericRelation,
|
|
|
|
)
|
|
|
|
from django.db import models
|
|
|
|
from django.db.models.fields.related import (
|
2015-01-28 13:35:27 +01:00
|
|
|
ForeignKey, ForeignObject, ForeignObjectRel, ManyToManyField, ManyToOneRel,
|
|
|
|
OneToOneField,
|
2015-01-07 01:16:35 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
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,
|
|
|
|
)
|
|
|
|
|
2015-02-13 19:55:36 +01:00
|
|
|
MANY_TO_MANY_CLASSES = {
|
|
|
|
ManyToManyField,
|
|
|
|
}
|
|
|
|
|
|
|
|
MANY_TO_ONE_CLASSES = {
|
2015-01-07 01:16:35 +01:00
|
|
|
ForeignObject,
|
|
|
|
ForeignKey,
|
|
|
|
GenericForeignKey,
|
|
|
|
}
|
|
|
|
|
2015-02-13 19:55:36 +01:00
|
|
|
ONE_TO_MANY_CLASSES = {
|
2015-01-07 01:16:35 +01:00
|
|
|
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',
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2015-04-17 23:38:20 +02:00
|
|
|
class FieldFlagsTests(test.SimpleTestCase):
|
2015-01-07 01:16:35 +01:00
|
|
|
@classmethod
|
|
|
|
def setUpClass(cls):
|
2017-01-21 14:13:44 +01:00
|
|
|
super().setUpClass()
|
2018-09-28 15:57:12 +02:00
|
|
|
cls.fields = [
|
|
|
|
*AllFieldsModel._meta.fields,
|
|
|
|
*AllFieldsModel._meta.private_fields,
|
|
|
|
]
|
|
|
|
|
|
|
|
cls.all_fields = [
|
|
|
|
*cls.fields,
|
|
|
|
*AllFieldsModel._meta.many_to_many,
|
|
|
|
*AllFieldsModel._meta.private_fields,
|
|
|
|
]
|
|
|
|
|
|
|
|
cls.fields_and_reverse_objects = [
|
|
|
|
*cls.all_fields,
|
|
|
|
*AllFieldsModel._meta.related_objects,
|
|
|
|
]
|
2015-01-07 01:16:35 +01:00
|
|
|
|
|
|
|
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(
|
2016-04-08 04:04:45 +02:00
|
|
|
all(f.auto_created.__class__ == bool for f in self.fields_and_reverse_objects)
|
2015-01-07 01:16:35 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
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):
|
2015-04-20 14:29:50 +02:00
|
|
|
m2m_type_fields = [
|
2015-01-07 01:16:35 +01:00
|
|
|
f for f in self.all_fields
|
|
|
|
if f.is_relation and f.many_to_many
|
2015-04-20 14:29:50 +02:00
|
|
|
]
|
2015-01-07 01:16:35 +01:00
|
|
|
# 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:
|
2015-02-26 15:19:17 +01:00
|
|
|
reverse_field = field.remote_field
|
2015-01-07 01:16:35 +01:00
|
|
|
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:
|
2015-02-26 15:19:17 +01:00
|
|
|
reverse_field = field.remote_field
|
2015-01-07 01:16:35 +01:00
|
|
|
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:
|
2015-02-26 15:19:17 +01:00
|
|
|
reverse_field = field.remote_field
|
2015-01-07 01:16:35 +01:00
|
|
|
self.assertEqual(field.model, reverse_field.related_model)
|
|
|
|
self.assertEqual(field.related_model, reverse_field.model)
|
2015-08-28 15:25:54 +02:00
|
|
|
|
|
|
|
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)
|