Fixed metaclass to handle proxy correctly. Some issues with getting path properties set correctly.
This commit is contained in:
parent
32e44dceab
commit
043ebf7213
6 changed files with 289 additions and 265 deletions
|
|
@ -3,6 +3,7 @@ Default Typeclass for Comms.
|
||||||
|
|
||||||
See objects.objects for more information on Typeclassing.
|
See objects.objects for more information on Typeclassing.
|
||||||
"""
|
"""
|
||||||
|
from src.typeclasses.models import TypeclassBase
|
||||||
from src.comms.models import Msg, TempMsg, ChannelDB
|
from src.comms.models import Msg, TempMsg, ChannelDB
|
||||||
from src.utils import logger
|
from src.utils import logger
|
||||||
from src.utils.utils import make_iter
|
from src.utils.utils import make_iter
|
||||||
|
|
@ -13,7 +14,7 @@ class Channel(ChannelDB):
|
||||||
This is the base class for all Comms. Inherit from this to create different
|
This is the base class for all Comms. Inherit from this to create different
|
||||||
types of communication channels.
|
types of communication channels.
|
||||||
"""
|
"""
|
||||||
_is_typeclass = True
|
__metaclass__ = TypeclassBase
|
||||||
|
|
||||||
# helper methods, for easy overloading
|
# helper methods, for easy overloading
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ they control by simply linking to a new object's user property.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from src.typeclasses.models import TypeclassBase
|
||||||
from src.objects.models import ObjectDB
|
from src.objects.models import ObjectDB
|
||||||
from src.commands import cmdset, command
|
from src.commands import cmdset, command
|
||||||
from src.utils.logger import log_depmsg
|
from src.utils.logger import log_depmsg
|
||||||
|
|
@ -35,8 +36,9 @@ class Object(ObjectDB):
|
||||||
"""
|
"""
|
||||||
This is the base class for all in-game objects. Inherit from this
|
This is the base class for all in-game objects. Inherit from this
|
||||||
to create different types of objects in the game.
|
to create different types of objects in the game.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
_is_typeclass = True
|
__metaclass__ = TypeclassBase
|
||||||
|
|
||||||
# __init__ is only defined here in order to present docstring to API.
|
# __init__ is only defined here in order to present docstring to API.
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ instead for most things).
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from src.typeclasses.models import TypeclassBase
|
||||||
from src.players.models import PlayerDB
|
from src.players.models import PlayerDB
|
||||||
from src.comms.models import ChannelDB
|
from src.comms.models import ChannelDB
|
||||||
from src.utils import logger
|
from src.utils import logger
|
||||||
|
|
@ -26,7 +27,7 @@ class Player(PlayerDB):
|
||||||
"""
|
"""
|
||||||
Base typeclass for all Players.
|
Base typeclass for all Players.
|
||||||
"""
|
"""
|
||||||
_is_typeclass = True
|
__metaclass__ = TypeclassBase
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ It also defines a few common scripts.
|
||||||
from twisted.internet.defer import Deferred, maybeDeferred
|
from twisted.internet.defer import Deferred, maybeDeferred
|
||||||
from twisted.internet.task import LoopingCall
|
from twisted.internet.task import LoopingCall
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from src.typeclasses.models import TypeclassBase
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from src.scripts.models import ScriptDB
|
from src.scripts.models import ScriptDB
|
||||||
from src.comms import channelhandler
|
from src.comms import channelhandler
|
||||||
|
|
@ -112,7 +113,7 @@ class ScriptBase(ScriptDB):
|
||||||
Base class for scripts. Don't inherit from this, inherit
|
Base class for scripts. Don't inherit from this, inherit
|
||||||
from the class 'Script' instead.
|
from the class 'Script' instead.
|
||||||
"""
|
"""
|
||||||
_is_typeclass = True
|
__metaclass__ = TypeclassBase
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ from src.server.models import ServerConfig
|
||||||
from src.typeclasses import managers
|
from src.typeclasses import managers
|
||||||
from src.locks.lockhandler import LockHandler
|
from src.locks.lockhandler import LockHandler
|
||||||
from src.utils import logger
|
from src.utils import logger
|
||||||
|
from django.db.models.base import ModelBase
|
||||||
from src.utils.utils import (
|
from src.utils.utils import (
|
||||||
make_iter, is_iter, to_str, inherits_from, lazy_property)
|
make_iter, is_iter, to_str, inherits_from, lazy_property)
|
||||||
from src.utils.dbserialize import to_pickle, from_pickle
|
from src.utils.dbserialize import to_pickle, from_pickle
|
||||||
|
|
@ -739,26 +740,285 @@ class PermissionHandler(TagHandler):
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
||||||
# imported for access by other
|
|
||||||
#from src.utils.idmapper.base import SharedMemoryModelBase
|
|
||||||
|
|
||||||
#class TypeclassBase(SharedMemoryModelBase):
|
#
|
||||||
# """
|
# Meta class for typeclasses
|
||||||
# Metaclass which should be set for the root of model proxies
|
#
|
||||||
# that don't define any new fields, like Object, Script etc.
|
|
||||||
# """
|
# django patch imports
|
||||||
# def __new__(cls, name, bases, attrs):
|
import copy
|
||||||
# """
|
from django.apps import apps
|
||||||
# We must define our Typeclasses as proxies. We also store the path
|
from django.db.models.base import subclass_exception
|
||||||
# directly on the class, this is useful for managers.
|
import warnings
|
||||||
# """
|
from django.db.models.options import Options
|
||||||
# if hasattr(cls, "Meta"):
|
from django.utils.deprecation import RemovedInDjango19Warning
|
||||||
# cls.Meta.proxy = True
|
from django.core.exceptions import MultipleObjectsReturned
|
||||||
# else:
|
from django.apps.config import MODELS_MODULE_NAME
|
||||||
# class Meta:
|
from django.db.models.fields.related import OneToOneField
|
||||||
# proxy = True
|
#/ django patch imports
|
||||||
# cls.Meta = Meta
|
|
||||||
# return super(TypeclassBase, cls).__new__(name, bases, attrs)
|
from src.utils.idmapper.base import SharedMemoryModelBase
|
||||||
|
|
||||||
|
class TypeclassBase(SharedMemoryModelBase):
|
||||||
|
"""
|
||||||
|
Metaclass which should be set for the root of model proxies
|
||||||
|
that don't define any new fields, like Object, Script etc.
|
||||||
|
"""
|
||||||
|
def __new__(cls, name, bases, attrs):
|
||||||
|
"""
|
||||||
|
We must define our Typeclasses as proxies. We also store the path
|
||||||
|
directly on the class, this is useful for managers.
|
||||||
|
"""
|
||||||
|
# typeclass proxy setup
|
||||||
|
if "Meta" in attrs:
|
||||||
|
attrs["Meta"].proxy = True
|
||||||
|
else:
|
||||||
|
class Meta:
|
||||||
|
proxy = True
|
||||||
|
attrs["Meta"] = Meta
|
||||||
|
|
||||||
|
|
||||||
|
# patch start - django multi-inheritance
|
||||||
|
# this is a copy of django.db.models.base.__new__
|
||||||
|
# with a few lines (marked patch below) changed
|
||||||
|
# as per https://code.djangoproject.com/ticket/11560
|
||||||
|
|
||||||
|
super_new = super(ModelBase, cls).__new__
|
||||||
|
|
||||||
|
# Also ensure initialization is only performed for subclasses of Model
|
||||||
|
# (excluding Model class itself).
|
||||||
|
parents = [b for b in bases if isinstance(b, ModelBase)]
|
||||||
|
if not parents:
|
||||||
|
return super_new(cls, name, bases, attrs)
|
||||||
|
|
||||||
|
# Create the class.
|
||||||
|
module = attrs.pop('__module__')
|
||||||
|
new_class = super_new(cls, name, bases, {'__module__': module})
|
||||||
|
attr_meta = attrs.pop('Meta', None)
|
||||||
|
abstract = getattr(attr_meta, 'abstract', False)
|
||||||
|
if not attr_meta:
|
||||||
|
meta = getattr(new_class, 'Meta', None)
|
||||||
|
else:
|
||||||
|
meta = attr_meta
|
||||||
|
base_meta = getattr(new_class, '_meta', None)
|
||||||
|
|
||||||
|
# Look for an application configuration to attach the model to.
|
||||||
|
app_config = apps.get_containing_app_config(module)
|
||||||
|
|
||||||
|
if getattr(meta, 'app_label', None) is None:
|
||||||
|
|
||||||
|
if app_config is None:
|
||||||
|
# If the model is imported before the configuration for its
|
||||||
|
# application is created (#21719), or isn't in an installed
|
||||||
|
# application (#21680), use the legacy logic to figure out the
|
||||||
|
# app_label by looking one level up from the package or module
|
||||||
|
# named 'models'. If no such package or module exists, fall
|
||||||
|
# back to looking one level up from the module this model is
|
||||||
|
# defined in.
|
||||||
|
|
||||||
|
# For 'django.contrib.sites.models', this would be 'sites'.
|
||||||
|
# For 'geo.models.places' this would be 'geo'.
|
||||||
|
|
||||||
|
msg = (
|
||||||
|
"Model class %s.%s doesn't declare an explicit app_label "
|
||||||
|
"and either isn't in an application in INSTALLED_APPS or "
|
||||||
|
"else was imported before its application was loaded. " %
|
||||||
|
(module, name))
|
||||||
|
if abstract:
|
||||||
|
msg += "Its app_label will be set to None in Django 1.9."
|
||||||
|
else:
|
||||||
|
msg += "This will no longer be supported in Django 1.9."
|
||||||
|
warnings.warn(msg, RemovedInDjango19Warning, stacklevel=2)
|
||||||
|
|
||||||
|
model_module = sys.modules[new_class.__module__]
|
||||||
|
package_components = model_module.__name__.split('.')
|
||||||
|
package_components.reverse() # find the last occurrence of 'models'
|
||||||
|
try:
|
||||||
|
app_label_index = package_components.index(MODELS_MODULE_NAME) + 1
|
||||||
|
except ValueError:
|
||||||
|
app_label_index = 1
|
||||||
|
kwargs = {"app_label": package_components[app_label_index]}
|
||||||
|
|
||||||
|
else:
|
||||||
|
kwargs = {"app_label": app_config.label}
|
||||||
|
|
||||||
|
else:
|
||||||
|
kwargs = {}
|
||||||
|
|
||||||
|
new_class.add_to_class('_meta', Options(meta, **kwargs))
|
||||||
|
if not abstract:
|
||||||
|
new_class.add_to_class(
|
||||||
|
'DoesNotExist',
|
||||||
|
subclass_exception(
|
||||||
|
str('DoesNotExist'),
|
||||||
|
tuple(x.DoesNotExist for x in parents if hasattr(x, '_meta') and not x._meta.abstract) or (ObjectDoesNotExist,),
|
||||||
|
module,
|
||||||
|
attached_to=new_class))
|
||||||
|
new_class.add_to_class(
|
||||||
|
'MultipleObjectsReturned',
|
||||||
|
subclass_exception(
|
||||||
|
str('MultipleObjectsReturned'),
|
||||||
|
tuple(x.MultipleObjectsReturned for x in parents if hasattr(x, '_meta') and not x._meta.abstract) or (MultipleObjectsReturned,),
|
||||||
|
module,
|
||||||
|
attached_to=new_class))
|
||||||
|
if base_meta and not base_meta.abstract:
|
||||||
|
# Non-abstract child classes inherit some attributes from their
|
||||||
|
# non-abstract parent (unless an ABC comes before it in the
|
||||||
|
# method resolution order).
|
||||||
|
if not hasattr(meta, 'ordering'):
|
||||||
|
new_class._meta.ordering = base_meta.ordering
|
||||||
|
if not hasattr(meta, 'get_latest_by'):
|
||||||
|
new_class._meta.get_latest_by = base_meta.get_latest_by
|
||||||
|
|
||||||
|
is_proxy = new_class._meta.proxy
|
||||||
|
|
||||||
|
# If the model is a proxy, ensure that the base class
|
||||||
|
# hasn't been swapped out.
|
||||||
|
if is_proxy and base_meta and base_meta.swapped:
|
||||||
|
raise TypeError("%s cannot proxy the swapped model '%s'." % (name, base_meta.swapped))
|
||||||
|
|
||||||
|
if getattr(new_class, '_default_manager', None):
|
||||||
|
if not is_proxy:
|
||||||
|
# Multi-table inheritance doesn't inherit default manager from
|
||||||
|
# parents.
|
||||||
|
new_class._default_manager = None
|
||||||
|
new_class._base_manager = None
|
||||||
|
else:
|
||||||
|
# Proxy classes do inherit parent's default manager, if none is
|
||||||
|
# set explicitly.
|
||||||
|
new_class._default_manager = new_class._default_manager._copy_to_model(new_class)
|
||||||
|
new_class._base_manager = new_class._base_manager._copy_to_model(new_class)
|
||||||
|
|
||||||
|
# Add all attributes to the class.
|
||||||
|
for obj_name, obj in attrs.items():
|
||||||
|
new_class.add_to_class(obj_name, obj)
|
||||||
|
|
||||||
|
# All the fields of any type declared on this model
|
||||||
|
new_fields = (
|
||||||
|
new_class._meta.local_fields +
|
||||||
|
new_class._meta.local_many_to_many +
|
||||||
|
new_class._meta.virtual_fields
|
||||||
|
)
|
||||||
|
field_names = set(f.name for f in new_fields)
|
||||||
|
|
||||||
|
# Basic setup for proxy models.
|
||||||
|
if is_proxy:
|
||||||
|
base = None
|
||||||
|
for parent in [kls for kls in parents if hasattr(kls, '_meta')]:
|
||||||
|
if parent._meta.abstract:
|
||||||
|
if parent._meta.fields:
|
||||||
|
raise TypeError("Abstract base class containing model fields not permitted for proxy model '%s'." % name)
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
#if base is not None: # patch
|
||||||
|
while parent._meta.proxy: # patch
|
||||||
|
parent = parent._meta.proxy_for_model # patch
|
||||||
|
if base is not None and base is not parent: # patch
|
||||||
|
raise TypeError("Proxy model '%s' has more than one non-abstract model base class." % name)
|
||||||
|
else:
|
||||||
|
base = parent
|
||||||
|
if base is None:
|
||||||
|
raise TypeError("Proxy model '%s' has no non-abstract model base class." % name)
|
||||||
|
new_class._meta.setup_proxy(base)
|
||||||
|
new_class._meta.concrete_model = base._meta.concrete_model
|
||||||
|
else:
|
||||||
|
new_class._meta.concrete_model = new_class
|
||||||
|
|
||||||
|
# Collect the parent links for multi-table inheritance.
|
||||||
|
parent_links = {}
|
||||||
|
for base in reversed([new_class] + parents):
|
||||||
|
# Conceptually equivalent to `if base is Model`.
|
||||||
|
if not hasattr(base, '_meta'):
|
||||||
|
continue
|
||||||
|
# Skip concrete parent classes.
|
||||||
|
if base != new_class and not base._meta.abstract:
|
||||||
|
continue
|
||||||
|
# Locate OneToOneField instances.
|
||||||
|
for field in base._meta.local_fields:
|
||||||
|
if isinstance(field, OneToOneField):
|
||||||
|
parent_links[field.rel.to] = field
|
||||||
|
|
||||||
|
# Do the appropriate setup for any model parents.
|
||||||
|
for base in parents:
|
||||||
|
original_base = base
|
||||||
|
if not hasattr(base, '_meta'):
|
||||||
|
# Things without _meta aren't functional models, so they're
|
||||||
|
# uninteresting parents.
|
||||||
|
continue
|
||||||
|
|
||||||
|
parent_fields = base._meta.local_fields + base._meta.local_many_to_many
|
||||||
|
# Check for clashes between locally declared fields and those
|
||||||
|
# on the base classes (we cannot handle shadowed fields at the
|
||||||
|
# moment).
|
||||||
|
for field in parent_fields:
|
||||||
|
if field.name in field_names:
|
||||||
|
raise FieldError(
|
||||||
|
'Local field %r in class %r clashes '
|
||||||
|
'with field of similar name from '
|
||||||
|
'base class %r' % (field.name, name, base.__name__)
|
||||||
|
)
|
||||||
|
if not base._meta.abstract:
|
||||||
|
# Concrete classes...
|
||||||
|
base = base._meta.concrete_model
|
||||||
|
if base in parent_links:
|
||||||
|
field = parent_links[base]
|
||||||
|
elif not is_proxy:
|
||||||
|
attr_name = '%s_ptr' % base._meta.model_name
|
||||||
|
field = OneToOneField(base, name=attr_name,
|
||||||
|
auto_created=True, parent_link=True)
|
||||||
|
# Only add the ptr field if it's not already present;
|
||||||
|
# e.g. migrations will already have it specified
|
||||||
|
if not hasattr(new_class, attr_name):
|
||||||
|
new_class.add_to_class(attr_name, field)
|
||||||
|
else:
|
||||||
|
field = None
|
||||||
|
new_class._meta.parents[base] = field
|
||||||
|
else:
|
||||||
|
# .. and abstract ones.
|
||||||
|
for field in parent_fields:
|
||||||
|
new_class.add_to_class(field.name, copy.deepcopy(field))
|
||||||
|
|
||||||
|
# Pass any non-abstract parent classes onto child.
|
||||||
|
new_class._meta.parents.update(base._meta.parents)
|
||||||
|
|
||||||
|
# Inherit managers from the abstract base classes.
|
||||||
|
new_class.copy_managers(base._meta.abstract_managers)
|
||||||
|
|
||||||
|
# Proxy models inherit the non-abstract managers from their base,
|
||||||
|
# unless they have redefined any of them.
|
||||||
|
if is_proxy:
|
||||||
|
new_class.copy_managers(original_base._meta.concrete_managers)
|
||||||
|
|
||||||
|
# Inherit virtual fields (like GenericForeignKey) from the parent
|
||||||
|
# class
|
||||||
|
for field in base._meta.virtual_fields:
|
||||||
|
if base._meta.abstract and field.name in field_names:
|
||||||
|
raise FieldError(
|
||||||
|
'Local field %r in class %r clashes '
|
||||||
|
'with field of similar name from '
|
||||||
|
'abstract base class %r' % (field.name, name, base.__name__)
|
||||||
|
)
|
||||||
|
new_class.add_to_class(field.name, copy.deepcopy(field))
|
||||||
|
|
||||||
|
if abstract:
|
||||||
|
# Abstract base models can't be instantiated and don't appear in
|
||||||
|
# the list of models for an app. We do the final setup for them a
|
||||||
|
# little differently from normal models.
|
||||||
|
attr_meta.abstract = False
|
||||||
|
new_class.Meta = attr_meta
|
||||||
|
return new_class
|
||||||
|
|
||||||
|
new_class._prepare()
|
||||||
|
new_class._meta.apps.register_model(new_class._meta.app_label, new_class)
|
||||||
|
return new_class
|
||||||
|
|
||||||
|
# /patch end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Main TypedObject abstraction
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
class TypedObject(SharedMemoryModel):
|
class TypedObject(SharedMemoryModel):
|
||||||
|
|
|
||||||
|
|
@ -95,14 +95,6 @@ class SharedMemoryModelBase(ModelBase):
|
||||||
document this auto-wrapping in the class header, this could seem very much like magic to the user otherwise.
|
document this auto-wrapping in the class header, this could seem very much like magic to the user otherwise.
|
||||||
"""
|
"""
|
||||||
# set up the typeclass handling only if a variable _is_typeclass is set on the class
|
# set up the typeclass handling only if a variable _is_typeclass is set on the class
|
||||||
if "_is_typeclass" in attrs:
|
|
||||||
if "Meta" in attrs:
|
|
||||||
attrs["Meta"].proxy = True
|
|
||||||
else:
|
|
||||||
class Meta:
|
|
||||||
proxy = True
|
|
||||||
attrs["Meta"] = Meta
|
|
||||||
|
|
||||||
def create_wrapper(cls, fieldname, wrappername, editable=True, foreignkey=False):
|
def create_wrapper(cls, fieldname, wrappername, editable=True, foreignkey=False):
|
||||||
"Helper method to create property wrappers with unique names (must be in separate call)"
|
"Helper method to create property wrappers with unique names (must be in separate call)"
|
||||||
def _get(cls, fname):
|
def _get(cls, fname):
|
||||||
|
|
@ -197,240 +189,7 @@ class SharedMemoryModelBase(ModelBase):
|
||||||
#print "wrapping %s -> %s" % (fieldname, wrappername)
|
#print "wrapping %s -> %s" % (fieldname, wrappername)
|
||||||
create_wrapper(cls, fieldname, wrappername, editable=field.editable, foreignkey=foreignkey)
|
create_wrapper(cls, fieldname, wrappername, editable=field.editable, foreignkey=foreignkey)
|
||||||
|
|
||||||
# patch start
|
return super(SharedMemoryModelBase, cls).__new__(cls, name, bases, attrs)
|
||||||
|
|
||||||
super_new = super(ModelBase, cls).__new__
|
|
||||||
|
|
||||||
# Also ensure initialization is only performed for subclasses of Model
|
|
||||||
# (excluding Model class itself).
|
|
||||||
parents = [b for b in bases if isinstance(b, ModelBase)]
|
|
||||||
if not parents:
|
|
||||||
return super_new(cls, name, bases, attrs)
|
|
||||||
|
|
||||||
# Create the class.
|
|
||||||
module = attrs.pop('__module__')
|
|
||||||
new_class = super_new(cls, name, bases, {'__module__': module})
|
|
||||||
attr_meta = attrs.pop('Meta', None)
|
|
||||||
abstract = getattr(attr_meta, 'abstract', False)
|
|
||||||
if not attr_meta:
|
|
||||||
meta = getattr(new_class, 'Meta', None)
|
|
||||||
else:
|
|
||||||
meta = attr_meta
|
|
||||||
base_meta = getattr(new_class, '_meta', None)
|
|
||||||
|
|
||||||
# Look for an application configuration to attach the model to.
|
|
||||||
app_config = apps.get_containing_app_config(module)
|
|
||||||
|
|
||||||
if getattr(meta, 'app_label', None) is None:
|
|
||||||
|
|
||||||
if app_config is None:
|
|
||||||
# If the model is imported before the configuration for its
|
|
||||||
# application is created (#21719), or isn't in an installed
|
|
||||||
# application (#21680), use the legacy logic to figure out the
|
|
||||||
# app_label by looking one level up from the package or module
|
|
||||||
# named 'models'. If no such package or module exists, fall
|
|
||||||
# back to looking one level up from the module this model is
|
|
||||||
# defined in.
|
|
||||||
|
|
||||||
# For 'django.contrib.sites.models', this would be 'sites'.
|
|
||||||
# For 'geo.models.places' this would be 'geo'.
|
|
||||||
|
|
||||||
msg = (
|
|
||||||
"Model class %s.%s doesn't declare an explicit app_label "
|
|
||||||
"and either isn't in an application in INSTALLED_APPS or "
|
|
||||||
"else was imported before its application was loaded. " %
|
|
||||||
(module, name))
|
|
||||||
if abstract:
|
|
||||||
msg += "Its app_label will be set to None in Django 1.9."
|
|
||||||
else:
|
|
||||||
msg += "This will no longer be supported in Django 1.9."
|
|
||||||
warnings.warn(msg, RemovedInDjango19Warning, stacklevel=2)
|
|
||||||
|
|
||||||
model_module = sys.modules[new_class.__module__]
|
|
||||||
package_components = model_module.__name__.split('.')
|
|
||||||
package_components.reverse() # find the last occurrence of 'models'
|
|
||||||
try:
|
|
||||||
app_label_index = package_components.index(MODELS_MODULE_NAME) + 1
|
|
||||||
except ValueError:
|
|
||||||
app_label_index = 1
|
|
||||||
kwargs = {"app_label": package_components[app_label_index]}
|
|
||||||
|
|
||||||
else:
|
|
||||||
kwargs = {"app_label": app_config.label}
|
|
||||||
|
|
||||||
else:
|
|
||||||
kwargs = {}
|
|
||||||
|
|
||||||
new_class.add_to_class('_meta', Options(meta, **kwargs))
|
|
||||||
if not abstract:
|
|
||||||
new_class.add_to_class(
|
|
||||||
'DoesNotExist',
|
|
||||||
subclass_exception(
|
|
||||||
str('DoesNotExist'),
|
|
||||||
tuple(x.DoesNotExist for x in parents if hasattr(x, '_meta') and not x._meta.abstract) or (ObjectDoesNotExist,),
|
|
||||||
module,
|
|
||||||
attached_to=new_class))
|
|
||||||
new_class.add_to_class(
|
|
||||||
'MultipleObjectsReturned',
|
|
||||||
subclass_exception(
|
|
||||||
str('MultipleObjectsReturned'),
|
|
||||||
tuple(x.MultipleObjectsReturned for x in parents if hasattr(x, '_meta') and not x._meta.abstract) or (MultipleObjectsReturned,),
|
|
||||||
module,
|
|
||||||
attached_to=new_class))
|
|
||||||
if base_meta and not base_meta.abstract:
|
|
||||||
# Non-abstract child classes inherit some attributes from their
|
|
||||||
# non-abstract parent (unless an ABC comes before it in the
|
|
||||||
# method resolution order).
|
|
||||||
if not hasattr(meta, 'ordering'):
|
|
||||||
new_class._meta.ordering = base_meta.ordering
|
|
||||||
if not hasattr(meta, 'get_latest_by'):
|
|
||||||
new_class._meta.get_latest_by = base_meta.get_latest_by
|
|
||||||
|
|
||||||
is_proxy = new_class._meta.proxy
|
|
||||||
|
|
||||||
# If the model is a proxy, ensure that the base class
|
|
||||||
# hasn't been swapped out.
|
|
||||||
if is_proxy and base_meta and base_meta.swapped:
|
|
||||||
raise TypeError("%s cannot proxy the swapped model '%s'." % (name, base_meta.swapped))
|
|
||||||
|
|
||||||
if getattr(new_class, '_default_manager', None):
|
|
||||||
if not is_proxy:
|
|
||||||
# Multi-table inheritance doesn't inherit default manager from
|
|
||||||
# parents.
|
|
||||||
new_class._default_manager = None
|
|
||||||
new_class._base_manager = None
|
|
||||||
else:
|
|
||||||
# Proxy classes do inherit parent's default manager, if none is
|
|
||||||
# set explicitly.
|
|
||||||
new_class._default_manager = new_class._default_manager._copy_to_model(new_class)
|
|
||||||
new_class._base_manager = new_class._base_manager._copy_to_model(new_class)
|
|
||||||
|
|
||||||
# Add all attributes to the class.
|
|
||||||
for obj_name, obj in attrs.items():
|
|
||||||
new_class.add_to_class(obj_name, obj)
|
|
||||||
|
|
||||||
# All the fields of any type declared on this model
|
|
||||||
new_fields = (
|
|
||||||
new_class._meta.local_fields +
|
|
||||||
new_class._meta.local_many_to_many +
|
|
||||||
new_class._meta.virtual_fields
|
|
||||||
)
|
|
||||||
field_names = set(f.name for f in new_fields)
|
|
||||||
|
|
||||||
# Basic setup for proxy models.
|
|
||||||
if is_proxy:
|
|
||||||
base = None
|
|
||||||
for parent in [kls for kls in parents if hasattr(kls, '_meta')]:
|
|
||||||
if parent._meta.abstract:
|
|
||||||
if parent._meta.fields:
|
|
||||||
raise TypeError("Abstract base class containing model fields not permitted for proxy model '%s'." % name)
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
if base is not None:
|
|
||||||
raise TypeError("Proxy model '%s' has more than one non-abstract model base class." % name)
|
|
||||||
else:
|
|
||||||
base = parent
|
|
||||||
#if base is None: # patch
|
|
||||||
while parent._meta.proxy: # patch
|
|
||||||
parent = parent._meta.proxy_for_model # patch
|
|
||||||
if base is not None and base is not parent: # patch
|
|
||||||
raise TypeError("Proxy model '%s' has no non-abstract model base class." % name)
|
|
||||||
new_class._meta.setup_proxy(base)
|
|
||||||
new_class._meta.concrete_model = base._meta.concrete_model
|
|
||||||
else:
|
|
||||||
new_class._meta.concrete_model = new_class
|
|
||||||
|
|
||||||
# Collect the parent links for multi-table inheritance.
|
|
||||||
parent_links = {}
|
|
||||||
for base in reversed([new_class] + parents):
|
|
||||||
# Conceptually equivalent to `if base is Model`.
|
|
||||||
if not hasattr(base, '_meta'):
|
|
||||||
continue
|
|
||||||
# Skip concrete parent classes.
|
|
||||||
if base != new_class and not base._meta.abstract:
|
|
||||||
continue
|
|
||||||
# Locate OneToOneField instances.
|
|
||||||
for field in base._meta.local_fields:
|
|
||||||
if isinstance(field, OneToOneField):
|
|
||||||
parent_links[field.rel.to] = field
|
|
||||||
|
|
||||||
# Do the appropriate setup for any model parents.
|
|
||||||
for base in parents:
|
|
||||||
original_base = base
|
|
||||||
if not hasattr(base, '_meta'):
|
|
||||||
# Things without _meta aren't functional models, so they're
|
|
||||||
# uninteresting parents.
|
|
||||||
continue
|
|
||||||
|
|
||||||
parent_fields = base._meta.local_fields + base._meta.local_many_to_many
|
|
||||||
# Check for clashes between locally declared fields and those
|
|
||||||
# on the base classes (we cannot handle shadowed fields at the
|
|
||||||
# moment).
|
|
||||||
for field in parent_fields:
|
|
||||||
if field.name in field_names:
|
|
||||||
raise FieldError(
|
|
||||||
'Local field %r in class %r clashes '
|
|
||||||
'with field of similar name from '
|
|
||||||
'base class %r' % (field.name, name, base.__name__)
|
|
||||||
)
|
|
||||||
if not base._meta.abstract:
|
|
||||||
# Concrete classes...
|
|
||||||
base = base._meta.concrete_model
|
|
||||||
if base in parent_links:
|
|
||||||
field = parent_links[base]
|
|
||||||
elif not is_proxy:
|
|
||||||
attr_name = '%s_ptr' % base._meta.model_name
|
|
||||||
field = OneToOneField(base, name=attr_name,
|
|
||||||
auto_created=True, parent_link=True)
|
|
||||||
# Only add the ptr field if it's not already present;
|
|
||||||
# e.g. migrations will already have it specified
|
|
||||||
if not hasattr(new_class, attr_name):
|
|
||||||
new_class.add_to_class(attr_name, field)
|
|
||||||
else:
|
|
||||||
field = None
|
|
||||||
new_class._meta.parents[base] = field
|
|
||||||
else:
|
|
||||||
# .. and abstract ones.
|
|
||||||
for field in parent_fields:
|
|
||||||
new_class.add_to_class(field.name, copy.deepcopy(field))
|
|
||||||
|
|
||||||
# Pass any non-abstract parent classes onto child.
|
|
||||||
new_class._meta.parents.update(base._meta.parents)
|
|
||||||
|
|
||||||
# Inherit managers from the abstract base classes.
|
|
||||||
new_class.copy_managers(base._meta.abstract_managers)
|
|
||||||
|
|
||||||
# Proxy models inherit the non-abstract managers from their base,
|
|
||||||
# unless they have redefined any of them.
|
|
||||||
if is_proxy:
|
|
||||||
new_class.copy_managers(original_base._meta.concrete_managers)
|
|
||||||
|
|
||||||
# Inherit virtual fields (like GenericForeignKey) from the parent
|
|
||||||
# class
|
|
||||||
for field in base._meta.virtual_fields:
|
|
||||||
if base._meta.abstract and field.name in field_names:
|
|
||||||
raise FieldError(
|
|
||||||
'Local field %r in class %r clashes '
|
|
||||||
'with field of similar name from '
|
|
||||||
'abstract base class %r' % (field.name, name, base.__name__)
|
|
||||||
)
|
|
||||||
new_class.add_to_class(field.name, copy.deepcopy(field))
|
|
||||||
|
|
||||||
if abstract:
|
|
||||||
# Abstract base models can't be instantiated and don't appear in
|
|
||||||
# the list of models for an app. We do the final setup for them a
|
|
||||||
# little differently from normal models.
|
|
||||||
attr_meta.abstract = False
|
|
||||||
new_class.Meta = attr_meta
|
|
||||||
return new_class
|
|
||||||
|
|
||||||
new_class._prepare()
|
|
||||||
new_class._meta.apps.register_model(new_class._meta.app_label, new_class)
|
|
||||||
return new_class
|
|
||||||
|
|
||||||
|
|
||||||
# /patch end
|
|
||||||
#return super(SharedMemoryModelBase, cls).__new__(cls, name, bases, attrs, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class SharedMemoryModel(Model):
|
class SharedMemoryModel(Model):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue