141 lines
4.4 KiB
Python
141 lines
4.4 KiB
Python
from __future__ import unicode_literals
|
|
|
|
import logging
|
|
import types
|
|
|
|
from django.conf import settings
|
|
from django.core.exceptions import FieldDoesNotExist
|
|
from django.db.models.constants import LOOKUP_SEP
|
|
from django.urls import resolve as django_resolve
|
|
from django.urls.base import get_script_prefix
|
|
from django.utils.six.moves import reduce as reduce_function
|
|
|
|
from mayan.apps.common.compat import dict_type, dictionary_type
|
|
|
|
from .literals import DJANGO_SQLITE_BACKEND
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def check_for_sqlite():
|
|
return settings.DATABASES['default']['ENGINE'] == DJANGO_SQLITE_BACKEND and settings.DEBUG is False
|
|
|
|
|
|
def get_related_field(model, related_field_name):
|
|
try:
|
|
local_field_name, remaining_field_path = related_field_name.split(
|
|
LOOKUP_SEP, 1
|
|
)
|
|
except ValueError:
|
|
local_field_name = related_field_name
|
|
remaining_field_path = None
|
|
|
|
related_field = model._meta.get_field(local_field_name)
|
|
|
|
if remaining_field_path:
|
|
return get_related_field(
|
|
model=related_field.related_model,
|
|
related_field_name=remaining_field_path
|
|
)
|
|
|
|
return related_field
|
|
|
|
|
|
def introspect_attribute(attribute_name, obj):
|
|
"""
|
|
Resolve the attribute of model. Supports nested reference using dotted
|
|
paths or double underscore.
|
|
"""
|
|
try:
|
|
# Try as a related field
|
|
obj._meta.get_field(field_name=attribute_name)
|
|
except (AttributeError, FieldDoesNotExist):
|
|
attribute_name = attribute_name.replace('__', '.')
|
|
|
|
try:
|
|
# If there are separators in the attribute name, traverse them
|
|
# to the final attribute
|
|
attribute_part, attribute_remaining = attribute_name.split(
|
|
'.', 1
|
|
)
|
|
except ValueError:
|
|
return attribute_name, obj
|
|
else:
|
|
related_field = obj._meta.get_field(field_name=attribute_part)
|
|
return introspect_attribute(
|
|
attribute_name=attribute_part,
|
|
obj=related_field.related_model,
|
|
)
|
|
else:
|
|
return attribute_name, obj
|
|
|
|
|
|
def resolve(path, urlconf=None):
|
|
path = '/{}'.format(path.replace(get_script_prefix(), '', 1))
|
|
return django_resolve(path=path, urlconf=urlconf)
|
|
|
|
|
|
def resolve_attribute(attribute, obj, kwargs=None):
|
|
"""
|
|
Resolve the attribute of an object. Behaves like the Python REPL but with
|
|
an unified dotted path schema regardless of the attribute type.
|
|
Supports callables, dictionaries, properties, related model fields.
|
|
"""
|
|
if not kwargs:
|
|
kwargs = {}
|
|
|
|
# Try as a callable
|
|
try:
|
|
return attribute(obj, **kwargs)
|
|
except TypeError:
|
|
# Try as a dictionary
|
|
try:
|
|
return obj[attribute]
|
|
except TypeError:
|
|
try:
|
|
# If there are dots in the attribute name, traverse them
|
|
# to the final attribute
|
|
result = reduce_function(getattr, attribute.split('.'), obj)
|
|
try:
|
|
# Try it as a method
|
|
return result(**kwargs)
|
|
except TypeError:
|
|
# Try it as a property
|
|
return result
|
|
except AttributeError:
|
|
# Try as a related model field
|
|
if LOOKUP_SEP in attribute:
|
|
attribute_replaced = attribute.replace(LOOKUP_SEP, '.')
|
|
return resolve_attribute(
|
|
obj=obj, attribute=attribute_replaced, kwargs=kwargs
|
|
)
|
|
else:
|
|
raise
|
|
|
|
|
|
def return_attrib(obj, attrib, arguments=None):
|
|
if isinstance(attrib, types.FunctionType):
|
|
return attrib(obj)
|
|
elif isinstance(
|
|
obj, dict_type
|
|
) or isinstance(obj, dictionary_type):
|
|
return obj[attrib]
|
|
else:
|
|
result = reduce_function(getattr, attrib.split('.'), obj)
|
|
if isinstance(result, types.MethodType):
|
|
if arguments:
|
|
return result(**arguments)
|
|
else:
|
|
return result()
|
|
else:
|
|
return result
|
|
|
|
|
|
def return_related(instance, related_field):
|
|
"""
|
|
This functions works in a similar method to return_attrib but is
|
|
meant for related models. Support multiple levels of relationship
|
|
using double underscore.
|
|
"""
|
|
return reduce_function(getattr, related_field.split('__'), instance)
|