2014-09-08 09:01:42 +08:00
|
|
|
from importlib import import_module
|
|
|
|
|
2014-12-25 20:30:37 +08:00
|
|
|
from django.utils.version import get_docs_version
|
|
|
|
|
2014-09-09 05:28:55 +08:00
|
|
|
|
2013-10-22 01:33:57 +08:00
|
|
|
def deconstructible(*args, **kwargs):
|
|
|
|
"""
|
|
|
|
Class decorator that allow the decorated class to be serialized
|
|
|
|
by the migrations subsystem.
|
|
|
|
|
|
|
|
Accepts an optional kwarg `path` to specify the import path.
|
|
|
|
"""
|
|
|
|
path = kwargs.pop('path', None)
|
|
|
|
|
|
|
|
def decorator(klass):
|
|
|
|
def __new__(cls, *args, **kwargs):
|
|
|
|
# We capture the arguments to make returning them trivial
|
|
|
|
obj = super(klass, cls).__new__(cls)
|
|
|
|
obj._constructor_args = (args, kwargs)
|
|
|
|
return obj
|
|
|
|
|
|
|
|
def deconstruct(obj):
|
|
|
|
"""
|
|
|
|
Returns a 3-tuple of class import path, positional arguments,
|
|
|
|
and keyword arguments.
|
|
|
|
"""
|
2017-01-22 09:02:00 +08:00
|
|
|
# Fallback version
|
2014-09-08 09:01:42 +08:00
|
|
|
if path:
|
|
|
|
module_name, _, name = path.rpartition('.')
|
|
|
|
else:
|
|
|
|
module_name = obj.__module__
|
|
|
|
name = obj.__class__.__name__
|
|
|
|
# Make sure it's actually there and not an inner class
|
|
|
|
module = import_module(module_name)
|
|
|
|
if not hasattr(module, name):
|
|
|
|
raise ValueError(
|
|
|
|
"Could not find object %s in %s.\n"
|
|
|
|
"Please note that you cannot serialize things like inner "
|
|
|
|
"classes. Please move the object into the main module "
|
|
|
|
"body to use migrations.\n"
|
|
|
|
"For more information, see "
|
2014-12-25 20:30:37 +08:00
|
|
|
"https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values"
|
|
|
|
% (name, module_name, get_docs_version()))
|
2013-10-22 01:33:57 +08:00
|
|
|
return (
|
2014-09-08 09:01:42 +08:00
|
|
|
path or '%s.%s' % (obj.__class__.__module__, name),
|
2013-10-22 01:33:57 +08:00
|
|
|
obj._constructor_args[0],
|
|
|
|
obj._constructor_args[1],
|
|
|
|
)
|
|
|
|
|
|
|
|
klass.__new__ = staticmethod(__new__)
|
|
|
|
klass.deconstruct = deconstruct
|
|
|
|
|
|
|
|
return klass
|
|
|
|
|
|
|
|
if not args:
|
|
|
|
return decorator
|
|
|
|
return decorator(*args, **kwargs)
|