mirror of
https://github.com/django/django.git
synced 2024-11-29 22:56:46 +01:00
c339a5a6f7
The only reason why GenericForeignKey and GenericRelation are stored separately inside _meta is that they need to be cloned for every model subclass, but that's not true for any other virtual field. Actually, it's only true for GenericRelation.
224 lines
7.1 KiB
Python
224 lines
7.1 KiB
Python
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(FieldFlagsTests, cls).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)
|