mirror of
https://github.com/jazzband/django-eav2.git
synced 2026-03-17 06:50:24 +00:00
Merge branch 'master' of github.com:mvpdev/eav
Conflicts: fields.py
This commit is contained in:
commit
c4e0cd715f
5 changed files with 163 additions and 84 deletions
19
fields.py
19
fields.py
|
|
@ -9,11 +9,17 @@ from django.core.exceptions import ValidationError
|
|||
class EavSlugField(models.SlugField):
|
||||
|
||||
def validate(self, value, instance):
|
||||
"""
|
||||
Slugs are used to convert the Python attribute name to a database
|
||||
lookup and vice versa. We need it to be a valid Python identifier.
|
||||
We don't want it to start with a '_', underscore will be used
|
||||
var variables we don't want to be saved in db.
|
||||
"""
|
||||
super(EavSlugField, self).validate(value, instance)
|
||||
slug_regex = r'[a-z]+[a-z0-9_]*'
|
||||
slug_regex = r'[a-z][a-z0-9_]*'
|
||||
if not re.match(slug_regex, value):
|
||||
raise ValidationError(_(u"Must be all lower case, "\
|
||||
u"not start with a number, and contain "\
|
||||
u"start with a letter, and contain "\
|
||||
u"only letters, numbers, or underscores."))
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -24,15 +30,22 @@ class EavSlugField(models.SlugField):
|
|||
name = name.strip().lower()
|
||||
|
||||
# Change spaces to underscores
|
||||
name = re.sub('\s+', '_', name)
|
||||
name = '_'.join(name.split())
|
||||
|
||||
# Remove non alphanumeric characters
|
||||
return re.sub('[^\w]', '', name)
|
||||
|
||||
|
||||
class EavDatatypeField(models.CharField):
|
||||
"""
|
||||
This holds checks for the attributes datatypes.
|
||||
"""
|
||||
|
||||
def validate(self, value, instance):
|
||||
"""
|
||||
We don't want them to be able to change the attribute type
|
||||
once it have been created.
|
||||
"""
|
||||
super(EavDatatypeField, self).validate(value, instance)
|
||||
from .models import EavAttribute
|
||||
if not instance.pk:
|
||||
|
|
|
|||
30
models.py
30
models.py
|
|
@ -197,6 +197,7 @@ class EavValue(models.Model):
|
|||
super(EavValue, self).save(*args, **kwargs)
|
||||
|
||||
|
||||
# todo: do it in a faster way (one update)
|
||||
def _blank(self):
|
||||
"""
|
||||
Set all the field to none
|
||||
|
|
@ -232,19 +233,19 @@ class EavEntity(object):
|
|||
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 = attribute.get_value_for_entity(self.model)
|
||||
if value:
|
||||
return attribute.get_value_for_entity(self.model).value
|
||||
value_obj = attribute.get_value_for_entity(self.model)
|
||||
if value_obj:
|
||||
return value_obj.value
|
||||
return None
|
||||
|
||||
raise AttributeError(_(u"%s EAV does not have attribute " \
|
||||
u"named \"%s\".") % \
|
||||
(self.model._meta.object_name, name))
|
||||
return object.__getattr__(self, name)
|
||||
|
||||
|
||||
@classmethod
|
||||
|
|
@ -337,8 +338,6 @@ class EavEntity(object):
|
|||
|
||||
return cache['attributes']
|
||||
|
||||
|
||||
|
||||
|
||||
def get_values(self):
|
||||
return EavValue.objects.filter(entity_ct=self.ct,
|
||||
|
|
@ -356,6 +355,7 @@ class EavEntity(object):
|
|||
|
||||
return cache['slug_mapping']
|
||||
|
||||
|
||||
def get_all_attribute_slugs(self):
|
||||
"""
|
||||
Returns all attributes slugs for the entity
|
||||
|
|
@ -381,18 +381,10 @@ class EavEntity(object):
|
|||
return self.__class__.get_attribute_by_slug_for_model(m_cls, slug)
|
||||
|
||||
|
||||
def get_attribute_by_id(self, attribute_id):
|
||||
"""
|
||||
Returns an attribute object knowing the pk for the entity
|
||||
class linked to the current instance.
|
||||
"""
|
||||
for attr in self.get_all_attributes():
|
||||
if attr.pk == attribute_id:
|
||||
return attr
|
||||
|
||||
|
||||
def __iter__(self):
|
||||
"Iterates over non-empty EAV attributes. Normal fields are not included."
|
||||
"""
|
||||
Iterates over non-empty EAV attributes. Normal fields are not included.
|
||||
"""
|
||||
return iter(self.get_values())
|
||||
|
||||
|
||||
|
|
|
|||
10
tests/cache.py
Executable file
10
tests/cache.py
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding= UTF-8 -*-
|
||||
|
||||
__author__ = 'Isatis39871'
|
||||
__date__ = ''
|
||||
__version__ = ''
|
||||
__all__ = []
|
||||
|
||||
import os, sys
|
||||
from optparse import OptionParser
|
||||
|
|
@ -29,10 +29,34 @@ class EavSetterAndGetterTests(TestCase):
|
|||
self.value = EavValue.objects.create(entity=self.patient,
|
||||
attribute=self.attribute,
|
||||
value_text='Denver')
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
EavRegistry.unregister(Patient)
|
||||
EavRegistry.unregister(User)
|
||||
|
||||
|
||||
def additional_attribute_setup(self):
|
||||
|
||||
self.country_attr = EavAttribute.objects\
|
||||
.create(datatype=EavAttribute.TYPE_TEXT,
|
||||
name='Country', slug='country')
|
||||
|
||||
class PatientEav(EavConfig):
|
||||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
return EavAttribute.objects.filter(slug='country')
|
||||
|
||||
self.PatientEav = PatientEav
|
||||
|
||||
class UserEav(EavConfig):
|
||||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
return EavAttribute.objects.filter(slug='city')
|
||||
|
||||
self.UserEav = UserEav
|
||||
|
||||
|
||||
def test_get_value_to_entity(self):
|
||||
self.assertEqual(self.attribute.get_value_for_entity(self.patient),
|
||||
|
|
@ -83,7 +107,22 @@ class EavSetterAndGetterTests(TestCase):
|
|||
|
||||
self.assertEqual(patient.eav.city, 'Paris')
|
||||
self.assertEqual(EavValue.objects.filter(value_text='Paris').count(), 1)
|
||||
|
||||
|
||||
def test_underscore_prevent_a_data_from_been_saved(self):
|
||||
|
||||
patient = Patient.objects.create(name='new_patient')
|
||||
patient.eav._test = 'Paris'
|
||||
patient.save()
|
||||
|
||||
patient = Patient.objects.get(name='new_patient')
|
||||
try:
|
||||
patient.eav._test
|
||||
self.fail()
|
||||
except AttributeError:
|
||||
pass
|
||||
self.assertFalse(EavValue.objects.filter(value_text='Paris').count())
|
||||
|
||||
|
||||
def test_you_can_create_several_type_of_attributes(self):
|
||||
|
||||
|
|
@ -128,10 +167,18 @@ class EavSetterAndGetterTests(TestCase):
|
|||
self.assertFalse(EavValue.objects.filter(value_text='Paris').count())
|
||||
|
||||
|
||||
def test_get_a_value_that_does_not_exists_returns_none(self):
|
||||
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
|
||||
|
||||
|
||||
def test_attributes_can_be_labelled(self):
|
||||
|
||||
|
|
@ -160,29 +207,16 @@ 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')
|
||||
|
||||
|
||||
self.additional_attribute_setup()
|
||||
|
||||
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)
|
||||
EavRegistry.register(Patient, self.PatientEav)
|
||||
EavRegistry.register(User, self.UserEav)
|
||||
|
||||
self.assertEqual(list(Patient.eav.get_eav_attributes()),
|
||||
list(EavAttribute.objects.filter(slug='country')))
|
||||
|
|
@ -192,23 +226,16 @@ class EavSetterAndGetterTests(TestCase):
|
|||
|
||||
|
||||
def test_can_filter_attribute_availability_for_entity(self):
|
||||
attribute = EavAttribute.objects\
|
||||
.create(datatype=EavAttribute.TYPE_TEXT,
|
||||
name='Country', slug='country')
|
||||
|
||||
self.additional_attribute_setup()
|
||||
|
||||
self.patient.eav.city = 'Tunis'
|
||||
self.patient.save()
|
||||
self.assertEqual(Patient.objects.get(pk=self.patient.pk).eav.city,
|
||||
'Tunis')
|
||||
|
||||
EavRegistry.unregister(Patient)
|
||||
|
||||
class PatientEav(EavConfig):
|
||||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
return EavAttribute.objects.filter(slug='country')
|
||||
|
||||
EavRegistry.register(Patient, PatientEav)
|
||||
EavRegistry.register(Patient, self.PatientEav)
|
||||
|
||||
p = Patient.objects.create(name='Patrick')
|
||||
p.eav.city = 'Paris'
|
||||
|
|
@ -222,27 +249,11 @@ class EavSetterAndGetterTests(TestCase):
|
|||
|
||||
def test_can_have_differente_attribute_filter(self):
|
||||
|
||||
|
||||
attribute = EavAttribute.objects\
|
||||
.create(datatype=EavAttribute.TYPE_TEXT,
|
||||
name='Country', slug='country')
|
||||
self.additional_attribute_setup()
|
||||
|
||||
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)
|
||||
EavRegistry.register(Patient, self.PatientEav)
|
||||
EavRegistry.register(User, self.UserEav)
|
||||
|
||||
p = Patient.objects.create(name='Patrick')
|
||||
u = User.objects.create(username='John')
|
||||
|
|
@ -266,19 +277,11 @@ class EavSetterAndGetterTests(TestCase):
|
|||
|
||||
def test_can_have_a_subclass_for_config_class(self):
|
||||
|
||||
attribute = EavAttribute.objects\
|
||||
.create(datatype=EavAttribute.TYPE_TEXT,
|
||||
name='Country', slug='country')
|
||||
self.additional_attribute_setup()
|
||||
|
||||
EavRegistry.unregister(Patient)
|
||||
|
||||
class PatientEav(EavConfig):
|
||||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
return EavAttribute.objects.filter(slug='country')
|
||||
|
||||
class SubPatientEav(PatientEav):
|
||||
class SubPatientEav(self.PatientEav):
|
||||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
|
|
@ -286,7 +289,6 @@ class EavSetterAndGetterTests(TestCase):
|
|||
|
||||
EavRegistry.register(Patient, SubPatientEav)
|
||||
|
||||
|
||||
self.patient.eav.city = 'Paris'
|
||||
self.patient.eav.country = 'USA'
|
||||
self.patient.save()
|
||||
|
|
@ -314,6 +316,7 @@ class EavSetterAndGetterTests(TestCase):
|
|||
self.assertEqual(self.value._get_value(), 'Denver')
|
||||
self.assertEqual(self.value.value, self.value._get_value())
|
||||
|
||||
|
||||
def test_set_value_store_the_python_object_and_blank_other_fields(self):
|
||||
|
||||
self.value._set_value('Bamako')
|
||||
|
|
@ -324,3 +327,64 @@ class EavSetterAndGetterTests(TestCase):
|
|||
self.assertEqual(self.value.value_date, None)
|
||||
self.assertEqual(self.value.value_bool, False)
|
||||
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(EavEntity.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'))
|
||||
|
||||
|
||||
def test_values(self):
|
||||
self.additional_attribute_setup()
|
||||
self.patient.eav.city = 'Beijin'
|
||||
self.patient.eav.coutry = 'China'
|
||||
self.patient.save()
|
||||
|
||||
self.assertEqual(list(self.patient.eav.get_values()),
|
||||
list(EavValue.objects.exclude(value_text='Denver')))
|
||||
|
||||
|
||||
def test_get_all_attribute_slugs_for_model(self):
|
||||
|
||||
self.country_attr = EavAttribute.objects\
|
||||
.create(datatype=EavAttribute.TYPE_TEXT,
|
||||
name='Street', slug='street')
|
||||
|
||||
self.additional_attribute_setup()
|
||||
|
||||
class UserEav(EavConfig):
|
||||
|
||||
@classmethod
|
||||
def get_eav_attributes(cls):
|
||||
return EavAttribute.objects.exclude(slug='city')
|
||||
|
||||
EavRegistry.register(User, UserEav)
|
||||
|
||||
u = User.objects.create(username='John')
|
||||
|
||||
slugs = dict((s.slug, s) for s in EavAttribute.objects\
|
||||
.exclude(slug='city'))
|
||||
|
||||
assert slugs\
|
||||
== EavEntity.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()
|
||||
self.patient.eav.country = 'Kenya'
|
||||
self.patient.eav.save()
|
||||
|
||||
self.assertEqual(list(EavValue.objects.all()),
|
||||
list(Patient.objects.get(pk=self.patient.pk).eav))
|
||||
|
||||
|
||||
|
|
|
|||
0
tests/slug.py
Normal file
0
tests/slug.py
Normal file
Loading…
Reference in a new issue