mirror of
https://github.com/jazzband/django-eav2.git
synced 2026-03-31 21:50:26 +00:00
Rename models, config attrs, started admin intigration
This commit is contained in:
parent
8a46f3938f
commit
515cdf07ee
9 changed files with 393 additions and 187 deletions
108
admin.py
Normal file
108
admin.py
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#!/usr/bin/env python
|
||||
# vim: ai ts=4 sts=4 et sw=4 coding=utf-8
|
||||
#
|
||||
# This software is derived from EAV-Django originally written and
|
||||
# copyrighted by Andrey Mikhaylenko <http://pypi.python.org/pypi/eav-django>
|
||||
#
|
||||
# This is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This software is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with EAV-Django. If not, see <http://gnu.org/licenses/>.
|
||||
|
||||
|
||||
from django.contrib import admin
|
||||
from django.contrib.admin.options import (
|
||||
ModelAdmin, InlineModelAdmin, StackedInline
|
||||
)
|
||||
from django.forms.models import BaseInlineFormSet
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from .models import Attribute, Value, EnumValue, EnumGroup
|
||||
|
||||
|
||||
class BaseEntityAdmin(ModelAdmin):
|
||||
|
||||
def render_change_form(self, request, context, **kwargs):
|
||||
"""
|
||||
Wrapper for ModelAdmin.render_change_form. Replaces standard static
|
||||
AdminForm with an EAV-friendly one. The point is that our form generates
|
||||
fields dynamically and fieldsets must be inferred from a prepared and
|
||||
validated form instance, not just the form class. Django does not seem
|
||||
to provide hooks for this purpose, so we simply wrap the view and
|
||||
substitute some data.
|
||||
"""
|
||||
form = context['adminform'].form
|
||||
|
||||
# infer correct data from the form
|
||||
fieldsets = [(None, {'fields': form.fields.keys()})]
|
||||
adminform = admin.helpers.AdminForm(form, fieldsets,
|
||||
self.prepopulated_fields)
|
||||
media = mark_safe(self.media + adminform.media)
|
||||
|
||||
context.update(adminform=adminform, media=media)
|
||||
|
||||
super_meth = super(BaseEntityAdmin, self).render_change_form
|
||||
return super_meth(request, context, **kwargs)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class BaseEntityInlineFormSet(BaseInlineFormSet):
|
||||
"""
|
||||
An inline formset that correctly initializes EAV forms.
|
||||
"""
|
||||
def add_fields(self, form, index):
|
||||
if self.instance:
|
||||
setattr(form.instance, self.fk.name, self.instance)
|
||||
form._build_dynamic_fields()
|
||||
super(BaseEntityInlineFormSet, self).add_fields(form, index)
|
||||
|
||||
|
||||
class BaseEntityInline(InlineModelAdmin):
|
||||
"""
|
||||
Inline model admin that works correctly with EAV attributes. You should mix
|
||||
in the standard StackedInline or TabularInline classes in order to define
|
||||
formset representation, e.g.::
|
||||
|
||||
class ItemInline(BaseEntityInline, StackedInline):
|
||||
model = Item
|
||||
form = forms.ItemForm
|
||||
|
||||
.. warning: TabularInline does *not* work out of the box. There is,
|
||||
however, a patched template `admin/edit_inline/tabular.html` bundled
|
||||
with EAV-Django. You can copy or symlink the `admin` directory to your
|
||||
templates search path (see Django documentation).
|
||||
|
||||
"""
|
||||
formset = BaseEntityInlineFormSet
|
||||
|
||||
def get_fieldsets(self, request, obj=None):
|
||||
if self.declared_fieldsets:
|
||||
return self.declared_fieldsets
|
||||
|
||||
formset = self.get_formset(request)
|
||||
fk_name = self.fk_name or formset.fk.name
|
||||
kw = {fk_name: obj} if obj else {}
|
||||
instance = self.model(**kw)
|
||||
form = formset.form(request.POST, instance=instance)
|
||||
|
||||
return [(None, {'fields': form.fields.keys()})]
|
||||
|
||||
class AttributeAdmin(ModelAdmin):
|
||||
list_display = ('name', 'slug', 'datatype', 'description')
|
||||
prepopulated_fields = {'name': ('slug',)}
|
||||
|
||||
admin.site.register(Attribute, AttributeAdmin)
|
||||
admin.site.register(Value)
|
||||
admin.site.register(EnumValue)
|
||||
admin.site.register(EnumGroup)
|
||||
|
||||
|
|
@ -66,9 +66,10 @@ class EavDatatypeField(models.CharField):
|
|||
once it have been created.
|
||||
"""
|
||||
super(EavDatatypeField, self).validate(value, instance)
|
||||
from .models import EavAttribute
|
||||
from .models import Attribute
|
||||
if not instance.pk:
|
||||
return
|
||||
if value != EavAttribute.objects.get(pk=instance.pk).datatype:
|
||||
if value != Attribute.objects.get(pk=instance.pk).datatype:
|
||||
raise ValidationError(_(u"You cannot change the datatype of an "
|
||||
u"existing attribute."))
|
||||
|
||||
|
|
|
|||
137
forms.py
Normal file
137
forms.py
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#!/usr/bin/env python
|
||||
# vim: ai ts=4 sts=4 et sw=4 coding=utf-8
|
||||
#
|
||||
# This software is derived from EAV-Django originally written and
|
||||
# copyrighted by Andrey Mikhaylenko <http://pypi.python.org/pypi/eav-django>
|
||||
#
|
||||
# This is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This software is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with EAV-Django. If not, see <http://gnu.org/licenses/>.
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
# django
|
||||
from django.forms import BooleanField, CharField, DateField, FloatField, \
|
||||
IntegerField, ModelForm, ChoiceField, ValidationError
|
||||
from django.contrib.admin.widgets import AdminDateWidget, FilteredSelectMultiple #, RelatedFieldWidgetWrapper
|
||||
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):
|
||||
"""
|
||||
ModelForm for entity with support for EAV attributes. Form fields are created
|
||||
on the fly depending on Schema defined for given entity instance. If no schema
|
||||
is defined (i.e. the entity instance has not been saved yet), only static
|
||||
fields are used. However, on form validation the schema will be retrieved
|
||||
and EAV fields dynamically added to the form, so when the validation is
|
||||
actually done, all EAV fields are present in it (unless Rubric is not defined).
|
||||
"""
|
||||
|
||||
FIELD_CLASSES = {
|
||||
'text': CharField,
|
||||
'float': FloatField,
|
||||
'int': IntegerField,
|
||||
'date': DateField,
|
||||
'bool': BooleanField,
|
||||
'enum': ChoiceField, #RelatedFieldWidgetWrapper(MultipleChoiceField),
|
||||
}
|
||||
FIELD_EXTRA = {
|
||||
'date': {'widget': AdminDateWidget},
|
||||
}
|
||||
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._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)
|
||||
|
||||
defaults = {
|
||||
'label': attribute.name.capitalize(),
|
||||
'required': False,
|
||||
'help_text': attribute.help_text,
|
||||
}
|
||||
|
||||
datatype = attribute.datatype
|
||||
if datatype == attribute.TYPE_ENUM:
|
||||
enums = attribute.get_choices() \
|
||||
.values_list('id', 'value')
|
||||
|
||||
choices = [('', '-----')] + list(enums)
|
||||
|
||||
defaults.update({'choices': choices})
|
||||
if value:
|
||||
defaults.update({'initial': value.pk})
|
||||
|
||||
|
||||
MappedField = self.FIELD_CLASSES[datatype]
|
||||
self.fields[attribute.slug] = MappedField(**defaults)
|
||||
|
||||
# fill initial data (if attribute was already defined)
|
||||
if value and not datatype == attribute.TYPE_ENUM: # m2m is already done above
|
||||
self.initial[attribute.slug] = value
|
||||
|
||||
def save(self, commit=True):
|
||||
"""
|
||||
Saves this ``form``'s cleaned_data into model instance ``self.instance``
|
||||
and related EAV attributes.
|
||||
|
||||
Returns ``instance``.
|
||||
"""
|
||||
|
||||
if self.errors:
|
||||
raise ValueError("The %s could not be saved because the data didn't"
|
||||
" validate." % self.instance._meta.object_name)
|
||||
|
||||
# create entity instance, don't save yet
|
||||
instance = super(BaseDynamicEntityForm, self).save(commit=False)
|
||||
|
||||
# assign attributes
|
||||
for attribute in self.proxy.get_eav_attributes():
|
||||
value = self.cleaned_data.get(attribute.slug)
|
||||
if attribute.datatype == attribute.TYPE_ENUM:
|
||||
if value:
|
||||
value = attribute.enum_group.enums.get(pk=value)
|
||||
else:
|
||||
value = None
|
||||
|
||||
setattr(self.proxy, attribute.slug, value)
|
||||
|
||||
# save entity and its attributes
|
||||
if commit:
|
||||
instance.save()
|
||||
|
||||
return instance
|
||||
12
managers.py
12
managers.py
|
|
@ -21,7 +21,7 @@ from functools import wraps
|
|||
|
||||
from django.db import models
|
||||
|
||||
from .models import EavAttribute, EavValue
|
||||
from .models import Attribute, Value
|
||||
|
||||
def eav_filter(func):
|
||||
'''
|
||||
|
|
@ -78,22 +78,22 @@ def expand_eav_filter(model_cls, key, value):
|
|||
value = 5
|
||||
Would return:
|
||||
key = 'eav_values__in'
|
||||
value = EavValues.objects.filter(value_int=5, attribute__slug='height')
|
||||
value = Values.objects.filter(value_int=5, attribute__slug='height')
|
||||
'''
|
||||
from .utils import EavRegistry
|
||||
fields = key.split('__')
|
||||
|
||||
config_cls = EavRegistry.get_config_cls_for_model(model_cls)
|
||||
if len(fields) > 1 and config_cls and \
|
||||
fields[0] == config_cls.proxy_field_name:
|
||||
fields[0] == config_cls.eav_attr:
|
||||
slug = fields[1]
|
||||
gr_name = config_cls.generic_relation_field_name
|
||||
datatype = EavAttribute.objects.get(slug=slug).datatype
|
||||
gr_name = config_cls.generic_relation_attr
|
||||
datatype = Attribute.objects.get(slug=slug).datatype
|
||||
|
||||
lookup = '__%s' % fields[2] if len(fields) > 2 else ''
|
||||
kwargs = {'value_%s%s' % (datatype, lookup): value,
|
||||
'attribute__slug': slug}
|
||||
value = EavValue.objects.filter(**kwargs)
|
||||
value = Value.objects.filter(**kwargs)
|
||||
|
||||
return '%s__in' % gr_name, value
|
||||
|
||||
|
|
|
|||
100
models.py
100
models.py
|
|
@ -37,14 +37,6 @@ def get_unique_class_identifier(cls):
|
|||
return '.'.join((inspect.getfile(cls), cls.__name__))
|
||||
|
||||
|
||||
class EavAttributeLabel(models.Model):
|
||||
name = models.CharField(_(u"name"), db_index=True,
|
||||
unique=True, max_length=100)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class EnumValue(models.Model):
|
||||
value = models.CharField(_(u"value"), db_index=True,
|
||||
unique=True, max_length=50)
|
||||
|
|
@ -62,16 +54,16 @@ class EnumGroup(models.Model):
|
|||
return self.name
|
||||
|
||||
|
||||
class EavAttribute(models.Model):
|
||||
class Attribute(models.Model):
|
||||
'''
|
||||
The A model in E-A-V. This holds the 'concepts' along with the data type
|
||||
something like:
|
||||
|
||||
>>> EavAttribute.objects.create(name='Height', datatype='float')
|
||||
<EavAttribute: Height (Float)>
|
||||
>>> Attribute.objects.create(name='Height', datatype='float')
|
||||
<Attribute: Height (Float)>
|
||||
|
||||
>>> EavAttribute.objects.create(name='Color', datatype='text', slug='color')
|
||||
<EavAttribute: Color (Text)>
|
||||
>>> Attribute.objects.create(name='Color', datatype='text', slug='color')
|
||||
<Attribute: Color (Text)>
|
||||
'''
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
|
@ -115,47 +107,43 @@ class EavAttribute(models.Model):
|
|||
datatype = EavDatatypeField(_(u"data type"), max_length=6,
|
||||
choices=DATATYPE_CHOICES)
|
||||
|
||||
created = models.DateTimeField(_(u"created"), default=datetime.now)
|
||||
created = models.DateTimeField(_(u"created"), default=datetime.now,
|
||||
editable=False)
|
||||
|
||||
modified = models.DateTimeField(_(u"modified"), auto_now=True)
|
||||
|
||||
labels = models.ManyToManyField(EavAttributeLabel,
|
||||
verbose_name=_(u"labels"))
|
||||
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
self.slug = EavSlugField.create_slug_from_name(self.name)
|
||||
self.full_clean()
|
||||
super(EavAttribute, self).save(*args, **kwargs)
|
||||
super(Attribute, self).save(*args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
if self.datatype == self.TYPE_ENUM and not enum_group:
|
||||
if self.datatype == self.TYPE_ENUM and not self.enum_group:
|
||||
raise ValidationError(_(
|
||||
u"You must set the enum_group for TYPE_ENUM attributes"))
|
||||
u"You must set the choice group for multiple choice" \
|
||||
u"attributes"))
|
||||
|
||||
def add_label(self, label):
|
||||
label, created = EavAttributeLabel.objects.get_or_create(name=label)
|
||||
label.eavattribute_set.add(self)
|
||||
|
||||
def remove_label(self, label):
|
||||
try:
|
||||
label_obj = EavAttributeLabel.objects.get(name=label)
|
||||
except EavAttributeLabel.DoesNotExist:
|
||||
return
|
||||
self.labels.remove(label_obj)
|
||||
def get_choices(self):
|
||||
'''
|
||||
Returns the avilable choices for enums.
|
||||
'''
|
||||
if not self.datatype == Attribute.TYPE_ENUM:
|
||||
return None
|
||||
return self.enum_group.enums.all()
|
||||
|
||||
|
||||
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 EavValue object that has a foreignkey to self (attribute) and
|
||||
to the entity. Returns nothing if a matching EavValue object
|
||||
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.eavvalue_set.filter(entity_ct=ct, entity_id=entity.pk)
|
||||
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 "\
|
||||
|
|
@ -171,7 +159,7 @@ class EavAttribute(models.Model):
|
|||
"""
|
||||
Save any value for an entity, calling the appropriate method
|
||||
according to the type of the value.
|
||||
Value should not be an EavValue but a normal value
|
||||
Value should not be an Value but a normal value
|
||||
"""
|
||||
self._save_single_value(entity, value)
|
||||
|
||||
|
|
@ -181,18 +169,18 @@ class EavAttribute(models.Model):
|
|||
Save a a value of type that doesn't need special joining like
|
||||
int, float, text, date, etc.
|
||||
|
||||
Value should not be an EavValue object but a normal value.
|
||||
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.eavvalue_set.get(entity_ct=ct,
|
||||
eavvalue = self.value_set.get(entity_ct=ct,
|
||||
entity_id=entity.pk,
|
||||
attribute=attribute)
|
||||
except EavValue.DoesNotExist:
|
||||
eavvalue = self.eavvalue_set.model(entity_ct=ct,
|
||||
except Value.DoesNotExist:
|
||||
eavvalue = self.value_set.model(entity_ct=ct,
|
||||
entity_id=entity.pk,
|
||||
attribute=attribute)
|
||||
if value != eavvalue.value:
|
||||
|
|
@ -204,7 +192,7 @@ class EavAttribute(models.Model):
|
|||
return u"%s (%s)" % (self.name, self.get_datatype_display())
|
||||
|
||||
|
||||
class EavValue(models.Model):
|
||||
class Value(models.Model):
|
||||
'''
|
||||
The V model in E-A-V. This holds the 'value' for an attribute and an
|
||||
entity:
|
||||
|
|
@ -214,10 +202,10 @@ class EavValue(models.Model):
|
|||
>>> from .utils import EavRegistry
|
||||
>>> EavRegistry.register(User)
|
||||
>>> u = User.objects.create(username='crazy_dev_user')
|
||||
>>> a = EavAttribute.objects.create(name='Favorite Drink', datatype='text',
|
||||
>>> a = Attribute.objects.create(name='Favorite Drink', datatype='text',
|
||||
... slug='fav_drink')
|
||||
>>> EavValue.objects.create(entity=u, attribute=a, value_text='red bull')
|
||||
<EavValue: crazy_dev_user - Favorite Drink: "red bull">
|
||||
>>> Value.objects.create(entity=u, attribute=a, value_text='red bull')
|
||||
<Value: crazy_dev_user - Favorite Drink: "red bull">
|
||||
|
||||
'''
|
||||
|
||||
|
|
@ -230,7 +218,8 @@ class EavValue(models.Model):
|
|||
value_int = models.IntegerField(blank=True, null=True)
|
||||
value_date = models.DateTimeField(blank=True, null=True)
|
||||
value_bool = models.NullBooleanField(blank=True, null=True)
|
||||
value_enum = models.ForeignKey(EnumValue, blank=True, null=True)
|
||||
value_enum = models.ForeignKey(EnumValue, blank=True, null=True,
|
||||
related_name='eav_values')
|
||||
|
||||
generic_value_id = models.IntegerField(blank=True, null=True)
|
||||
generic_value_ct = models.ForeignKey(ContentType, blank=True, null=True,
|
||||
|
|
@ -241,17 +230,19 @@ class EavValue(models.Model):
|
|||
created = models.DateTimeField(_(u"created"), default=datetime.now)
|
||||
modified = models.DateTimeField(_(u"modified"), auto_now=True)
|
||||
|
||||
attribute = models.ForeignKey(EavAttribute, db_index=True,
|
||||
attribute = models.ForeignKey(Attribute, db_index=True,
|
||||
verbose_name=_(u"attribute"))
|
||||
|
||||
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.full_clean()
|
||||
super(EavValue, self).save(*args, **kwargs)
|
||||
super(Value, self).save(*args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
if self.attribute.datatype == EavAttribute.TYPE_ENUM and \
|
||||
if self.attribute.datatype == Attribute.TYPE_ENUM and \
|
||||
self.value_enum:
|
||||
if self.value_enum not in self.attribute.enumvalues:
|
||||
if self.value_enum not in self.attribute.enum_group.enums.all():
|
||||
raise ValidationError(_(u"%(choice)s is not a valid " \
|
||||
u"choice for %s(attribute)") % \
|
||||
{'choice': self.value_enum,
|
||||
|
|
@ -266,10 +257,9 @@ class EavValue(models.Model):
|
|||
if field.name.startswith('value_') and field.null == True:
|
||||
setattr(self, field.name, None)
|
||||
|
||||
|
||||
def _get_value(self):
|
||||
"""
|
||||
Get returns the Python object hold by this EavValue object.
|
||||
Get returns the Python object hold by this Value object.
|
||||
"""
|
||||
return getattr(self, 'value_%s' % self.attribute.datatype)
|
||||
|
||||
|
|
@ -284,7 +274,7 @@ class EavValue(models.Model):
|
|||
return u"%s - %s: \"%s\"" % (self.entity, self.attribute.name, self.value)
|
||||
|
||||
|
||||
class EavEntity(object):
|
||||
class Entity(object):
|
||||
|
||||
_cache = {}
|
||||
|
||||
|
|
@ -393,13 +383,13 @@ class EavEntity(object):
|
|||
"""
|
||||
cache = cls.get_attr_cache_for_model(model_cls)
|
||||
if not cache:
|
||||
cache = EavEntity.update_attr_cache_for_model(model_cls)
|
||||
cache = Entity.update_attr_cache_for_model(model_cls)
|
||||
|
||||
return cache['attributes']
|
||||
|
||||
|
||||
def get_values(self):
|
||||
return EavValue.objects.filter(entity_ct=self.ct,
|
||||
return Value.objects.filter(entity_ct=self.ct,
|
||||
entity_id=self.model.pk).select_related()
|
||||
|
||||
@classmethod
|
||||
|
|
@ -410,7 +400,7 @@ class EavEntity(object):
|
|||
"""
|
||||
cache = cls.get_attr_cache_for_model(model_cls)
|
||||
if not cache:
|
||||
cache = EavEntity.update_attr_cache_for_model(model_cls)
|
||||
cache = Entity.update_attr_cache_for_model(model_cls)
|
||||
|
||||
return cache['slug_mapping']
|
||||
|
||||
|
|
@ -432,7 +422,7 @@ class EavEntity(object):
|
|||
"""
|
||||
cache = cls.get_attr_cache_for_model(model_cls)
|
||||
if not cache:
|
||||
cache = EavEntity.update_attr_cache_for_model(model_cls)
|
||||
cache = Entity.update_attr_cache_for_model(model_cls)
|
||||
return cache['slug_mapping'].get(slug, None)
|
||||
|
||||
|
||||
|
|
@ -453,7 +443,7 @@ class EavEntity(object):
|
|||
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.proxy_field_name)
|
||||
instance_eav = getattr(kwargs['instance'], config_cls.eav_attr)
|
||||
instance_eav.save()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@ class EavBasicTests(TestCase):
|
|||
EavRegistry.unregister(Patient)
|
||||
EavRegistry.register(Patient)
|
||||
|
||||
self.attribute = EavAttribute.objects.create(datatype=EavAttribute.TYPE_TEXT,
|
||||
self.attribute = Attribute.objects.create(datatype=Attribute.TYPE_TEXT,
|
||||
name='City', help_text='The City', slug='city')
|
||||
|
||||
self.entity = Patient.objects.create(name="Doe")
|
||||
|
||||
self.value = EavValue.objects.create(entity=self.entity,
|
||||
self.value = Value.objects.create(entity=self.entity,
|
||||
attribute=self.attribute,
|
||||
value_text='Denver')
|
||||
|
||||
|
|
@ -37,18 +37,18 @@ class EavBasicTests(TestCase):
|
|||
|
||||
|
||||
def test_can_create_attribute(self):
|
||||
EavAttribute.objects.create(datatype=EavAttribute.TYPE_TEXT,
|
||||
Attribute.objects.create(datatype=Attribute.TYPE_TEXT,
|
||||
name='My text test', slug='test',
|
||||
help_text='My help text')
|
||||
|
||||
|
||||
def test_can_eaventity_children_give_you_all_attributes_by_default(self):
|
||||
qs = Patient.eav.get_eav_attributes()
|
||||
self.assertEqual(list(qs), list(EavAttribute.objects.all()))
|
||||
self.assertEqual(list(qs), list(Attribute.objects.all()))
|
||||
|
||||
|
||||
def test_value_creation(self):
|
||||
EavValue.objects.create(entity=self.entity,
|
||||
Value.objects.create(entity=self.entity,
|
||||
attribute=self.attribute,
|
||||
value_float=1.2)
|
||||
|
||||
|
|
@ -58,20 +58,20 @@ class EavBasicTests(TestCase):
|
|||
|
||||
|
||||
def test_value_types(self):
|
||||
_text = EavAttribute.objects.create(datatype=EavAttribute.TYPE_TEXT,
|
||||
_text = Attribute.objects.create(datatype=Attribute.TYPE_TEXT,
|
||||
name='Text', slug='text',
|
||||
help_text='The text')
|
||||
val = EavValue.objects.create(entity=self.entity,
|
||||
val = Value.objects.create(entity=self.entity,
|
||||
attribute = _text)
|
||||
value = "Test text"
|
||||
val.value = value
|
||||
val.save()
|
||||
self.assertEqual(val.value, value)
|
||||
|
||||
_float = EavAttribute.objects.create(datatype=EavAttribute.TYPE_FLOAT,
|
||||
_float = Attribute.objects.create(datatype=Attribute.TYPE_FLOAT,
|
||||
name='Float', slug='float',
|
||||
help_text='The float')
|
||||
val = EavValue.objects.create(entity=self.entity,
|
||||
val = Value.objects.create(entity=self.entity,
|
||||
attribute = _float)
|
||||
value = 1.22
|
||||
val.value = value
|
||||
|
|
@ -79,30 +79,30 @@ class EavBasicTests(TestCase):
|
|||
self.assertEqual(val.value, value)
|
||||
|
||||
|
||||
_int = EavAttribute.objects.create(datatype=EavAttribute.TYPE_INT,
|
||||
_int = Attribute.objects.create(datatype=Attribute.TYPE_INT,
|
||||
name='Int', slug='int',
|
||||
help_text='The int')
|
||||
val = EavValue.objects.create(entity=self.entity,
|
||||
val = Value.objects.create(entity=self.entity,
|
||||
attribute = _int)
|
||||
value = 7
|
||||
val.value = value
|
||||
val.save()
|
||||
self.assertEqual(val.value, value)
|
||||
|
||||
_date = EavAttribute.objects.create(datatype=EavAttribute.TYPE_DATE,
|
||||
_date = Attribute.objects.create(datatype=Attribute.TYPE_DATE,
|
||||
name='Date', slug='date',
|
||||
help_text='The date')
|
||||
val = EavValue.objects.create(entity=self.entity,
|
||||
val = Value.objects.create(entity=self.entity,
|
||||
attribute = _date)
|
||||
value = datetime.now()
|
||||
val.value = value
|
||||
val.save()
|
||||
self.assertEqual(val.value, value)
|
||||
|
||||
_bool = EavAttribute.objects.create(datatype=EavAttribute.TYPE_BOOLEAN,
|
||||
_bool = Attribute.objects.create(datatype=Attribute.TYPE_BOOLEAN,
|
||||
name='Bool', slug='bool',
|
||||
help_text='The bool')
|
||||
val = EavValue.objects.create(entity=self.entity,
|
||||
val = Value.objects.create(entity=self.entity,
|
||||
attribute = _bool)
|
||||
value = False
|
||||
val.value = value
|
||||
|
|
@ -141,7 +141,7 @@ class EavBasicTests(TestCase):
|
|||
|
||||
@classmethod
|
||||
def get_eav_attributes(self):
|
||||
return EavAttribute.objects.all()
|
||||
return Attribute.objects.all()
|
||||
|
||||
EavRegistry.register(Patient, PatientEav)
|
||||
|
||||
|
|
@ -159,10 +159,10 @@ class EavBasicTests(TestCase):
|
|||
|
||||
class PatientEav(EavConfig):
|
||||
|
||||
proxy_field_name = 'my_eav'
|
||||
manager_field_name ='my_objects'
|
||||
generic_relation_field_name = 'my_eav_values'
|
||||
generic_relation_field_related_name = "patient"
|
||||
eav_attr = 'my_eav'
|
||||
manager_attr ='my_objects'
|
||||
generic_relation_attr = 'my_eav_values'
|
||||
generic_relation_related_name = "patient"
|
||||
|
||||
EavRegistry.register(Patient, PatientEav)
|
||||
|
||||
|
|
@ -174,8 +174,8 @@ class EavBasicTests(TestCase):
|
|||
|
||||
p2.my_eav.city = "Mbrarare"
|
||||
p2.save()
|
||||
value = EavValue.objects.get(value_text='Mbrarare')
|
||||
name = PatientEav.generic_relation_field_related_name
|
||||
value = Value.objects.get(value_text='Mbrarare')
|
||||
name = PatientEav.generic_relation_related_name
|
||||
self.assertTrue(value, name)
|
||||
|
||||
bak_registered_manager = Patient.objects
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@ class EavFilterTests(TestCase):
|
|||
EavRegistry.unregister(Patient)
|
||||
EavRegistry.register(Patient)
|
||||
|
||||
self.attribute = EavAttribute.objects\
|
||||
.create(datatype=EavAttribute.TYPE_TEXT,
|
||||
self.attribute = Attribute.objects\
|
||||
.create(datatype=Attribute.TYPE_TEXT,
|
||||
name='City', slug='city')
|
||||
|
||||
self.patient = Patient.objects.create(name="Doe")
|
||||
|
||||
self.value = EavValue.objects.create(entity=self.patient,
|
||||
self.value = Value.objects.create(entity=self.patient,
|
||||
attribute=self.attribute,
|
||||
value_text='Denver')
|
||||
|
||||
|
|
@ -38,15 +38,15 @@ class EavFilterTests(TestCase):
|
|||
|
||||
def additional_attribute_setup(self):
|
||||
|
||||
self.country_attr = EavAttribute.objects\
|
||||
.create(datatype=EavAttribute.TYPE_TEXT,
|
||||
self.country_attr = Attribute.objects\
|
||||
.create(datatype=Attribute.TYPE_TEXT,
|
||||
name='Country', slug='country')
|
||||
|
||||
class PatientEav(EavConfig):
|
||||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
return EavAttribute.objects.filter(slug='country')
|
||||
return Attribute.objects.filter(slug='country')
|
||||
|
||||
self.PatientEav = PatientEav
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ class EavFilterTests(TestCase):
|
|||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
return EavAttribute.objects.all()
|
||||
return Attribute.objects.all()
|
||||
|
||||
self.UserEav = UserEav
|
||||
EavRegistry.register(User, UserEav)
|
||||
|
|
@ -71,7 +71,7 @@ class EavFilterTests(TestCase):
|
|||
# self.patient.save()
|
||||
|
||||
|
||||
#print EavEntity.objects.filter(eav__city='Paris')
|
||||
#print Entity.objects.filter(eav__city='Paris')
|
||||
|
||||
|
||||
def test_you_can_filter_entity_by_attribute_values(self):
|
||||
|
|
@ -168,12 +168,12 @@ class EavFilterTests(TestCase):
|
|||
|
||||
def test_you_can_filter_entity_by_q_objects_with_lookups(self):
|
||||
class UserEav(EavConfig):
|
||||
manager_field_name = 'eav_objects'
|
||||
manager_attr = 'eav_objects'
|
||||
EavRegistry.register(User, UserEav)
|
||||
|
||||
EavAttribute.objects.create(datatype=EavAttribute.TYPE_INT,
|
||||
Attribute.objects.create(datatype=Attribute.TYPE_INT,
|
||||
name='Height')
|
||||
EavAttribute.objects.create(datatype=EavAttribute.TYPE_FLOAT,
|
||||
Attribute.objects.create(datatype=Attribute.TYPE_FLOAT,
|
||||
name='Weight')
|
||||
u = User.objects.create(username='Bob')
|
||||
u.eav.height = 10
|
||||
|
|
@ -213,12 +213,12 @@ class EavFilterTests(TestCase):
|
|||
'''
|
||||
This test demonstrates a few EAV queries that are known to be broken.
|
||||
it currently fails.
|
||||
''''
|
||||
'''
|
||||
EavRegistry.register(User)
|
||||
|
||||
EavAttribute.objects.create(datatype=EavAttribute.TYPE_INT,
|
||||
Attribute.objects.create(datatype=Attribute.TYPE_INT,
|
||||
name='Height')
|
||||
EavAttribute.objects.create(datatype=EavAttribute.TYPE_FLOAT,
|
||||
Attribute.objects.create(datatype=Attribute.TYPE_FLOAT,
|
||||
name='Weight')
|
||||
u = User.objects.create(username='Bob')
|
||||
u.eav.height = 10
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ class EavSetterAndGetterTests(TestCase):
|
|||
EavRegistry.unregister(Patient)
|
||||
EavRegistry.register(Patient)
|
||||
|
||||
self.attribute = EavAttribute.objects\
|
||||
.create(datatype=EavAttribute.TYPE_TEXT,
|
||||
self.attribute = Attribute.objects\
|
||||
.create(datatype=Attribute.TYPE_TEXT,
|
||||
name='City', slug='city')
|
||||
|
||||
self.patient = Patient.objects.create(name="Doe")
|
||||
|
||||
self.value = EavValue.objects.create(entity=self.patient,
|
||||
self.value = Value.objects.create(entity=self.patient,
|
||||
attribute=self.attribute,
|
||||
value_text='Denver')
|
||||
|
||||
|
|
@ -37,15 +37,15 @@ class EavSetterAndGetterTests(TestCase):
|
|||
|
||||
def additional_attribute_setup(self):
|
||||
|
||||
self.country_attr = EavAttribute.objects\
|
||||
.create(datatype=EavAttribute.TYPE_TEXT,
|
||||
self.country_attr = Attribute.objects\
|
||||
.create(datatype=Attribute.TYPE_TEXT,
|
||||
name='Country', slug='country')
|
||||
|
||||
class PatientEav(EavConfig):
|
||||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
return EavAttribute.objects.filter(slug='country')
|
||||
return Attribute.objects.filter(slug='country')
|
||||
|
||||
self.PatientEav = PatientEav
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ class EavSetterAndGetterTests(TestCase):
|
|||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
return EavAttribute.objects.filter(slug='city')
|
||||
return Attribute.objects.filter(slug='city')
|
||||
|
||||
self.UserEav = UserEav
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ class EavSetterAndGetterTests(TestCase):
|
|||
|
||||
def test_save_single_value(self):
|
||||
patient = Patient.objects.create(name="x")
|
||||
attr = EavAttribute.objects.create(datatype=EavAttribute.TYPE_TEXT,
|
||||
attr = Attribute.objects.create(datatype=Attribute.TYPE_TEXT,
|
||||
name='a', slug='a')
|
||||
# does nothing
|
||||
attr._save_single_value(patient)
|
||||
|
|
@ -96,7 +96,7 @@ class EavSetterAndGetterTests(TestCase):
|
|||
patient.save()
|
||||
|
||||
self.assertEqual(patient.eav.city, 'Paris')
|
||||
self.assertEqual(EavValue.objects.filter(value_text='Paris').count(), 1)
|
||||
self.assertEqual(Value.objects.filter(value_text='Paris').count(), 1)
|
||||
|
||||
|
||||
def test_you_can_assign_a_value_to_a_saved_object(self):
|
||||
|
|
@ -106,7 +106,7 @@ class EavSetterAndGetterTests(TestCase):
|
|||
patient.save()
|
||||
|
||||
self.assertEqual(patient.eav.city, 'Paris')
|
||||
self.assertEqual(EavValue.objects.filter(value_text='Paris').count(), 1)
|
||||
self.assertEqual(Value.objects.filter(value_text='Paris').count(), 1)
|
||||
|
||||
|
||||
def test_underscore_prevent_a_data_from_been_saved(self):
|
||||
|
|
@ -121,24 +121,24 @@ class EavSetterAndGetterTests(TestCase):
|
|||
self.fail()
|
||||
except AttributeError:
|
||||
pass
|
||||
self.assertFalse(EavValue.objects.filter(value_text='Paris').count())
|
||||
self.assertFalse(Value.objects.filter(value_text='Paris').count())
|
||||
|
||||
|
||||
def test_you_can_create_several_type_of_attributes(self):
|
||||
|
||||
self.patient = Patient(name='test')
|
||||
|
||||
EavAttribute.objects.create(datatype=EavAttribute.TYPE_TEXT,
|
||||
Attribute.objects.create(datatype=Attribute.TYPE_TEXT,
|
||||
name='text', slug='text')
|
||||
EavAttribute.objects.create(datatype=EavAttribute.TYPE_FLOAT,
|
||||
Attribute.objects.create(datatype=Attribute.TYPE_FLOAT,
|
||||
name='float', slug='float')
|
||||
EavAttribute.objects.create(datatype=EavAttribute.TYPE_INT,
|
||||
Attribute.objects.create(datatype=Attribute.TYPE_INT,
|
||||
name='int', slug='int')
|
||||
EavAttribute.objects.create(datatype=EavAttribute.TYPE_DATE,
|
||||
Attribute.objects.create(datatype=Attribute.TYPE_DATE,
|
||||
name='date', slug='date')
|
||||
EavAttribute.objects.create(datatype=EavAttribute.TYPE_BOOLEAN,
|
||||
Attribute.objects.create(datatype=Attribute.TYPE_BOOLEAN,
|
||||
name='bool', slug='bool')
|
||||
EavAttribute.objects.create(datatype=EavAttribute.TYPE_OBJECT,
|
||||
Attribute.objects.create(datatype=Attribute.TYPE_OBJECT,
|
||||
name='object', slug='object')
|
||||
|
||||
now = datetime.today()
|
||||
|
|
@ -164,7 +164,7 @@ class EavSetterAndGetterTests(TestCase):
|
|||
|
||||
self.patient.eav.no_an_attribute = 'Woot'
|
||||
self.patient.save()
|
||||
self.assertFalse(EavValue.objects.filter(value_text='Paris').count())
|
||||
self.assertFalse(Value.objects.filter(value_text='Paris').count())
|
||||
|
||||
|
||||
def test_get_a_value_that_does_not_exists(self):
|
||||
|
|
@ -178,36 +178,6 @@ class EavSetterAndGetterTests(TestCase):
|
|||
self.fail()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
def test_attributes_can_be_labelled(self):
|
||||
|
||||
attribute = EavAttribute.objects\
|
||||
.create(datatype=EavAttribute.TYPE_TEXT,
|
||||
name='Country', slug='country')
|
||||
|
||||
# add labels
|
||||
self.attribute.add_label('a')
|
||||
self.attribute.add_label('c')
|
||||
attribute.add_label('b')
|
||||
attribute.add_label('c')
|
||||
|
||||
self.assertEqual(EavAttribute.objects.get(labels__name='a').name,
|
||||
'City')
|
||||
self.assertEqual(EavAttribute.objects.get(labels__name='b').name,
|
||||
'Country')
|
||||
|
||||
# cross labels
|
||||
self.assertEqual(EavAttribute.objects.filter(labels__name='c').count(),
|
||||
2)
|
||||
|
||||
# remove labels
|
||||
self.attribute.remove_label('a')
|
||||
self.assertFalse(EavAttribute.objects.filter(labels__name='a').count())
|
||||
# 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):
|
||||
|
|
@ -219,10 +189,10 @@ class EavSetterAndGetterTests(TestCase):
|
|||
EavRegistry.register(User, self.UserEav)
|
||||
|
||||
self.assertEqual(list(Patient.eav.get_eav_attributes()),
|
||||
list(EavAttribute.objects.filter(slug='country')))
|
||||
list(Attribute.objects.filter(slug='country')))
|
||||
|
||||
self.assertEqual(list(User.eav.get_eav_attributes()),
|
||||
list(EavAttribute.objects.filter(slug='city')))
|
||||
list(Attribute.objects.filter(slug='city')))
|
||||
|
||||
|
||||
def test_can_filter_attribute_availability_for_entity(self):
|
||||
|
|
@ -285,7 +255,7 @@ class EavSetterAndGetterTests(TestCase):
|
|||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
return EavAttribute.objects.filter(slug='country')
|
||||
return Attribute.objects.filter(slug='country')
|
||||
|
||||
EavRegistry.register(Patient, SubPatientEav)
|
||||
|
||||
|
|
@ -333,10 +303,10 @@ class EavSetterAndGetterTests(TestCase):
|
|||
EavRegistry.register(Patient, self.PatientEav)
|
||||
|
||||
assert list(self.PatientEav.get_eav_attributes_for_model(Patient))\
|
||||
== list(EavEntity.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(EavAttribute.objects.filter(slug='country'))
|
||||
== list(Attribute.objects.filter(slug='country'))
|
||||
|
||||
|
||||
def test_values(self):
|
||||
|
|
@ -346,13 +316,13 @@ class EavSetterAndGetterTests(TestCase):
|
|||
self.patient.save()
|
||||
|
||||
self.assertEqual(list(self.patient.eav.get_values()),
|
||||
list(EavValue.objects.exclude(value_text='Denver')))
|
||||
list(Value.objects.exclude(value_text='Denver')))
|
||||
|
||||
|
||||
def test_get_all_attribute_slugs_for_model(self):
|
||||
|
||||
self.country_attr = EavAttribute.objects\
|
||||
.create(datatype=EavAttribute.TYPE_TEXT,
|
||||
self.country_attr = Attribute.objects\
|
||||
.create(datatype=Attribute.TYPE_TEXT,
|
||||
name='Street', slug='street')
|
||||
|
||||
self.additional_attribute_setup()
|
||||
|
|
@ -361,17 +331,17 @@ class EavSetterAndGetterTests(TestCase):
|
|||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
return EavAttribute.objects.exclude(slug='city')
|
||||
return Attribute.objects.exclude(slug='city')
|
||||
|
||||
EavRegistry.register(User, UserEav)
|
||||
|
||||
u = User.objects.create(username='John')
|
||||
|
||||
slugs = dict((s.slug, s) for s in EavAttribute.objects\
|
||||
slugs = dict((s.slug, s) for s in Attribute.objects\
|
||||
.exclude(slug='city'))
|
||||
|
||||
assert slugs\
|
||||
== EavEntity.get_all_attribute_slugs_for_model(User)\
|
||||
== Entity.get_all_attribute_slugs_for_model(User)\
|
||||
== User.eav.get_all_attribute_slugs_for_model(User)\
|
||||
== u.eav.get_all_attribute_slugs()
|
||||
|
||||
|
|
@ -381,7 +351,7 @@ class EavSetterAndGetterTests(TestCase):
|
|||
self.patient.eav.country = 'Kenya'
|
||||
self.patient.eav.save()
|
||||
|
||||
self.assertEqual(list(EavValue.objects.all()),
|
||||
self.assertEqual(list(Value.objects.all()),
|
||||
list(Patient.objects.get(pk=self.patient.pk).eav))
|
||||
|
||||
|
||||
|
|
|
|||
56
utils.py
56
utils.py
|
|
@ -21,17 +21,17 @@ from django.contrib.contenttypes import generic
|
|||
from django.db.utils import DatabaseError
|
||||
from django.db.models.signals import post_init, post_save, post_delete, pre_init
|
||||
from .managers import EntityManager
|
||||
from .models import (EavEntity, EavAttribute, EavValue,
|
||||
from .models import (Entity, Attribute, Value,
|
||||
get_unique_class_identifier)
|
||||
|
||||
|
||||
#todo : rename this file in registry
|
||||
class EavConfig(EavEntity):
|
||||
class EavConfig(Entity):
|
||||
|
||||
proxy_field_name = 'eav'
|
||||
manager_field_name ='objects'
|
||||
generic_relation_field_name = 'eav_values'
|
||||
generic_relation_field_related_name = None
|
||||
eav_attr = 'eav'
|
||||
manager_attr ='objects'
|
||||
generic_relation_attr = 'eav_values'
|
||||
generic_relation_related_name = None
|
||||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
|
|
@ -39,7 +39,7 @@ class EavConfig(EavEntity):
|
|||
By default, all attributes apply to an entity,
|
||||
unless otherwise specified.
|
||||
"""
|
||||
return EavAttribute.objects.all()
|
||||
return Attribute.objects.all()
|
||||
|
||||
|
||||
class EavRegistry(object):
|
||||
|
|
@ -69,7 +69,7 @@ class EavRegistry(object):
|
|||
instance = kwargs['instance']
|
||||
config_cls = EavRegistry.get_config_cls_for_model(sender)
|
||||
|
||||
setattr(instance, config_cls.proxy_field_name, EavEntity(instance))
|
||||
setattr(instance, config_cls.eav_attr, Entity(instance))
|
||||
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -79,7 +79,7 @@ class EavRegistry(object):
|
|||
create an attribute.
|
||||
"""
|
||||
for cache in EavRegistry.cache.itervalues():
|
||||
EavEntity.update_attr_cache_for_model(cache['model_cls'])
|
||||
Entity.update_attr_cache_for_model(cache['model_cls'])
|
||||
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -112,7 +112,7 @@ class EavRegistry(object):
|
|||
# we want to call attach and save handler on instance creation and
|
||||
# saving
|
||||
post_init.connect(EavRegistry.attach, sender=model_cls)
|
||||
post_save.connect(EavEntity.save_handler, sender=model_cls)
|
||||
post_save.connect(Entity.save_handler, sender=model_cls)
|
||||
|
||||
# todo: rename cache in data
|
||||
EavRegistry.cache[cls_id] = { 'config_cls': config_cls,
|
||||
|
|
@ -121,38 +121,38 @@ class EavRegistry(object):
|
|||
|
||||
# save the old manager if the attribute name conflict with the new
|
||||
# one
|
||||
if hasattr(model_cls, config_cls.manager_field_name):
|
||||
mgr = getattr(model_cls, config_cls.manager_field_name)
|
||||
if hasattr(model_cls, config_cls.manager_attr):
|
||||
mgr = getattr(model_cls, config_cls.manager_attr)
|
||||
EavRegistry.cache[cls_id]['old_mgr'] = mgr
|
||||
|
||||
if not manager_only:
|
||||
# set add the config_cls as an attribute of the model
|
||||
# it will allow to perform some operation directly from this model
|
||||
setattr(model_cls, config_cls.proxy_field_name, config_cls)
|
||||
setattr(model_cls, config_cls.eav_attr, config_cls)
|
||||
|
||||
# todo : not useful anymore ?
|
||||
setattr(getattr(model_cls, config_cls.proxy_field_name),
|
||||
setattr(getattr(model_cls, config_cls.eav_attr),
|
||||
'get_eav_attributes', config_cls.get_eav_attributes)
|
||||
|
||||
# attache the new manager to the model
|
||||
mgr = EntityManager()
|
||||
mgr.contribute_to_class(model_cls, config_cls.manager_field_name)
|
||||
mgr.contribute_to_class(model_cls, config_cls.manager_attr)
|
||||
|
||||
if not manager_only:
|
||||
# todo: see with david how to change that
|
||||
try:
|
||||
EavEntity.update_attr_cache_for_model(model_cls)
|
||||
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_field_related_name:
|
||||
rel_name = config_cls.generic_relation_field_related_name
|
||||
if config_cls.generic_relation_related_name:
|
||||
rel_name = config_cls.generic_relation_related_name
|
||||
else:
|
||||
rel_name = model_cls.__name__
|
||||
gr_name = config_cls.generic_relation_field_name.lower()
|
||||
generic_relation = generic.GenericRelation(EavValue,
|
||||
gr_name = config_cls.generic_relation_attr.lower()
|
||||
generic_relation = generic.GenericRelation(Value,
|
||||
object_id_field='entity_id',
|
||||
content_type_field='entity_ct',
|
||||
related_name=rel_name)
|
||||
|
|
@ -174,15 +174,15 @@ class EavRegistry(object):
|
|||
manager_only = cache['manager_only']
|
||||
if not manager_only:
|
||||
post_init.disconnect(EavRegistry.attach, sender=model_cls)
|
||||
post_save.disconnect(EavEntity.save_handler, sender=model_cls)
|
||||
post_save.disconnect(Entity.save_handler, sender=model_cls)
|
||||
|
||||
try:
|
||||
delattr(model_cls, config_cls.manager_field_name)
|
||||
delattr(model_cls, config_cls.manager_attr)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# remove remaining reference to the generic relation
|
||||
gen_rel_field = config_cls.generic_relation_field_name
|
||||
gen_rel_field = config_cls.generic_relation_attr
|
||||
for field in model_cls._meta.local_many_to_many:
|
||||
if field.name == gen_rel_field:
|
||||
model_cls._meta.local_many_to_many.remove(field)
|
||||
|
|
@ -194,15 +194,15 @@ class EavRegistry(object):
|
|||
|
||||
if 'old_mgr' in cache:
|
||||
cache['old_mgr'].contribute_to_class(model_cls,
|
||||
config_cls.manager_field_name)
|
||||
config_cls.manager_attr)
|
||||
|
||||
try:
|
||||
delattr(model_cls, config_cls.proxy_field_name)
|
||||
delattr(model_cls, config_cls.eav_attr)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if not manager_only:
|
||||
EavEntity.flush_attr_cache_for_model(model_cls)
|
||||
Entity.flush_attr_cache_for_model(model_cls)
|
||||
|
||||
EavRegistry.cache.pop(cls_id)
|
||||
|
||||
|
|
@ -210,5 +210,5 @@ class EavRegistry(object):
|
|||
# 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)
|
||||
post_save.connect(EavRegistry.update_attribute_cache, sender=Attribute)
|
||||
post_delete.connect(EavRegistry.update_attribute_cache, sender=Attribute)
|
||||
|
|
|
|||
Loading…
Reference in a new issue