diff --git a/managers.py b/managers.py index 50c4369..9afce03 100644 --- a/managers.py +++ b/managers.py @@ -1,4 +1,7 @@ +import re + from django.db import models +from .models import EavEntity class EntityManager(models.Manager): def filter(self, *args, **kwargs): @@ -9,11 +12,9 @@ class EntityManager(models.Manager): return qs def _filter_by_lookup(self, qs, lookup, value): - eav_entity = self._get_eav_entity() - slugs = eav_entity.get_all_attribute_slugs() - cache = eav_entity.get_attr_cache() + slugs = EavEntity.get_all_attribute_slugs_for_model(self.model) fields = self.model._meta.get_all_field_names() - attributes = eav_entity.get_all_attributes() + attributes = EavEntity.get_all_attributes_for_model(self.model) config_cls = self._get_config_cls() eav_prefix = config_cls.proxy_field_name @@ -22,14 +23,14 @@ class EntityManager(models.Manager): if not lookup.startswith("%s__" % eav_prefix): return {lookup: value} - lookup = lookup.lstrip("%s__" % eav_prefix) + lookup = re.sub(r'^%s__' % eav_prefix, '', lookup) # Sublookup will be None if there is no __ in the lookup name, sublookup = (lookup.split('__', 1) + [None])[:2] if name in slugs: # EAV attribute (Attr instance linked to entity) - attribute = eav_entity.get_attribute_by_slug(name) + attribute = EavEntity.get_attribute_by_slug_for_model(self.model , name) return self._filter_by_simple_schema(qs, lookup, sublookup, value, attribute) else: raise NameError('Cannot filter items by attributes: unknown ' @@ -51,8 +52,3 @@ class EntityManager(models.Manager): def _get_config_cls(self): from .utils import EavRegistry return EavRegistry.get_config_cls_for_model(self.model) - - def _get_eav_entity(self): - cls = self._get_config_cls() - eav_entity_name = cls.proxy_field_name - return getattr(self.model, eav_entity_name) diff --git a/models.py b/models.py index ccd6e56..243f674 100644 --- a/models.py +++ b/models.py @@ -83,7 +83,11 @@ class EavAttribute(models.Model): self.slug = EavSlugField.create_slug_from_name(self.name) self.full_clean() super(EavAttribute, self).save(*args, **kwargs) + EavEntity.update_all_caches() + def delete(self, *args, **kwargs): + EavEntity.update_all_caches() + super(EavAttribute, self).delete(*args, **kwargs) def add_label(self, label): label, created = EavAttributeLabel.objects.get_or_create(name=label) @@ -170,11 +174,6 @@ class EavValue(models.Model): ''' - #class Meta: - # we can't a TextField on mysql with a unique constraint - #unique_together = ('entity_ct', 'entity_id', 'attribute', - # 'value_text', 'value_float', 'value_date', - # 'value_bool') entity_ct = models.ForeignKey(ContentType, related_name='value_entities') entity_id = models.IntegerField() entity = generic.GenericForeignKey(ct_field='entity_ct', fk_field='entity_id') @@ -190,6 +189,9 @@ class EavValue(models.Model): value_object = generic.GenericForeignKey(ct_field='generic_value_ct', fk_field='generic_value_id') + created = models.DateTimeField(_(u"created"), default=datetime.now) + modified = models.DateTimeField(_(u"modified"), auto_now=True) + attribute = models.ForeignKey(EavAttribute) def save(self, *args, **kwargs): @@ -258,7 +260,15 @@ class EavEntity(object): cache['slug_mapping'] = dict((s.slug, s) for s in cache['attributes']) return cache - + @classmethod + def update_all_caches(cls): + """ + Update all caches of registered entities. + """ + from .utils import EavRegistry + for entity in EavRegistry.cache.itervalues(): + cls.update_attr_cache_for_model(entity['model_cls']) + @classmethod def flush_attr_cache_for_model(cls, model_cls): """ @@ -299,38 +309,63 @@ class EavEntity(object): attribute_value = getattr(self, attribute.slug) attribute.save_value(self.model, attribute_value) - def get_all_attributes(self): + @classmethod + def get_all_attributes_for_model(cls, model_cls): """ Return the current cache or if it doesn't exists, update it and returns it. """ - - cache = self.get_attr_cache() + cache = cls.get_attr_cache_for_model(model_cls) if not cache: - cache = self.update_attr_cache() + cache = EavEntity.update_attr_cache_for_model(model_cls) + return cache['attributes'] + + def get_all_attributes(self): + m_cls = self.__class__ + return self.__class__.get_all_attributes_for_model(m_cls) + def get_values(self): return EavValue.objects.filter(entity_ct=self.ct, entity_id=self.model.pk).select_related() + @classmethod + def get_all_attribute_slugs_for_model(cls, model_cls): + """ + Returns all attributes slugs for the entity + class linked to the passed model. + """ + cache = cls.get_attr_cache_for_model(model_cls) + if not cache: + cache = EavEntity.update_attr_cache_for_model(model_cls) + + return cache['slug_mapping'] + def get_all_attribute_slugs(self): """ Returns all attributes slugs for the entity class linked to the current instance. """ - cache = self.get_attr_cache() + m_cls = self.model.__class__ + return self.__class__.get_all_attribute_slugs_for_model(m_cls) + + @classmethod + def get_attribute_by_slug_for_model(cls, model_cls, slug): + """ + Returns all attributes slugs for the entity + class linked to the passed model. + """ + cache = cls.get_attr_cache_for_model(model_cls) if not cache: - cache = self.update_attr_cache() - - return cache['slug_mapping'] + cache = EavEntity.update_attr_cache_for_model(model_cls) + if slug in cache['slug_mapping']: + return cache['slug_mapping'][slug] def get_attribute_by_slug(self, slug): - """ - Returns an attribute object knowing the slug for the entity - class linked to the current instance. - """ - return self.get_all_attribute_slugs().get(slug, None) + m_cls = self.model.__class__ + return self.__class__.get_attribute_by_slug_for_model(m_cls, slug) + def get_attribute_by_id(self, attribute_id): """ diff --git a/tests/basics.py b/tests/basics.py index 7d0bcc4..9a75053 100644 --- a/tests/basics.py +++ b/tests/basics.py @@ -124,9 +124,7 @@ class EavBasicTests(TestCase): def test_eavregistry_ataches_and_detaches_eav_attribute(self): EavRegistry.unregister(Patient) - print "hello" p = Patient() - print "hello" self.assertFalse(hasattr(p, 'eav')) EavRegistry.register(Patient) diff --git a/tests/set_and_get.py b/tests/set_and_get.py index f52a96d..baebed3 100644 --- a/tests/set_and_get.py +++ b/tests/set_and_get.py @@ -173,8 +173,8 @@ class EavSetterAndGetterTests(TestCase): p.eav.country = 'USA' p.save() p = Patient.objects.get(pk=p.pk) - - self.assertFalse(p.eav.city) + + self.assertFalse(p.eav.city, 'Paris') self.assertEqual(p.eav.country, 'USA') p = Patient()