django-eav2/utils.py

163 lines
5.2 KiB
Python
Raw Normal View History

from django.contrib.contenttypes import generic
2010-09-08 12:18:29 +00:00
from django.db.utils import DatabaseError
from django.db.models.signals import post_init, post_save, post_delete
2010-09-06 21:22:41 +00:00
from .managers import EntityManager
2010-09-08 01:28:03 +00:00
from .models import (EavEntity, EavAttribute, EavValue,
get_unique_class_identifier)
2010-09-06 20:46:11 +00:00
2010-09-08 15:24:14 +00:00
class EavConfig(EavEntity):
proxy_field_name = 'eav'
manager_field_name ='objects'
generic_relation_field_name = 'eav_values'
2010-09-06 20:46:11 +00:00
@classmethod
def get_eav_attributes(cls):
"""
By default, all attributes apply to an entity,
unless otherwise specified.
"""
return EavAttribute.objects.all()
class EavRegistry(object):
"""
Tools to add eav features to models
"""
cache = {}
2010-09-06 20:46:11 +00:00
2010-09-08 01:28:03 +00:00
2010-09-07 19:28:39 +00:00
@staticmethod
def get_config_cls_for_model(model_cls):
2010-09-08 01:28:03 +00:00
"""
Returns the configuration class for the given
model
"""
cls_id = get_unique_class_identifier(model_cls)
2010-09-08 17:51:56 +00:00
if cls_id in EavRegistry.cache:
return EavRegistry.cache[cls_id]['config_cls']
2010-09-08 01:28:03 +00:00
2010-09-07 19:28:39 +00:00
2010-09-06 20:46:11 +00:00
@staticmethod
def attach(sender, *args, **kwargs):
"""
Attache EAV toolkit to an instance after init.
"""
instance = kwargs['instance']
2010-09-08 01:28:03 +00:00
config_cls = EavRegistry.get_config_cls_for_model(sender)
2010-09-06 20:46:11 +00:00
setattr(instance, config_cls.proxy_field_name, EavEntity(instance))
2010-09-06 20:46:11 +00:00
@staticmethod
def update_attribute_cache(sender, *args, **kwargs):
"""
Update the attribute cache for all the models every time we
create an attribute.
"""
for cache in EavRegistry.cache.itervalues():
EavEntity.update_attr_cache_for_model(cache['model_cls'])
2010-09-08 15:24:14 +00:00
@staticmethod
def wrap_config_class(model_cls, config_cls):
"""
Check if the config class is EavConfig, and create a subclass if
it is
"""
if config_cls is EavConfig:
return type("%sConfig" % model_cls.__name__, (EavConfig,), {})
return config_cls
@staticmethod
def register(model_cls, config_cls=EavConfig):
2010-09-06 20:46:11 +00:00
"""
Inject eav features into the given model and attach a signal
listener to it for setup.
"""
cls_id = get_unique_class_identifier(model_cls)
if cls_id in EavRegistry.cache:
2010-09-06 20:46:11 +00:00
return
2010-09-08 15:24:14 +00:00
config_cls = EavRegistry.wrap_config_class(model_cls, config_cls)
2010-09-06 20:46:11 +00:00
post_init.connect(EavRegistry.attach, sender=model_cls)
post_save.connect(EavEntity.save_handler, sender=model_cls)
EavRegistry.cache[cls_id] = { 'config_cls': config_cls,
2010-09-08 01:28:03 +00:00
'model_cls': model_cls }
2010-09-06 20:46:11 +00:00
if hasattr(model_cls, config_cls.manager_field_name):
mgr = getattr(model_cls, config_cls.manager_field_name)
EavRegistry.cache[cls_id]['old_mgr'] = mgr
2010-09-06 20:46:11 +00:00
2010-09-08 15:24:14 +00:00
setattr(model_cls, config_cls.proxy_field_name, config_cls)
setattr(getattr(model_cls, config_cls.proxy_field_name),
'get_eav_attributes', config_cls.get_eav_attributes)
2010-09-06 20:46:11 +00:00
mgr = EntityManager()
mgr.contribute_to_class(model_cls, config_cls.manager_field_name)
2010-09-08 12:18:29 +00:00
try:
EavEntity.update_attr_cache_for_model(model_cls)
except DatabaseError:
pass
2010-09-06 20:46:11 +00:00
gr_name = config_cls.generic_relation_field_name
generic_relation = generic.GenericRelation(EavValue,
2010-09-08 01:28:03 +00:00
object_id_field='entity_id',
2010-09-08 18:29:02 +00:00
content_type_field='entity_ct',
2010-09-08 19:16:46 +00:00
related_name=model_cls.__name__)
generic_relation.contribute_to_class(model_cls, gr_name)
2010-09-06 20:46:11 +00:00
@staticmethod
def unregister(model_cls):
"""
Inject eav features into the given model and attach a signal
listener to it for setup.
"""
cls_id = get_unique_class_identifier(model_cls)
if not cls_id in EavRegistry.cache:
2010-09-06 20:46:11 +00:00
return
cache = EavRegistry.cache[cls_id]
config_cls = cache['config_cls']
2010-09-06 20:46:11 +00:00
post_init.disconnect(EavRegistry.attach, sender=model_cls)
post_save.disconnect(EavEntity.save_handler, sender=model_cls)
2010-09-06 20:46:11 +00:00
try:
delattr(model_cls, config_cls.manager_field_name)
2010-09-06 20:46:11 +00:00
except AttributeError:
pass
try:
delattr(model_cls, config_cls.generic_relation_field_name)
except AttributeError:
pass
2010-09-06 20:46:11 +00:00
if 'old_mgr' in cache:
cache['old_mgr'].contribute_to_class(model_cls,
config_cls.manager_field_name)
2010-09-06 20:46:11 +00:00
try:
delattr(model_cls, config_cls.proxy_field_name)
2010-09-06 20:46:11 +00:00
except AttributeError:
pass
EavEntity.flush_attr_cache_for_model(model_cls)
EavRegistry.cache.pop(cls_id)
# todo : test cache
# todo : tst unique identitfier
# todo: test update attribute cache on attribute creation
post_save.connect(EavRegistry.update_attribute_cache, sender=EavAttribute)
post_delete.connect(EavRegistry.update_attribute_cache, sender=EavAttribute)