Fixed cache bug

This commit is contained in:
ksamuel 2010-09-08 15:24:14 +00:00
parent 9afdc58a05
commit 1cdc831bd1
3 changed files with 136 additions and 19 deletions

View file

@ -106,6 +106,7 @@ class EavAttribute(models.Model):
'''
ct = ContentType.objects.get_for_model(entity)
qs = self.eavvalue_set.filter(entity_ct=ct, entity_id=entity.pk)
print entity
count = qs.count()
if count > 1:
raise AttributeError(u"You should have one and only one value "\
@ -134,6 +135,7 @@ class EavAttribute(models.Model):
Use attribute if you want to use something else than the current
one
"""
print "save_value()"
ct = ContentType.objects.get_for_model(entity)
attribute = attribute or self
try:
@ -144,7 +146,10 @@ class EavAttribute(models.Model):
eavvalue = self.eavvalue_set.model(entity_ct=ct,
entity_id=entity.pk,
attribute=attribute)
print "value", value
print "eavvalue.value", eavvalue.value
if value != eavvalue.value:
print value
eavvalue.value = value
eavvalue.save()
@ -223,13 +228,21 @@ class EavEntity(object):
# TODO: memoize
def __getattr__(self, name):
print self, name
print "__getattr__"
if not name.startswith('_'):
for slug in self.get_all_attribute_slugs():
attribute = self.get_attribute_by_slug(name)
if attribute:
value = attribute.get_value_for_entity(self.model)
if value:
return attribute.get_value_for_entity(self.model).value
attribute = self.get_attribute_by_slug(name)
print attribute
print "if attribute"
if attribute:
print attribute
value = attribute.get_value_for_entity(self.model)
print value
print "if value"
if value:
return attribute.get_value_for_entity(self.model).value
return None
raise AttributeError(_(u"%s EAV does not have attribute " \
@ -237,12 +250,22 @@ class EavEntity(object):
(self.model._meta.object_name, name))
@classmethod
def get_eav_attributes_for_model(cls, model_cls):
"""
Return the attributes for this model
"""
from .utils import EavRegistry
config_cls = EavRegistry.get_config_cls_for_model(model_cls)
return config_cls.get_eav_attributes()
@classmethod
def get_attr_cache_for_model(cls, model_cls):
"""
Return the attribute cache for this model
"""
return cls._cache.setdefault(get_unique_class_identifier(cls), {})
return cls._cache.setdefault(get_unique_class_identifier(model_cls), {})
@classmethod
@ -251,7 +274,8 @@ class EavEntity(object):
Create or update the attributes cache for this entity class.
"""
cache = cls.get_attr_cache_for_model(model_cls)
cache['attributes'] = cls.get_eav_attributes().select_related()
cache['attributes'] = cls.get_eav_attributes_for_model(model_cls)\
.select_related()
cache['slug_mapping'] = dict((s.slug, s) for s in cache['attributes'])
return cache
@ -265,6 +289,13 @@ class EavEntity(object):
cache.clear()
return cache
def get_eav_attributes(self):
"""
Return the attributes for this model
"""
return self.__class__.get_eav_attributes_for_model(self.model.__class__)
def update_attr_cache(self):
"""
@ -291,11 +322,12 @@ class EavEntity(object):
def save(self):
for attribute in self.get_all_attributes():
for attribute in self.get_eav_attributes():
if hasattr(self, attribute.slug):
attribute_value = getattr(self, attribute.slug)
attribute.save_value(self.model, attribute_value)
@classmethod
def get_all_attributes_for_model(cls, model_cls):
"""
@ -308,9 +340,7 @@ class EavEntity(object):
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):
@ -346,8 +376,8 @@ class EavEntity(object):
cache = cls.get_attr_cache_for_model(model_cls)
if not cache:
cache = EavEntity.update_attr_cache_for_model(model_cls)
if slug in cache['slug_mapping']:
return cache['slug_mapping'][slug]
return cache['slug_mapping'].get(slug, None)
def get_attribute_by_slug(self, slug):
m_cls = self.model.__class__
@ -363,10 +393,12 @@ class EavEntity(object):
if attr.pk == attribute_id:
return attr
def __iter__(self):
"Iterates over non-empty EAV attributes. Normal fields are not included."
return iter(self.get_values())
@staticmethod
def save_handler(sender, *args, **kwargs):
kwargs['instance'].eav.save()

View file

@ -1,6 +1,7 @@
from datetime import datetime
from django.test import TestCase
from django.contrib.auth.models import User
from ..models import *
from ..utils import EavRegistry, EavConfig
@ -147,6 +148,35 @@ class EavSetterAndGetterTests(TestCase):
# remove a label that is not attach does nothing
self.attribute.remove_label('a')
self.attribute.remove_label('x')
def test_attributes_are_filtered_according_to_config_class(self):
attribute = EavAttribute.objects\
.create(datatype=EavAttribute.TYPE_TEXT,
name='Country', slug='country')
EavRegistry.unregister(Patient)
class PatientEav(EavConfig):
@classmethod
def get_eav_attributes(cls):
return EavAttribute.objects.filter(slug='country')
class UserEav(EavConfig):
@classmethod
def get_eav_attributes(cls):
return EavAttribute.objects.filter(slug='city')
EavRegistry.register(Patient, PatientEav)
EavRegistry.register(User, UserEav)
self.assertEqual(list(Patient.eav.get_eav_attributes()),
list(EavAttribute.objects.filter(slug='country')))
self.assertEqual(list(User.eav.get_eav_attributes()),
list(EavAttribute.objects.filter(slug='city')))
def test_can_filter_attribute_availability_for_entity(self):
@ -177,8 +207,48 @@ class EavSetterAndGetterTests(TestCase):
self.assertFalse(p.eav.city, 'Paris')
self.assertEqual(p.eav.country, 'USA')
p = Patient()
def test_can_have_differente_attribute_filter(self):
attribute = EavAttribute.objects\
.create(datatype=EavAttribute.TYPE_TEXT,
name='Country', slug='country')
EavRegistry.unregister(Patient)
class PatientEav(EavConfig):
@classmethod
def get_eav_attributes(cls):
return EavAttribute.objects.filter(slug='country')
class UserEav(EavConfig):
@classmethod
def get_eav_attributes(cls):
return EavAttribute.objects.filter(slug='city')
EavRegistry.register(Patient, PatientEav)
EavRegistry.register(User, UserEav)
p = Patient.objects.create(name='Patrick')
u = User.objects.create(username='John')
p.eav.city = 'Paris'
p.eav.country = 'USA'
p.save()
u.eav.city = 'Paris'
u.eav.country = 'USA'
u.save()
p = Patient.objects.get(pk=p.pk)
u = User.objects.get(pk=u.pk)
self.assertFalse(p.eav.city)
self.assertEqual(p.eav.country, 'USA')
self.assertFalse(u.eav.country)
self.assertEqual(u.eav.city, 'Paris')
# todo: test multiple children

View file

@ -5,7 +5,7 @@ from .managers import EntityManager
from .models import (EavEntity, EavAttribute, EavValue,
get_unique_class_identifier)
class EavConfig(object):
class EavConfig(EavEntity):
proxy_field_name = 'eav'
manager_field_name ='objects'
@ -17,6 +17,8 @@ class EavConfig(object):
By default, all attributes apply to an entity,
unless otherwise specified.
"""
print 'Generic'
print cls
return EavAttribute.objects.all()
@ -57,6 +59,17 @@ class EavRegistry(object):
for cache in EavRegistry.cache.itervalues():
EavEntity.update_attr_cache_for_model(cache['model_cls'])
@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):
@ -70,17 +83,19 @@ class EavRegistry(object):
if cls_id in EavRegistry.cache:
return
config_cls = EavRegistry.wrap_config_class(model_cls, config_cls)
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,
'model_cls': model_cls }
print config_cls
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
setattr(model_cls, config_cls.proxy_field_name, EavEntity)
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)