mirror of
https://github.com/jazzband/django-eav2.git
synced 2026-03-16 22:40:26 +00:00
Major refactor
This commit is contained in:
parent
515cdf07ee
commit
a53bc53daa
7 changed files with 104 additions and 334 deletions
5
admin.py
5
admin.py
|
|
@ -53,9 +53,6 @@ class BaseEntityAdmin(ModelAdmin):
|
|||
return super_meth(request, context, **kwargs)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class BaseEntityInlineFormSet(BaseInlineFormSet):
|
||||
"""
|
||||
An inline formset that correctly initializes EAV forms.
|
||||
|
|
@ -99,7 +96,7 @@ class BaseEntityInline(InlineModelAdmin):
|
|||
|
||||
class AttributeAdmin(ModelAdmin):
|
||||
list_display = ('name', 'slug', 'datatype', 'description')
|
||||
prepopulated_fields = {'name': ('slug',)}
|
||||
prepopulated_fields = {'slug': ('name',)}
|
||||
|
||||
admin.site.register(Attribute, AttributeAdmin)
|
||||
admin.site.register(Value)
|
||||
|
|
|
|||
26
forms.py
26
forms.py
|
|
@ -28,22 +28,6 @@ from django.utils.translation import ugettext_lazy as _
|
|||
from .utils import EavRegistry
|
||||
|
||||
|
||||
__all__ = ['BaseSchemaForm', 'BaseDynamicEntityForm']
|
||||
|
||||
|
||||
class BaseSchemaForm(ModelForm):
|
||||
|
||||
'''
|
||||
def clean_name(self):
|
||||
"Avoid name clashes between static and dynamic attributes."
|
||||
name = self.cleaned_data['name']
|
||||
reserved_names = self._meta.model._meta.get_all_field_names()
|
||||
if name not in reserved_names:
|
||||
return name
|
||||
raise ValidationError(_('Attribute name must not clash with reserved names'
|
||||
' ("%s")') % '", "'.join(reserved_names))
|
||||
'''
|
||||
|
||||
|
||||
class BaseDynamicEntityForm(ModelForm):
|
||||
"""
|
||||
|
|
@ -69,15 +53,15 @@ class BaseDynamicEntityForm(ModelForm):
|
|||
def __init__(self, data=None, *args, **kwargs):
|
||||
super(BaseDynamicEntityForm, self).__init__(data, *args, **kwargs)
|
||||
config_cls = EavRegistry.get_config_cls_for_model(self.instance.__class__)
|
||||
self.proxy = getattr(self.instance, config_cls.eav_attr)
|
||||
self.entity = getattr(self.instance, config_cls.eav_attr)
|
||||
self._build_dynamic_fields()
|
||||
|
||||
def _build_dynamic_fields(self):
|
||||
# reset form fields
|
||||
self.fields = deepcopy(self.base_fields)
|
||||
|
||||
for attribute in self.proxy.get_eav_attributes():
|
||||
value = getattr(self.proxy, attribute.slug)
|
||||
for attribute in self.entity.get_all_attributes():
|
||||
value = getattr(self.entity, attribute.slug)
|
||||
|
||||
defaults = {
|
||||
'label': attribute.name.capitalize(),
|
||||
|
|
@ -120,7 +104,7 @@ class BaseDynamicEntityForm(ModelForm):
|
|||
instance = super(BaseDynamicEntityForm, self).save(commit=False)
|
||||
|
||||
# assign attributes
|
||||
for attribute in self.proxy.get_eav_attributes():
|
||||
for attribute in self.entity.get_all_attributes():
|
||||
value = self.cleaned_data.get(attribute.slug)
|
||||
if attribute.datatype == attribute.TYPE_ENUM:
|
||||
if value:
|
||||
|
|
@ -128,7 +112,7 @@ class BaseDynamicEntityForm(ModelForm):
|
|||
else:
|
||||
value = None
|
||||
|
||||
setattr(self.proxy, attribute.slug, value)
|
||||
setattr(self.entity, attribute.slug, value)
|
||||
|
||||
# save entity and its attributes
|
||||
if commit:
|
||||
|
|
|
|||
253
models.py
253
models.py
|
|
@ -31,9 +31,9 @@ from .fields import EavSlugField, EavDatatypeField
|
|||
|
||||
|
||||
def get_unique_class_identifier(cls):
|
||||
"""
|
||||
Return a unique identifier for a class
|
||||
"""
|
||||
'''
|
||||
Return a unique identifier for a class
|
||||
'''
|
||||
return '.'.join((inspect.getfile(cls), cls.__name__))
|
||||
|
||||
|
||||
|
|
@ -86,13 +86,13 @@ class Attribute(models.Model):
|
|||
(TYPE_ENUM, _(u"Multiple Choice")),
|
||||
)
|
||||
|
||||
name = models.CharField(_(u"name"), max_length=100,
|
||||
help_text=_(u"User-friendly attribute name"))
|
||||
|
||||
slug = EavSlugField(_(u"slug"), max_length=50, db_index=True,
|
||||
help_text=_(u"Short unique attribute label"),
|
||||
unique=True)
|
||||
|
||||
name = models.CharField(_(u"name"), max_length=100,
|
||||
help_text=_(u"User-friendly attribute name"))
|
||||
|
||||
description = models.CharField(_(u"description"), max_length=256,
|
||||
blank=True, null=True,
|
||||
help_text=_(u"Short description"))
|
||||
|
|
@ -133,59 +133,33 @@ class Attribute(models.Model):
|
|||
return None
|
||||
return self.enum_group.enums.all()
|
||||
|
||||
'''
|
||||
@classmethod
|
||||
def get_for_entity_class(cls, entity_cls):
|
||||
from .utils import EavRegistry
|
||||
config_cls = EavRegistry.get_config_cls_for_model(entity_cls)
|
||||
return config_cls.get_attributes()
|
||||
'''
|
||||
|
||||
def get_value_for_entity(self, entity):
|
||||
'''
|
||||
Passed any object that may be used as an 'entity' object (is linked
|
||||
to through the generic relation from some EaveValue object. Returns
|
||||
an Value object that has a foreignkey to self (attribute) and
|
||||
to the entity. Returns nothing if a matching Value object
|
||||
doesn't exist.
|
||||
'''
|
||||
ct = ContentType.objects.get_for_model(entity)
|
||||
qs = self.value_set.filter(entity_ct=ct, entity_id=entity.pk)
|
||||
count = qs.count()
|
||||
if count > 1:
|
||||
raise AttributeError(u"You should have one and only one value "\
|
||||
u"for the attribute %s and the entity %s. Found "\
|
||||
u"%s" % (self, entity, qs.count()))
|
||||
if count:
|
||||
return qs[0]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def save_value(self, entity, value):
|
||||
"""
|
||||
Save any value for an entity, calling the appropriate method
|
||||
according to the type of the value.
|
||||
Value should not be an Value but a normal value
|
||||
"""
|
||||
self._save_single_value(entity, value)
|
||||
|
||||
|
||||
def _save_single_value(self, entity, value=None, attribute=None):
|
||||
"""
|
||||
Save a a value of type that doesn't need special joining like
|
||||
int, float, text, date, etc.
|
||||
|
||||
Value should not be an Value object but a normal value.
|
||||
Use attribute if you want to use something else than the current
|
||||
one
|
||||
"""
|
||||
ct = ContentType.objects.get_for_model(entity)
|
||||
attribute = attribute or self
|
||||
try:
|
||||
eavvalue = self.value_set.get(entity_ct=ct,
|
||||
entity_id=entity.pk,
|
||||
attribute=attribute)
|
||||
value_obj = self.value_set.get(entity_ct=ct,
|
||||
entity_id=entity.pk,
|
||||
attribute=self)
|
||||
except Value.DoesNotExist:
|
||||
eavvalue = self.value_set.model(entity_ct=ct,
|
||||
entity_id=entity.pk,
|
||||
attribute=attribute)
|
||||
if value != eavvalue.value:
|
||||
eavvalue.value = value
|
||||
eavvalue.save()
|
||||
if value == None:
|
||||
return
|
||||
value_obj = Value.objects.create(entity_ct=ct,
|
||||
entity_id=entity.pk,
|
||||
attribute=self)
|
||||
if value == None:
|
||||
value_obj.delete()
|
||||
return
|
||||
|
||||
if value != value_obj.value:
|
||||
value_obj.value = value
|
||||
value_obj.save()
|
||||
|
||||
|
||||
def __unicode__(self):
|
||||
|
|
@ -209,6 +183,10 @@ class Value(models.Model):
|
|||
|
||||
'''
|
||||
|
||||
class Meta:
|
||||
unique_together = ('entity_ct', 'entity_id', 'attribute')
|
||||
|
||||
|
||||
entity_ct = models.ForeignKey(ContentType, related_name='value_entities')
|
||||
entity_id = models.IntegerField()
|
||||
entity = generic.GenericForeignKey(ct_field='entity_ct', fk_field='entity_id')
|
||||
|
|
@ -233,7 +211,6 @@ class Value(models.Model):
|
|||
attribute = models.ForeignKey(Attribute, db_index=True,
|
||||
verbose_name=_(u"attribute"))
|
||||
|
||||
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.full_clean()
|
||||
|
|
@ -276,174 +253,64 @@ class Value(models.Model):
|
|||
|
||||
class Entity(object):
|
||||
|
||||
_cache = {}
|
||||
|
||||
def __init__(self, instance):
|
||||
self.model = instance
|
||||
self.ct = ContentType.objects.get_for_model(instance)
|
||||
|
||||
|
||||
# TODO: memoize
|
||||
def __getattr__(self, name):
|
||||
|
||||
if not name.startswith('_'):
|
||||
attribute = self.get_attribute_by_slug(name)
|
||||
if attribute:
|
||||
value_obj = attribute.get_value_for_entity(self.model)
|
||||
if value_obj:
|
||||
return value_obj.value
|
||||
return None
|
||||
|
||||
try:
|
||||
attribute = self.get_attribute_by_slug(name)
|
||||
except Attribute.DoesNotExist:
|
||||
raise AttributeError(_(u"%(obj)s has no EAV attribute named " \
|
||||
u"'%(attr)s'") % \
|
||||
{'obj':self.model, 'attr':name})
|
||||
|
||||
try:
|
||||
return self.get_value_by_attribute(attribute).value
|
||||
except Value.DoesNotExist:
|
||||
return None
|
||||
return object.__getattr__(self, name)
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_eav_attributes_for_model(cls, model_cls):
|
||||
"""
|
||||
Return the attributes for this model
|
||||
"""
|
||||
def get_all_attributes(self):
|
||||
'''
|
||||
Return all the attributes that are applicable to self.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(model_cls), {})
|
||||
|
||||
|
||||
@classmethod
|
||||
def update_attr_cache_for_model(cls, model_cls):
|
||||
"""
|
||||
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_for_model(model_cls)\
|
||||
.select_related()
|
||||
cache['slug_mapping'] = dict((s.slug, s) for s in cache['attributes'])
|
||||
return cache
|
||||
|
||||
|
||||
@classmethod
|
||||
def flush_attr_cache_for_model(cls, model_cls):
|
||||
"""
|
||||
Flush the attributes cache for this entity class
|
||||
"""
|
||||
cache = cls.get_attr_cache_for_model(model_cls)
|
||||
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):
|
||||
"""
|
||||
Create or update the attributes cache for the entity class linked
|
||||
to the current instance.
|
||||
"""
|
||||
return self.__class__.update_attr_cache_for_model(self.model.__class__)
|
||||
|
||||
|
||||
def flush_attr_cache(self):
|
||||
"""
|
||||
Flush the attributes cache for the entity class linked
|
||||
to the current instance.
|
||||
"""
|
||||
return self.__class__.flush_attr_cache_for_model(self.model.__class__)
|
||||
|
||||
|
||||
def get_attr_cache(self):
|
||||
"""
|
||||
Return the attribute cache for the entity class linked
|
||||
to the current instance.
|
||||
"""
|
||||
return self.__class__.get_attr_cache_for_model(self.model.__class__)
|
||||
|
||||
config_cls = EavRegistry.get_config_cls_for_model(self.model.__class__)
|
||||
return config_cls.get_attributes()
|
||||
|
||||
def save(self):
|
||||
for attribute in self.get_eav_attributes():
|
||||
for attribute in self.get_all_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):
|
||||
"""
|
||||
Return the current cache or if it doesn't exists, update it
|
||||
and returns it.
|
||||
"""
|
||||
cache = cls.get_attr_cache_for_model(model_cls)
|
||||
if not cache:
|
||||
cache = Entity.update_attr_cache_for_model(model_cls)
|
||||
|
||||
return cache['attributes']
|
||||
|
||||
|
||||
def get_values(self):
|
||||
'''
|
||||
Get all set EAV Value objects for self.model
|
||||
'''
|
||||
return Value.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 = Entity.update_attr_cache_for_model(model_cls)
|
||||
|
||||
return cache['slug_mapping']
|
||||
entity_id=self.model.pk).select_related()
|
||||
|
||||
|
||||
def get_all_attribute_slugs(self):
|
||||
"""
|
||||
Returns all attributes slugs for the entity
|
||||
class linked to the current instance.
|
||||
"""
|
||||
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 = Entity.update_attr_cache_for_model(model_cls)
|
||||
return cache['slug_mapping'].get(slug, None)
|
||||
return self.get_all_attributes().values_list('slug', Flat=True)
|
||||
|
||||
|
||||
def get_attribute_by_slug(self, slug):
|
||||
m_cls = self.model.__class__
|
||||
return self.__class__.get_attribute_by_slug_for_model(m_cls, slug)
|
||||
return self.get_all_attributes().get(slug=slug)
|
||||
|
||||
def get_value_by_attribute(self, attribute):
|
||||
return self.get_values().get(attribute=attribute)
|
||||
|
||||
def __iter__(self):
|
||||
"""
|
||||
Iterates over non-empty EAV attributes. Normal fields are not included.
|
||||
"""
|
||||
return iter(self.get_values())
|
||||
|
||||
|
||||
# todo: cache all changed value in EAV and check against existing attribtue
|
||||
@staticmethod
|
||||
def save_handler(sender, *args, **kwargs):
|
||||
from .utils import EavRegistry
|
||||
config_cls = EavRegistry.get_config_cls_for_model(sender)
|
||||
instance_eav = getattr(kwargs['instance'], config_cls.eav_attr)
|
||||
instance_eav.save()
|
||||
|
||||
|
||||
entity = getattr(kwargs['instance'], config_cls.eav_attr)
|
||||
entity.save()
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ class EavBasicTests(TestCase):
|
|||
self.entity = Patient.objects.create(name="Doe")
|
||||
|
||||
self.value = Value.objects.create(entity=self.entity,
|
||||
attribute=self.attribute,
|
||||
value_text='Denver')
|
||||
attribute=self.attribute,
|
||||
value_text='Denver')
|
||||
|
||||
def tearDown(self):
|
||||
EavRegistry.unregister(Patient)
|
||||
|
|
@ -43,15 +43,11 @@ class EavBasicTests(TestCase):
|
|||
|
||||
|
||||
def test_can_eaventity_children_give_you_all_attributes_by_default(self):
|
||||
qs = Patient.eav.get_eav_attributes()
|
||||
p = Patient.objects.create(name='bob')
|
||||
qs = p.eav.get_all_attributes()
|
||||
self.assertEqual(list(qs), list(Attribute.objects.all()))
|
||||
|
||||
|
||||
def test_value_creation(self):
|
||||
Value.objects.create(entity=self.entity,
|
||||
attribute=self.attribute,
|
||||
value_float=1.2)
|
||||
|
||||
def test_value_unicode(self):
|
||||
self.assertEqual(unicode(self.value), "Doe - City: \"Denver\"")
|
||||
|
||||
|
|
|
|||
|
|
@ -217,9 +217,11 @@ class EavFilterTests(TestCase):
|
|||
EavRegistry.register(User)
|
||||
|
||||
Attribute.objects.create(datatype=Attribute.TYPE_INT,
|
||||
name='Height')
|
||||
name='Height')
|
||||
|
||||
Attribute.objects.create(datatype=Attribute.TYPE_FLOAT,
|
||||
name='Weight')
|
||||
name='Weight')
|
||||
|
||||
u = User.objects.create(username='Bob')
|
||||
u.eav.height = 10
|
||||
u.eav.weight = 20
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ class EavSetterAndGetterTests(TestCase):
|
|||
|
||||
|
||||
def setUp(self):
|
||||
|
||||
EavRegistry.unregister(Patient)
|
||||
EavRegistry.register(Patient)
|
||||
|
||||
|
|
@ -27,8 +26,9 @@ class EavSetterAndGetterTests(TestCase):
|
|||
self.patient = Patient.objects.create(name="Doe")
|
||||
|
||||
self.value = Value.objects.create(entity=self.patient,
|
||||
attribute=self.attribute,
|
||||
value_text='Denver')
|
||||
attribute=self.attribute,
|
||||
value_text='Denver')
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
EavRegistry.unregister(Patient)
|
||||
|
|
@ -38,48 +38,36 @@ class EavSetterAndGetterTests(TestCase):
|
|||
def additional_attribute_setup(self):
|
||||
|
||||
self.country_attr = Attribute.objects\
|
||||
.create(datatype=Attribute.TYPE_TEXT,
|
||||
name='Country', slug='country')
|
||||
.create(datatype=Attribute.TYPE_TEXT,
|
||||
name='Country', slug='country')
|
||||
|
||||
class PatientEav(EavConfig):
|
||||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
def get_attributes(cls):
|
||||
return Attribute.objects.filter(slug='country')
|
||||
|
||||
|
||||
self.PatientEav = PatientEav
|
||||
|
||||
|
||||
class UserEav(EavConfig):
|
||||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
def get_attributes(cls):
|
||||
return Attribute.objects.filter(slug='city')
|
||||
|
||||
self.UserEav = UserEav
|
||||
|
||||
|
||||
def test_get_value_to_entity(self):
|
||||
self.assertEqual(self.attribute.get_value_for_entity(self.patient),
|
||||
self.value)
|
||||
self.UserEav = UserEav
|
||||
|
||||
|
||||
def test_save_single_value(self):
|
||||
patient = Patient.objects.create(name="x")
|
||||
attr = Attribute.objects.create(datatype=Attribute.TYPE_TEXT,
|
||||
name='a', slug='a')
|
||||
# does nothing
|
||||
attr._save_single_value(patient)
|
||||
|
||||
|
||||
# save value
|
||||
attr._save_single_value(patient, 'b')
|
||||
attr.save_value(patient, 'b')
|
||||
patient = Patient.objects.get(name="x")
|
||||
self.assertEqual(patient.eav.a, 'b')
|
||||
|
||||
# save value on another attribute
|
||||
attr._save_single_value(patient, 'Paris', self.attribute)
|
||||
patient = Patient.objects.get(name="x")
|
||||
self.assertEqual(patient.eav.city, 'Paris')
|
||||
|
||||
|
||||
|
||||
def test_save_value(self):
|
||||
# TODO: update test_save_value when multiple values are out
|
||||
|
|
@ -169,15 +157,7 @@ class EavSetterAndGetterTests(TestCase):
|
|||
|
||||
def test_get_a_value_that_does_not_exists(self):
|
||||
|
||||
# return None for non '_' values
|
||||
self.assertEqual(self.patient.eav.impossible_value, None)
|
||||
|
||||
# normal behavior for '_' values
|
||||
try:
|
||||
self.patient.eav._impossible_value
|
||||
self.fail()
|
||||
except AttributeError:
|
||||
pass
|
||||
self.assertFalse(hasattr(self.patient.eav, 'impossible_value'))
|
||||
|
||||
|
||||
def test_attributes_are_filtered_according_to_config_class(self):
|
||||
|
|
@ -188,15 +168,18 @@ class EavSetterAndGetterTests(TestCase):
|
|||
EavRegistry.register(Patient, self.PatientEav)
|
||||
EavRegistry.register(User, self.UserEav)
|
||||
|
||||
self.assertEqual(list(Patient.eav.get_eav_attributes()),
|
||||
p = Patient.objects.create(name='Bob')
|
||||
|
||||
u = User.objects.create(username='hope')
|
||||
|
||||
self.assertEqual(list(p.eav.get_all_attributes()),
|
||||
list(Attribute.objects.filter(slug='country')))
|
||||
|
||||
self.assertEqual(list(User.eav.get_eav_attributes()),
|
||||
self.assertEqual(list(u.eav.get_all_attributes()),
|
||||
list(Attribute.objects.filter(slug='city')))
|
||||
|
||||
|
||||
def test_can_filter_attribute_availability_for_entity(self):
|
||||
|
||||
self.additional_attribute_setup()
|
||||
|
||||
self.patient.eav.city = 'Tunis'
|
||||
|
|
@ -213,7 +196,7 @@ class EavSetterAndGetterTests(TestCase):
|
|||
p.save()
|
||||
p = Patient.objects.get(pk=p.pk)
|
||||
|
||||
self.assertFalse(p.eav.city, 'Paris')
|
||||
self.assertFalse(hasattr(p.eav, 'city'))
|
||||
self.assertEqual(p.eav.country, 'USA')
|
||||
|
||||
|
||||
|
|
@ -238,10 +221,10 @@ class EavSetterAndGetterTests(TestCase):
|
|||
p = Patient.objects.get(pk=p.pk)
|
||||
u = User.objects.get(pk=u.pk)
|
||||
|
||||
self.assertFalse(p.eav.city)
|
||||
self.assertFalse(hasattr(p.eav, 'city'))
|
||||
self.assertEqual(p.eav.country, 'USA')
|
||||
|
||||
self.assertFalse(u.eav.country)
|
||||
self.assertFalse(hasattr(u.eav, 'country'))
|
||||
self.assertEqual(u.eav.city, 'Paris')
|
||||
|
||||
|
||||
|
|
@ -254,7 +237,7 @@ class EavSetterAndGetterTests(TestCase):
|
|||
class SubPatientEav(self.PatientEav):
|
||||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
def get_attributes(cls):
|
||||
return Attribute.objects.filter(slug='country')
|
||||
|
||||
EavRegistry.register(Patient, SubPatientEav)
|
||||
|
|
@ -265,7 +248,7 @@ class EavSetterAndGetterTests(TestCase):
|
|||
|
||||
p = Patient.objects.get(pk=self.patient.pk)
|
||||
|
||||
self.assertFalse(p.eav.city)
|
||||
self.assertFalse(hasattr(p.eav, 'city'))
|
||||
self.assertEqual(p.eav.country, 'USA')
|
||||
|
||||
|
||||
|
|
@ -296,18 +279,7 @@ class EavSetterAndGetterTests(TestCase):
|
|||
self.assertEqual(self.value.value_object, None)
|
||||
|
||||
|
||||
def test_get_eav_attributes(self):
|
||||
|
||||
self.additional_attribute_setup()
|
||||
EavRegistry.unregister(Patient)
|
||||
EavRegistry.register(Patient, self.PatientEav)
|
||||
|
||||
assert list(self.PatientEav.get_eav_attributes_for_model(Patient))\
|
||||
== list(Entity.get_eav_attributes_for_model(Patient))\
|
||||
== list(self.patient.eav.get_eav_attributes())\
|
||||
== list(Patient.eav.get_eav_attributes_for_model(Patient))\
|
||||
== list(Attribute.objects.filter(slug='country'))
|
||||
|
||||
|
||||
|
||||
def test_values(self):
|
||||
self.additional_attribute_setup()
|
||||
|
|
@ -318,33 +290,7 @@ class EavSetterAndGetterTests(TestCase):
|
|||
self.assertEqual(list(self.patient.eav.get_values()),
|
||||
list(Value.objects.exclude(value_text='Denver')))
|
||||
|
||||
|
||||
def test_get_all_attribute_slugs_for_model(self):
|
||||
|
||||
self.country_attr = Attribute.objects\
|
||||
.create(datatype=Attribute.TYPE_TEXT,
|
||||
name='Street', slug='street')
|
||||
|
||||
self.additional_attribute_setup()
|
||||
|
||||
class UserEav(EavConfig):
|
||||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
return Attribute.objects.exclude(slug='city')
|
||||
|
||||
EavRegistry.register(User, UserEav)
|
||||
|
||||
u = User.objects.create(username='John')
|
||||
|
||||
slugs = dict((s.slug, s) for s in Attribute.objects\
|
||||
.exclude(slug='city'))
|
||||
|
||||
assert slugs\
|
||||
== Entity.get_all_attribute_slugs_for_model(User)\
|
||||
== User.eav.get_all_attribute_slugs_for_model(User)\
|
||||
== u.eav.get_all_attribute_slugs()
|
||||
|
||||
|
||||
def test_iteration(self):
|
||||
self.additional_attribute_setup()
|
||||
|
|
|
|||
30
utils.py
30
utils.py
|
|
@ -34,7 +34,7 @@ class EavConfig(Entity):
|
|||
generic_relation_related_name = None
|
||||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
def get_attributes(cls):
|
||||
"""
|
||||
By default, all attributes apply to an entity,
|
||||
unless otherwise specified.
|
||||
|
|
@ -70,18 +70,7 @@ class EavRegistry(object):
|
|||
config_cls = EavRegistry.get_config_cls_for_model(sender)
|
||||
|
||||
setattr(instance, config_cls.eav_attr, Entity(instance))
|
||||
|
||||
|
||||
@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():
|
||||
Entity.update_attr_cache_for_model(cache['model_cls'])
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def wrap_config_class(model_cls, config_cls):
|
||||
"""
|
||||
|
|
@ -132,19 +121,13 @@ class EavRegistry(object):
|
|||
|
||||
# todo : not useful anymore ?
|
||||
setattr(getattr(model_cls, config_cls.eav_attr),
|
||||
'get_eav_attributes', config_cls.get_eav_attributes)
|
||||
'get_attributes', config_cls.get_attributes)
|
||||
|
||||
# attache the new manager to the model
|
||||
mgr = EntityManager()
|
||||
mgr.contribute_to_class(model_cls, config_cls.manager_attr)
|
||||
|
||||
if not manager_only:
|
||||
# todo: see with david how to change that
|
||||
try:
|
||||
Entity.update_attr_cache_for_model(model_cls)
|
||||
except DatabaseError:
|
||||
pass
|
||||
|
||||
# todo: make that overridable
|
||||
# attach the generic relation to the model
|
||||
if config_cls.generic_relation_related_name:
|
||||
|
|
@ -200,15 +183,10 @@ class EavRegistry(object):
|
|||
delattr(model_cls, config_cls.eav_attr)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if not manager_only:
|
||||
Entity.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=Attribute)
|
||||
post_delete.connect(EavRegistry.update_attribute_cache, sender=Attribute)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue