django-eav2/eav/registry.py

199 lines
6.8 KiB
Python
Raw Normal View History

"""This modules contains the registry classes."""
2010-09-27 13:28:52 +00:00
from django.contrib.contenttypes import fields as generic
2018-06-04 21:59:05 +00:00
from django.db.models.signals import post_init, post_save, pre_save
2010-09-27 13:28:52 +00:00
2021-10-16 17:45:01 +00:00
from eav.managers import EntityManager
from eav.models import Attribute, Entity, Value
2010-09-27 13:28:52 +00:00
from eav.logic.entity_pk import get_entity_pk_type
2010-09-27 13:28:52 +00:00
class EavConfig(object):
"""
The default ``EavConfig`` class used if it is not overriden on registration.
2010-09-27 13:28:52 +00:00
This is where all the default eav attribute names are defined.
2018-06-01 12:36:46 +00:00
Available options are as follows:
1. manager_attr - Specifies manager name. Used to refer to the
manager from Entity class, "objects" by default.
2. manager_only - Specifies whether signals and generic relation should
be setup for the registered model.
3. eav_attr - Named of the Entity toolkit instance on the registered
model instance. "eav" by default. See attach_eav_attr.
4. generic_relation_attr - Name of the GenericRelation to Value
objects. "eav_values" by default.
5. generic_relation_related_name - Name of the related name for
GenericRelation from Entity to Value. None by default. Therefore,
if not overridden, it is not possible to query Values by Entities.
"""
2021-10-16 17:43:02 +00:00
2010-09-27 13:28:52 +00:00
manager_attr = 'objects'
manager_only = False
eav_attr = 'eav'
generic_relation_attr = 'eav_values'
generic_relation_related_name = None
@classmethod
def get_attributes(cls, instance=None):
"""
2010-09-27 13:28:52 +00:00
By default, all :class:`~eav.models.Attribute` object apply to an
entity, unless you provide a custom EavConfig class overriding this.
"""
return Attribute.objects.all()
2010-09-27 13:28:52 +00:00
class Registry(object):
"""
2010-09-27 13:28:52 +00:00
Handles registration through the
:meth:`register` and :meth:`unregister` methods.
"""
2010-09-27 13:28:52 +00:00
@staticmethod
def register(model_cls, config_cls=None):
"""
2010-09-27 13:28:52 +00:00
Registers *model_cls* with eav. You can pass an optional *config_cls*
to override the EavConfig defaults.
.. note::
Multiple registrations for the same entity are harmlessly ignored.
"""
2010-09-27 13:28:52 +00:00
if hasattr(model_cls, '_eav_config_cls'):
return
if config_cls is EavConfig or config_cls is None:
2021-10-16 17:43:02 +00:00
config_cls = type("%sConfig" % model_cls.__name__, (EavConfig,), {})
2010-09-27 13:28:52 +00:00
# set _eav_config_cls on the model so we can access it there
setattr(model_cls, '_eav_config_cls', config_cls)
reg = Registry(model_cls)
reg._register_self()
@staticmethod
def unregister(model_cls):
"""
2010-09-27 13:28:52 +00:00
Unregisters *model_cls* with eav.
.. note::
Unregistering a class not already registered is harmlessly ignored.
"""
2010-09-27 13:28:52 +00:00
if not getattr(model_cls, '_eav_config_cls', None):
return
reg = Registry(model_cls)
reg._unregister_self()
delattr(model_cls, '_eav_config_cls')
@staticmethod
def attach_eav_attr(sender, *args, **kwargs):
"""
Attach EAV Entity toolkit to an instance after init.
"""
2010-09-27 13:28:52 +00:00
instance = kwargs['instance']
config_cls = instance.__class__._eav_config_cls
setattr(instance, config_cls.eav_attr, Entity(instance))
def __init__(self, model_cls):
"""
2010-09-27 13:28:52 +00:00
Set the *model_cls* and its *config_cls*
"""
2010-09-27 13:28:52 +00:00
self.model_cls = model_cls
self.config_cls = model_cls._eav_config_cls
def _attach_manager(self):
"""
2010-09-27 13:28:52 +00:00
Attach the manager to *manager_attr* specified in *config_cls*
"""
2017-09-05 10:01:20 +00:00
# Save the old manager if the attribute name conflicts with the new one.
2010-09-27 13:28:52 +00:00
if hasattr(self.model_cls, self.config_cls.manager_attr):
mgr = getattr(self.model_cls, self.config_cls.manager_attr)
self.config_cls.old_mgr = mgr
2017-09-04 12:35:35 +00:00
self.model_cls._meta.local_managers.remove(mgr)
self.model_cls._meta._expire_cache()
# Attach the new manager to the model.
2010-09-27 13:28:52 +00:00
mgr = EntityManager()
mgr.contribute_to_class(self.model_cls, self.config_cls.manager_attr)
def _detach_manager(self):
"""
2017-09-05 10:01:20 +00:00
Detach the manager and restore the previous one (if there was one).
"""
2017-09-04 12:35:35 +00:00
mgr = getattr(self.model_cls, self.config_cls.manager_attr)
self.model_cls._meta.local_managers.remove(mgr)
self.model_cls._meta._expire_cache()
2018-04-06 14:27:18 +00:00
delattr(self.model_cls, self.config_cls.manager_attr)
2017-09-04 12:35:35 +00:00
2010-09-27 13:28:52 +00:00
if hasattr(self.config_cls, 'old_mgr'):
2021-10-16 17:43:02 +00:00
self.config_cls.old_mgr.contribute_to_class(
self.model_cls, self.config_cls.manager_attr
)
2010-09-27 13:28:52 +00:00
def _attach_signals(self):
"""
Attach pre- and post- save signals from model class
to Entity helper. This way, Entity instance will be
able to prepare and clean-up before and after creation /
update of the user's model class instance.
"""
2021-10-16 17:43:02 +00:00
post_init.connect(Registry.attach_eav_attr, sender=self.model_cls)
pre_save.connect(Entity.pre_save_handler, sender=self.model_cls)
post_save.connect(Entity.post_save_handler, sender=self.model_cls)
2010-09-27 13:28:52 +00:00
def _detach_signals(self):
"""
Detach all signals for eav.
"""
2021-10-16 17:43:02 +00:00
post_init.disconnect(Registry.attach_eav_attr, sender=self.model_cls)
pre_save.disconnect(Entity.pre_save_handler, sender=self.model_cls)
post_save.disconnect(Entity.post_save_handler, sender=self.model_cls)
2010-09-27 13:28:52 +00:00
def _attach_generic_relation(self):
"""Set up the generic relation for the entity."""
2021-10-16 17:43:02 +00:00
rel_name = (
self.config_cls.generic_relation_related_name or self.model_cls.__name__
)
2010-09-27 13:28:52 +00:00
gr_name = self.config_cls.generic_relation_attr.lower()
2021-10-16 17:43:02 +00:00
generic_relation = generic.GenericRelation(
Value,
object_id_field=get_entity_pk_type(self.model_cls),
2021-10-16 17:43:02 +00:00
content_type_field='entity_ct',
related_query_name=rel_name,
)
2010-09-27 13:28:52 +00:00
generic_relation.contribute_to_class(self.model_cls, gr_name)
def _detach_generic_relation(self):
"""
2010-09-27 13:28:52 +00:00
Remove the generic relation from the entity
"""
2010-09-27 13:28:52 +00:00
gen_rel_field = self.config_cls.generic_relation_attr.lower()
for field in self.model_cls._meta.local_many_to_many:
if field.name == gen_rel_field:
self.model_cls._meta.local_many_to_many.remove(field)
break
delattr(self.model_cls, gen_rel_field)
def _register_self(self):
"""
2010-09-27 13:28:52 +00:00
Call the necessary registration methods
"""
2010-09-27 13:28:52 +00:00
self._attach_manager()
if not self.config_cls.manager_only:
self._attach_signals()
self._attach_generic_relation()
def _unregister_self(self):
"""
2010-09-27 13:28:52 +00:00
Call the necessary unregistration methods
"""
2010-09-27 13:28:52 +00:00
self._detach_manager()
if not self.config_cls.manager_only:
self._detach_signals()
self._detach_generic_relation()