2014-09-09 20:46:28 +04:00
|
|
|
"""
|
|
|
|
Helper functions for returning the field information that is associated
|
2014-09-12 12:12:56 +04:00
|
|
|
with a model class. This includes returning all the forward and reverse
|
|
|
|
relationships and their associated metadata.
|
2014-09-09 20:46:28 +04:00
|
|
|
"""
|
2014-09-10 19:57:22 +04:00
|
|
|
from collections import namedtuple
|
2014-09-09 20:46:28 +04:00
|
|
|
from django.db import models
|
|
|
|
from django.utils import six
|
2014-09-10 19:57:22 +04:00
|
|
|
from django.utils.datastructures import SortedDict
|
2014-09-09 20:46:28 +04:00
|
|
|
import inspect
|
|
|
|
|
|
|
|
FieldInfo = namedtuple('FieldResult', ['pk', 'fields', 'forward_relations', 'reverse_relations'])
|
|
|
|
RelationInfo = namedtuple('RelationInfo', ['field', 'related', 'to_many', 'has_through_model'])
|
|
|
|
|
|
|
|
|
|
|
|
def _resolve_model(obj):
|
|
|
|
"""
|
|
|
|
Resolve supplied `obj` to a Django model class.
|
|
|
|
|
|
|
|
`obj` must be a Django model class itself, or a string
|
|
|
|
representation of one. Useful in situtations like GH #1225 where
|
|
|
|
Django may not have resolved a string-based reference to a model in
|
|
|
|
another model's foreign key definition.
|
|
|
|
|
|
|
|
String representations should have the format:
|
|
|
|
'appname.ModelName'
|
|
|
|
"""
|
|
|
|
if isinstance(obj, six.string_types) and len(obj.split('.')) == 2:
|
|
|
|
app_name, model_name = obj.split('.')
|
|
|
|
return models.get_model(app_name, model_name)
|
|
|
|
elif inspect.isclass(obj) and issubclass(obj, models.Model):
|
|
|
|
return obj
|
|
|
|
raise ValueError("{0} is not a Django model".format(obj))
|
|
|
|
|
|
|
|
|
|
|
|
def get_field_info(model):
|
|
|
|
"""
|
|
|
|
Given a model class, returns a `FieldInfo` instance containing metadata
|
|
|
|
about the various field types on the model.
|
|
|
|
"""
|
|
|
|
opts = model._meta.concrete_model._meta
|
|
|
|
|
|
|
|
# Deal with the primary key.
|
|
|
|
pk = opts.pk
|
|
|
|
while pk.rel and pk.rel.parent_link:
|
|
|
|
# If model is a child via multitable inheritance, use parent's pk.
|
|
|
|
pk = pk.rel.to._meta.pk
|
|
|
|
|
|
|
|
# Deal with regular fields.
|
2014-09-10 19:57:22 +04:00
|
|
|
fields = SortedDict()
|
2014-09-09 20:46:28 +04:00
|
|
|
for field in [field for field in opts.fields if field.serialize and not field.rel]:
|
|
|
|
fields[field.name] = field
|
|
|
|
|
|
|
|
# Deal with forward relationships.
|
2014-09-10 19:57:22 +04:00
|
|
|
forward_relations = SortedDict()
|
2014-09-09 20:46:28 +04:00
|
|
|
for field in [field for field in opts.fields if field.serialize and field.rel]:
|
|
|
|
forward_relations[field.name] = RelationInfo(
|
|
|
|
field=field,
|
|
|
|
related=_resolve_model(field.rel.to),
|
|
|
|
to_many=False,
|
|
|
|
has_through_model=False
|
|
|
|
)
|
|
|
|
|
|
|
|
# Deal with forward many-to-many relationships.
|
|
|
|
for field in [field for field in opts.many_to_many if field.serialize]:
|
|
|
|
forward_relations[field.name] = RelationInfo(
|
|
|
|
field=field,
|
|
|
|
related=_resolve_model(field.rel.to),
|
|
|
|
to_many=True,
|
|
|
|
has_through_model=(
|
|
|
|
not field.rel.through._meta.auto_created
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Deal with reverse relationships.
|
2014-09-10 19:57:22 +04:00
|
|
|
reverse_relations = SortedDict()
|
2014-09-09 20:46:28 +04:00
|
|
|
for relation in opts.get_all_related_objects():
|
|
|
|
accessor_name = relation.get_accessor_name()
|
|
|
|
reverse_relations[accessor_name] = RelationInfo(
|
|
|
|
field=None,
|
|
|
|
related=relation.model,
|
|
|
|
to_many=relation.field.rel.multiple,
|
|
|
|
has_through_model=False
|
|
|
|
)
|
|
|
|
|
|
|
|
# Deal with reverse many-to-many relationships.
|
|
|
|
for relation in opts.get_all_related_many_to_many_objects():
|
|
|
|
accessor_name = relation.get_accessor_name()
|
|
|
|
reverse_relations[accessor_name] = RelationInfo(
|
|
|
|
field=None,
|
|
|
|
related=relation.model,
|
|
|
|
to_many=True,
|
|
|
|
has_through_model=(
|
|
|
|
hasattr(relation.field.rel, 'through') and
|
|
|
|
not relation.field.rel.through._meta.auto_created
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
return FieldInfo(pk, fields, forward_relations, reverse_relations)
|