Don't import models and managers in __init__
This is deprecated as of django 1.8, and does not work as of django 1.9.fix_request_path_info
parent
6a86a5ce91
commit
c48d4f589b
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet
|
from polymorphic.models import PolymorphicModel
|
||||||
from polymorphic.showfields import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent
|
from polymorphic.showfields import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent
|
||||||
|
|
||||||
class Project(ShowFieldContent, PolymorphicModel):
|
class Project(ShowFieldContent, PolymorphicModel):
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,6 @@ Please see LICENSE and AUTHORS for more information.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
import django
|
import django
|
||||||
from .polymorphic_model import PolymorphicModel
|
|
||||||
from .manager import PolymorphicManager
|
|
||||||
from .query import PolymorphicQuerySet
|
|
||||||
from .query_translate import translate_polymorphic_Q_object
|
|
||||||
from .showfields import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent
|
from .showfields import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent
|
||||||
from .showfields import ShowFields, ShowFieldTypes, ShowFieldsAndTypes # import old names for compatibility
|
from .showfields import ShowFields, ShowFieldTypes, ShowFieldsAndTypes # import old names for compatibility
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,229 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
IMPORTANT:
|
Seamless Polymorphic Inheritance for Django Models
|
||||||
|
==================================================
|
||||||
|
|
||||||
The models.py module is not used anymore.
|
Please see README.rst and DOCS.rst for further information.
|
||||||
Please use the following import method in your apps:
|
|
||||||
|
|
||||||
from polymorphic import PolymorphicModel, ...
|
Or on the Web:
|
||||||
|
http://chrisglass.github.com/django_polymorphic/
|
||||||
|
http://github.com/chrisglass/django_polymorphic
|
||||||
|
|
||||||
|
Copyright:
|
||||||
|
This code and affiliated files are (C) by Bert Constantin and individual contributors.
|
||||||
|
Please see LICENSE and AUTHORS for more information.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.utils import six
|
||||||
|
|
||||||
|
from .base import PolymorphicModelBase
|
||||||
|
from .manager import PolymorphicManager
|
||||||
|
from .query_translate import translate_polymorphic_Q_object
|
||||||
|
|
||||||
|
###################################################################################
|
||||||
|
### PolymorphicModel
|
||||||
|
|
||||||
|
class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)):
|
||||||
|
"""
|
||||||
|
Abstract base class that provides polymorphic behaviour
|
||||||
|
for any model directly or indirectly derived from it.
|
||||||
|
|
||||||
|
For usage instructions & examples please see documentation.
|
||||||
|
|
||||||
|
PolymorphicModel declares one field for internal use (polymorphic_ctype)
|
||||||
|
and provides a polymorphic manager as the default manager
|
||||||
|
(and as 'objects').
|
||||||
|
|
||||||
|
PolymorphicModel overrides the save() and __init__ methods.
|
||||||
|
|
||||||
|
If your derived class overrides any of these methods as well, then you need
|
||||||
|
to take care that you correctly call the method of the superclass, like:
|
||||||
|
|
||||||
|
super(YourClass,self).save(*args,**kwargs)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# for PolymorphicModelBase, so it can tell which models are polymorphic and which are not (duck typing)
|
||||||
|
polymorphic_model_marker = True
|
||||||
|
|
||||||
|
# for PolymorphicQuery, True => an overloaded __repr__ with nicer multi-line output is used by PolymorphicQuery
|
||||||
|
polymorphic_query_multiline_output = False
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
# avoid ContentType related field accessor clash (an error emitted by model validation)
|
||||||
|
polymorphic_ctype = models.ForeignKey(ContentType, null=True, editable=False,
|
||||||
|
related_name='polymorphic_%(app_label)s.%(class)s_set+')
|
||||||
|
|
||||||
|
# some applications want to know the name of the fields that are added to its models
|
||||||
|
polymorphic_internal_model_fields = ['polymorphic_ctype']
|
||||||
|
|
||||||
|
# Note that Django 1.5 removes these managers because the model is abstract.
|
||||||
|
# They are pretended to be there by the metaclass in PolymorphicModelBase.get_inherited_managers()
|
||||||
|
objects = PolymorphicManager()
|
||||||
|
base_objects = models.Manager()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def translate_polymorphic_Q_object(self_class, q):
|
||||||
|
return translate_polymorphic_Q_object(self_class, q)
|
||||||
|
|
||||||
|
def pre_save_polymorphic(self):
|
||||||
|
"""Normally not needed.
|
||||||
|
This function may be called manually in special use-cases. When the object
|
||||||
|
is saved for the first time, we store its real class in polymorphic_ctype.
|
||||||
|
When the object later is retrieved by PolymorphicQuerySet, it uses this
|
||||||
|
field to figure out the real class of this object
|
||||||
|
(used by PolymorphicQuerySet._get_real_instances)
|
||||||
|
"""
|
||||||
|
if not self.polymorphic_ctype_id:
|
||||||
|
self.polymorphic_ctype = ContentType.objects.get_for_model(self, for_concrete_model=False)
|
||||||
|
pre_save_polymorphic.alters_data = True
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
"""Overridden model save function which supports the polymorphism
|
||||||
|
functionality (through pre_save_polymorphic)."""
|
||||||
|
self.pre_save_polymorphic()
|
||||||
|
return super(PolymorphicModel, self).save(*args, **kwargs)
|
||||||
|
save.alters_data = True
|
||||||
|
|
||||||
|
def get_real_instance_class(self):
|
||||||
|
"""
|
||||||
|
Normally not needed.
|
||||||
|
If a non-polymorphic manager (like base_objects) has been used to
|
||||||
|
retrieve objects, then the real class/type of these objects may be
|
||||||
|
determined using this method.
|
||||||
|
"""
|
||||||
|
# the following line would be the easiest way to do this, but it produces sql queries
|
||||||
|
# return self.polymorphic_ctype.model_class()
|
||||||
|
# so we use the following version, which uses the ContentType manager cache.
|
||||||
|
# Note that model_class() can return None for stale content types;
|
||||||
|
# when the content type record still exists but no longer refers to an existing model.
|
||||||
|
try:
|
||||||
|
model = ContentType.objects.get_for_id(self.polymorphic_ctype_id).model_class()
|
||||||
|
except AttributeError:
|
||||||
|
# Django <1.6 workaround
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Protect against bad imports (dumpdata without --natural) or other
|
||||||
|
# issues missing with the ContentType models.
|
||||||
|
if model is not None \
|
||||||
|
and not issubclass(model, self.__class__) \
|
||||||
|
and not issubclass(model, self.__class__._meta.proxy_for_model):
|
||||||
|
raise RuntimeError("ContentType {0} for {1} #{2} does not point to a subclass!".format(
|
||||||
|
self.polymorphic_ctype_id, model, self.pk,
|
||||||
|
))
|
||||||
|
return model
|
||||||
|
|
||||||
|
def get_real_concrete_instance_class_id(self):
|
||||||
|
model_class = self.get_real_instance_class()
|
||||||
|
if model_class is None:
|
||||||
|
return None
|
||||||
|
return ContentType.objects.get_for_model(model_class, for_concrete_model=True).pk
|
||||||
|
|
||||||
|
def get_real_concrete_instance_class(self):
|
||||||
|
model_class = self.get_real_instance_class()
|
||||||
|
if model_class is None:
|
||||||
|
return None
|
||||||
|
return ContentType.objects.get_for_model(model_class, for_concrete_model=True).model_class()
|
||||||
|
|
||||||
|
def get_real_instance(self):
|
||||||
|
"""Normally not needed.
|
||||||
|
If a non-polymorphic manager (like base_objects) has been used to
|
||||||
|
retrieve objects, then the complete object with it's real class/type
|
||||||
|
and all fields may be retrieved with this method.
|
||||||
|
Each method call executes one db query (if necessary)."""
|
||||||
|
real_model = self.get_real_instance_class()
|
||||||
|
if real_model == self.__class__:
|
||||||
|
return self
|
||||||
|
return real_model.objects.get(pk=self.pk)
|
||||||
|
|
||||||
|
def __init__(self, * args, ** kwargs):
|
||||||
|
"""Replace Django's inheritance accessor member functions for our model
|
||||||
|
(self.__class__) with our own versions.
|
||||||
|
We monkey patch them until a patch can be added to Django
|
||||||
|
(which would probably be very small and make all of this obsolete).
|
||||||
|
|
||||||
|
If we have inheritance of the form ModelA -> ModelB ->ModelC then
|
||||||
|
Django creates accessors like this:
|
||||||
|
- ModelA: modelb
|
||||||
|
- ModelB: modela_ptr, modelb, modelc
|
||||||
|
- ModelC: modela_ptr, modelb, modelb_ptr, modelc
|
||||||
|
|
||||||
|
These accessors allow Django (and everyone else) to travel up and down
|
||||||
|
the inheritance tree for the db object at hand.
|
||||||
|
|
||||||
|
The original Django accessors use our polymorphic manager.
|
||||||
|
But they should not. So we replace them with our own accessors that use
|
||||||
|
our appropriate base_objects manager.
|
||||||
|
"""
|
||||||
|
super(PolymorphicModel, self).__init__(*args, ** kwargs)
|
||||||
|
|
||||||
|
if self.__class__.polymorphic_super_sub_accessors_replaced:
|
||||||
|
return
|
||||||
|
self.__class__.polymorphic_super_sub_accessors_replaced = True
|
||||||
|
|
||||||
|
def create_accessor_function_for_model(model, accessor_name):
|
||||||
|
def accessor_function(self):
|
||||||
|
attr = model.base_objects.get(pk=self.pk)
|
||||||
|
return attr
|
||||||
|
return accessor_function
|
||||||
|
|
||||||
|
subclasses_and_superclasses_accessors = self._get_inheritance_relation_fields_and_models()
|
||||||
|
|
||||||
|
try:
|
||||||
|
from django.db.models.fields.related import ReverseOneToOneDescriptor, ForwardManyToOneDescriptor
|
||||||
|
except ImportError:
|
||||||
|
# django < 1.9
|
||||||
|
from django.db.models.fields.related import (
|
||||||
|
SingleRelatedObjectDescriptor as ReverseOneToOneDescriptor,
|
||||||
|
ReverseSingleRelatedObjectDescriptor as ForwardManyToOneDescriptor,
|
||||||
|
)
|
||||||
|
for name, model in subclasses_and_superclasses_accessors.items():
|
||||||
|
orig_accessor = getattr(self.__class__, name, None)
|
||||||
|
if type(orig_accessor) in [ReverseOneToOneDescriptor, ForwardManyToOneDescriptor]:
|
||||||
|
#print >>sys.stderr, '---------- replacing', name, orig_accessor, '->', model
|
||||||
|
setattr(self.__class__, name, property(create_accessor_function_for_model(model, name)))
|
||||||
|
|
||||||
|
def _get_inheritance_relation_fields_and_models(self):
|
||||||
|
"""helper function for __init__:
|
||||||
|
determine names of all Django inheritance accessor member functions for type(self)"""
|
||||||
|
|
||||||
|
def add_model(model, field_name, result):
|
||||||
|
result[field_name] = model
|
||||||
|
|
||||||
|
def add_model_if_regular(model, field_name, result):
|
||||||
|
if (issubclass(model, models.Model)
|
||||||
|
and model != models.Model
|
||||||
|
and model != self.__class__
|
||||||
|
and model != PolymorphicModel):
|
||||||
|
add_model(model, field_name, result)
|
||||||
|
|
||||||
|
def add_all_super_models(model, result):
|
||||||
|
for super_cls, field_to_super in model._meta.parents.items():
|
||||||
|
if field_to_super is not None: #if not a link to a proxy model
|
||||||
|
field_name = field_to_super.name #the field on model can have a different name to super_cls._meta.module_name, if the field is created manually using 'parent_link'
|
||||||
|
add_model_if_regular(super_cls, field_name, result)
|
||||||
|
add_all_super_models(super_cls, result)
|
||||||
|
|
||||||
|
def add_all_sub_models(super_cls, result):
|
||||||
|
for sub_cls in super_cls.__subclasses__(): #go through all subclasses of model
|
||||||
|
if super_cls in sub_cls._meta.parents: #super_cls may not be in sub_cls._meta.parents if super_cls is a proxy model
|
||||||
|
field_to_super = sub_cls._meta.parents[super_cls] #get the field that links sub_cls to super_cls
|
||||||
|
if field_to_super is not None: # if filed_to_super is not a link to a proxy model
|
||||||
|
super_to_sub_related_field = field_to_super.rel
|
||||||
|
if super_to_sub_related_field.related_name is None:
|
||||||
|
#if related name is None the related field is the name of the subclass
|
||||||
|
to_subclass_fieldname = sub_cls.__name__.lower()
|
||||||
|
else:
|
||||||
|
#otherwise use the given related name
|
||||||
|
to_subclass_fieldname = super_to_sub_related_field.related_name
|
||||||
|
|
||||||
|
add_model_if_regular(sub_cls, to_subclass_fieldname, result)
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
add_all_super_models(self.__class__, result)
|
||||||
|
add_all_sub_models(self.__class__, result)
|
||||||
|
return result
|
||||||
|
|
|
||||||
|
|
@ -1,229 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
Seamless Polymorphic Inheritance for Django Models
|
|
||||||
==================================================
|
|
||||||
|
|
||||||
Please see README.rst and DOCS.rst for further information.
|
|
||||||
|
|
||||||
Or on the Web:
|
|
||||||
http://chrisglass.github.com/django_polymorphic/
|
|
||||||
http://github.com/chrisglass/django_polymorphic
|
|
||||||
|
|
||||||
Copyright:
|
|
||||||
This code and affiliated files are (C) by Bert Constantin and individual contributors.
|
|
||||||
Please see LICENSE and AUTHORS for more information.
|
|
||||||
"""
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
from django.db import models
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
from .base import PolymorphicModelBase
|
|
||||||
from .manager import PolymorphicManager
|
|
||||||
from .query_translate import translate_polymorphic_Q_object
|
|
||||||
|
|
||||||
###################################################################################
|
|
||||||
### PolymorphicModel
|
|
||||||
|
|
||||||
class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)):
|
|
||||||
"""
|
|
||||||
Abstract base class that provides polymorphic behaviour
|
|
||||||
for any model directly or indirectly derived from it.
|
|
||||||
|
|
||||||
For usage instructions & examples please see documentation.
|
|
||||||
|
|
||||||
PolymorphicModel declares one field for internal use (polymorphic_ctype)
|
|
||||||
and provides a polymorphic manager as the default manager
|
|
||||||
(and as 'objects').
|
|
||||||
|
|
||||||
PolymorphicModel overrides the save() and __init__ methods.
|
|
||||||
|
|
||||||
If your derived class overrides any of these methods as well, then you need
|
|
||||||
to take care that you correctly call the method of the superclass, like:
|
|
||||||
|
|
||||||
super(YourClass,self).save(*args,**kwargs)
|
|
||||||
"""
|
|
||||||
|
|
||||||
# for PolymorphicModelBase, so it can tell which models are polymorphic and which are not (duck typing)
|
|
||||||
polymorphic_model_marker = True
|
|
||||||
|
|
||||||
# for PolymorphicQuery, True => an overloaded __repr__ with nicer multi-line output is used by PolymorphicQuery
|
|
||||||
polymorphic_query_multiline_output = False
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
# avoid ContentType related field accessor clash (an error emitted by model validation)
|
|
||||||
polymorphic_ctype = models.ForeignKey(ContentType, null=True, editable=False,
|
|
||||||
related_name='polymorphic_%(app_label)s.%(class)s_set+')
|
|
||||||
|
|
||||||
# some applications want to know the name of the fields that are added to its models
|
|
||||||
polymorphic_internal_model_fields = ['polymorphic_ctype']
|
|
||||||
|
|
||||||
# Note that Django 1.5 removes these managers because the model is abstract.
|
|
||||||
# They are pretended to be there by the metaclass in PolymorphicModelBase.get_inherited_managers()
|
|
||||||
objects = PolymorphicManager()
|
|
||||||
base_objects = models.Manager()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def translate_polymorphic_Q_object(self_class, q):
|
|
||||||
return translate_polymorphic_Q_object(self_class, q)
|
|
||||||
|
|
||||||
def pre_save_polymorphic(self):
|
|
||||||
"""Normally not needed.
|
|
||||||
This function may be called manually in special use-cases. When the object
|
|
||||||
is saved for the first time, we store its real class in polymorphic_ctype.
|
|
||||||
When the object later is retrieved by PolymorphicQuerySet, it uses this
|
|
||||||
field to figure out the real class of this object
|
|
||||||
(used by PolymorphicQuerySet._get_real_instances)
|
|
||||||
"""
|
|
||||||
if not self.polymorphic_ctype_id:
|
|
||||||
self.polymorphic_ctype = ContentType.objects.get_for_model(self, for_concrete_model=False)
|
|
||||||
pre_save_polymorphic.alters_data = True
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
"""Overridden model save function which supports the polymorphism
|
|
||||||
functionality (through pre_save_polymorphic)."""
|
|
||||||
self.pre_save_polymorphic()
|
|
||||||
return super(PolymorphicModel, self).save(*args, **kwargs)
|
|
||||||
save.alters_data = True
|
|
||||||
|
|
||||||
def get_real_instance_class(self):
|
|
||||||
"""
|
|
||||||
Normally not needed.
|
|
||||||
If a non-polymorphic manager (like base_objects) has been used to
|
|
||||||
retrieve objects, then the real class/type of these objects may be
|
|
||||||
determined using this method.
|
|
||||||
"""
|
|
||||||
# the following line would be the easiest way to do this, but it produces sql queries
|
|
||||||
# return self.polymorphic_ctype.model_class()
|
|
||||||
# so we use the following version, which uses the ContentType manager cache.
|
|
||||||
# Note that model_class() can return None for stale content types;
|
|
||||||
# when the content type record still exists but no longer refers to an existing model.
|
|
||||||
try:
|
|
||||||
model = ContentType.objects.get_for_id(self.polymorphic_ctype_id).model_class()
|
|
||||||
except AttributeError:
|
|
||||||
# Django <1.6 workaround
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Protect against bad imports (dumpdata without --natural) or other
|
|
||||||
# issues missing with the ContentType models.
|
|
||||||
if model is not None \
|
|
||||||
and not issubclass(model, self.__class__) \
|
|
||||||
and not issubclass(model, self.__class__._meta.proxy_for_model):
|
|
||||||
raise RuntimeError("ContentType {0} for {1} #{2} does not point to a subclass!".format(
|
|
||||||
self.polymorphic_ctype_id, model, self.pk,
|
|
||||||
))
|
|
||||||
return model
|
|
||||||
|
|
||||||
def get_real_concrete_instance_class_id(self):
|
|
||||||
model_class = self.get_real_instance_class()
|
|
||||||
if model_class is None:
|
|
||||||
return None
|
|
||||||
return ContentType.objects.get_for_model(model_class, for_concrete_model=True).pk
|
|
||||||
|
|
||||||
def get_real_concrete_instance_class(self):
|
|
||||||
model_class = self.get_real_instance_class()
|
|
||||||
if model_class is None:
|
|
||||||
return None
|
|
||||||
return ContentType.objects.get_for_model(model_class, for_concrete_model=True).model_class()
|
|
||||||
|
|
||||||
def get_real_instance(self):
|
|
||||||
"""Normally not needed.
|
|
||||||
If a non-polymorphic manager (like base_objects) has been used to
|
|
||||||
retrieve objects, then the complete object with it's real class/type
|
|
||||||
and all fields may be retrieved with this method.
|
|
||||||
Each method call executes one db query (if necessary)."""
|
|
||||||
real_model = self.get_real_instance_class()
|
|
||||||
if real_model == self.__class__:
|
|
||||||
return self
|
|
||||||
return real_model.objects.get(pk=self.pk)
|
|
||||||
|
|
||||||
def __init__(self, * args, ** kwargs):
|
|
||||||
"""Replace Django's inheritance accessor member functions for our model
|
|
||||||
(self.__class__) with our own versions.
|
|
||||||
We monkey patch them until a patch can be added to Django
|
|
||||||
(which would probably be very small and make all of this obsolete).
|
|
||||||
|
|
||||||
If we have inheritance of the form ModelA -> ModelB ->ModelC then
|
|
||||||
Django creates accessors like this:
|
|
||||||
- ModelA: modelb
|
|
||||||
- ModelB: modela_ptr, modelb, modelc
|
|
||||||
- ModelC: modela_ptr, modelb, modelb_ptr, modelc
|
|
||||||
|
|
||||||
These accessors allow Django (and everyone else) to travel up and down
|
|
||||||
the inheritance tree for the db object at hand.
|
|
||||||
|
|
||||||
The original Django accessors use our polymorphic manager.
|
|
||||||
But they should not. So we replace them with our own accessors that use
|
|
||||||
our appropriate base_objects manager.
|
|
||||||
"""
|
|
||||||
super(PolymorphicModel, self).__init__(*args, ** kwargs)
|
|
||||||
|
|
||||||
if self.__class__.polymorphic_super_sub_accessors_replaced:
|
|
||||||
return
|
|
||||||
self.__class__.polymorphic_super_sub_accessors_replaced = True
|
|
||||||
|
|
||||||
def create_accessor_function_for_model(model, accessor_name):
|
|
||||||
def accessor_function(self):
|
|
||||||
attr = model.base_objects.get(pk=self.pk)
|
|
||||||
return attr
|
|
||||||
return accessor_function
|
|
||||||
|
|
||||||
subclasses_and_superclasses_accessors = self._get_inheritance_relation_fields_and_models()
|
|
||||||
|
|
||||||
try:
|
|
||||||
from django.db.models.fields.related import ReverseOneToOneDescriptor, ForwardManyToOneDescriptor
|
|
||||||
except ImportError:
|
|
||||||
# django < 1.9
|
|
||||||
from django.db.models.fields.related import (
|
|
||||||
SingleRelatedObjectDescriptor as ReverseOneToOneDescriptor,
|
|
||||||
ReverseSingleRelatedObjectDescriptor as ForwardManyToOneDescriptor,
|
|
||||||
)
|
|
||||||
for name, model in subclasses_and_superclasses_accessors.items():
|
|
||||||
orig_accessor = getattr(self.__class__, name, None)
|
|
||||||
if type(orig_accessor) in [ReverseOneToOneDescriptor, ForwardManyToOneDescriptor]:
|
|
||||||
#print >>sys.stderr, '---------- replacing', name, orig_accessor, '->', model
|
|
||||||
setattr(self.__class__, name, property(create_accessor_function_for_model(model, name)))
|
|
||||||
|
|
||||||
def _get_inheritance_relation_fields_and_models(self):
|
|
||||||
"""helper function for __init__:
|
|
||||||
determine names of all Django inheritance accessor member functions for type(self)"""
|
|
||||||
|
|
||||||
def add_model(model, field_name, result):
|
|
||||||
result[field_name] = model
|
|
||||||
|
|
||||||
def add_model_if_regular(model, field_name, result):
|
|
||||||
if (issubclass(model, models.Model)
|
|
||||||
and model != models.Model
|
|
||||||
and model != self.__class__
|
|
||||||
and model != PolymorphicModel):
|
|
||||||
add_model(model, field_name, result)
|
|
||||||
|
|
||||||
def add_all_super_models(model, result):
|
|
||||||
for super_cls, field_to_super in model._meta.parents.items():
|
|
||||||
if field_to_super is not None: #if not a link to a proxy model
|
|
||||||
field_name = field_to_super.name #the field on model can have a different name to super_cls._meta.module_name, if the field is created manually using 'parent_link'
|
|
||||||
add_model_if_regular(super_cls, field_name, result)
|
|
||||||
add_all_super_models(super_cls, result)
|
|
||||||
|
|
||||||
def add_all_sub_models(super_cls, result):
|
|
||||||
for sub_cls in super_cls.__subclasses__(): #go through all subclasses of model
|
|
||||||
if super_cls in sub_cls._meta.parents: #super_cls may not be in sub_cls._meta.parents if super_cls is a proxy model
|
|
||||||
field_to_super = sub_cls._meta.parents[super_cls] #get the field that links sub_cls to super_cls
|
|
||||||
if field_to_super is not None: # if filed_to_super is not a link to a proxy model
|
|
||||||
super_to_sub_related_field = field_to_super.rel
|
|
||||||
if super_to_sub_related_field.related_name is None:
|
|
||||||
#if related name is None the related field is the name of the subclass
|
|
||||||
to_subclass_fieldname = sub_cls.__name__.lower()
|
|
||||||
else:
|
|
||||||
#otherwise use the given related name
|
|
||||||
to_subclass_fieldname = super_to_sub_related_field.related_name
|
|
||||||
|
|
||||||
add_model_if_regular(sub_cls, to_subclass_fieldname, result)
|
|
||||||
|
|
||||||
result = {}
|
|
||||||
add_all_super_models(self.__class__, result)
|
|
||||||
add_all_sub_models(self.__class__, result)
|
|
||||||
return result
|
|
||||||
Loading…
Reference in New Issue