Add ability to assign enum values as strings (#46)

* Add ability to assign enum values as strings
* Remove unreachable code (enum validation now fails earlier)
* Enable filtering by enum value
This commit is contained in:
Alexander Anikeev 2019-06-20 23:14:19 +07:00 committed by Siegmeyer
parent 27cf385f58
commit e857819fce
7 changed files with 44 additions and 25 deletions

View file

@ -210,9 +210,10 @@ foreign-keys:
# Of course, you can mix them with regular queries:
Part.objects.filter(name='Cog', eav__height=7.8)
# Querying enums looks as follows:
# Querying enums works either by enum instance or by it's text representation as follows:
yes = EnumValue.objects.get(name='Yes')
Part.objects.filter(eav__is_available=yes)
Part.objects.filter(eav__is_available=yes) # via EnumValue
Part.objects.filter(eav__is_available='yes) # via EnumValue's value
You can use ``Q`` expressions too:

View file

@ -19,9 +19,17 @@ from django.db.models.base import ModelBase
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from .validators import (
validate_text,
validate_float,
validate_int,
validate_date,
validate_bool,
validate_object,
validate_enum
)
from .exceptions import IllegalAssignmentException
from .fields import EavDatatypeField, EavSlugField
from .validators import *
from . import register
@ -245,7 +253,9 @@ class Attribute(models.Model):
validator(value)
if self.datatype == self.TYPE_ENUM:
if value not in self.enum_group.values.all():
if isinstance(value, EnumValue):
value = value.value
if not self.enum_group.values.filter(value=value).exists():
raise ValidationError(
_('%(val)s is not a valid choice for %(attr)s')
% dict(val = value, attr = self)
@ -405,18 +415,6 @@ class Value(models.Model):
self.full_clean()
super(Value, self).save(*args, **kwargs)
def clean(self):
"""
Raises ``ValidationError`` if this value's attribute is *TYPE_ENUM*
and value_enum is not a valid choice for this value's attribute.
"""
if self.attribute.datatype == Attribute.TYPE_ENUM and self.value_enum:
if self.value_enum not in self.attribute.enum_group.values.all():
raise ValidationError(
_('%(enum)s is not a valid choice for %(attr)s')
% dict(enum = self.value_enum, attr = self.attribute)
)
def _get_value(self):
"""
Return the python object this value is holding
@ -530,6 +528,8 @@ class Entity(object):
for attribute in self.get_all_attributes():
if self._hasattr(attribute.slug):
attribute_value = self._getattr(attribute.slug)
if attribute.datatype == Attribute.TYPE_ENUM and not isinstance(attribute_value, EnumValue):
attribute_value = EnumValue.objects.get(value=attribute_value)
attribute.save_value(self.instance, attribute_value)
def validate_attributes(self):

View file

@ -28,7 +28,7 @@ from django.db.models import Case, IntegerField, Q, When
from django.db.models.query import QuerySet
from django.db.utils import NotSupportedError
from .models import Attribute, Value
from .models import Attribute, Value, EnumValue
def is_eav_and_leaf(expr, gr_name):
@ -234,7 +234,10 @@ def expand_eav_filter(model_cls, key, value):
gr_name = config_cls.generic_relation_attr
datatype = Attribute.objects.get(slug=slug).datatype
lookup = '__%s' % fields[2] if len(fields) > 2 else ''
if datatype == Attribute.TYPE_ENUM and not isinstance(value, EnumValue):
lookup = '__value__{}'.format(fields[2]) if len(fields) > 2 else '__value'
else:
lookup = '__{}'.format(fields[2]) if len(fields) > 2 else ''
kwargs = {
'value_{}{}'.format(datatype, lookup): value,
'attribute__slug': slug

View file

@ -81,8 +81,5 @@ def validate_enum(value):
"""
from .models import EnumValue
if not isinstance(value, EnumValue):
raise ValidationError(_(u"Must be an EnumValue model object instance"))
if not value.pk:
if isinstance(value, EnumValue) and not value.pk:
raise ValidationError(_(u"EnumValue has not been saved yet"))

View file

@ -155,8 +155,6 @@ class DataValidation(TestCase):
self.assertRaises(ValidationError, p.save)
p.eav.fever = object
self.assertRaises(ValidationError, p.save)
p.eav.fever = 'yes'
self.assertRaises(ValidationError, p.save)
p.eav.fever = green
self.assertRaises(ValidationError, p.save)
p.eav.fever = EnumValue(value='yes')

View file

@ -1,6 +1,6 @@
from django.test import TestCase
from eav.models import EnumGroup, Attribute, Value
from eav.models import EnumGroup, Attribute, Value, EnumValue
import eav
from .models import Patient
@ -25,3 +25,17 @@ class MiscModels(TestCase):
p.eav.age = None
p.save()
self.assertEqual(Value.objects.count(), 0)
def test_string_enum_value_assignment(self):
yes = EnumValue.objects.create(value='yes')
no = EnumValue.objects.create(value='no')
ynu = EnumGroup.objects.create(name='Yes / No / Unknown')
ynu.values.add(yes)
ynu.values.add(no)
Attribute.objects.create(name='is_patient', datatype=Attribute.TYPE_ENUM, enum_group=ynu)
eav.register(Patient)
p = Patient.objects.create(name='Joe')
p.eav.is_patient = 'yes'
p.save()
p = Patient.objects.get(name='Joe') # get from DB again
self.assertEqual(p.eav.is_patient, yes)

View file

@ -94,6 +94,12 @@ class Queries(TestCase):
p = Patient.objects.filter(q2 & q1)
self.assertEqual(p.count(), 2)
# Anne
q1 = Q(eav__city__contains='Y') & Q(eav__fever='no')
q2 = Q(eav__age=3)
p = Patient.objects.filter(q1 & q2)
self.assertEqual(p.count(), 1)
# Anne
q1 = Q(eav__city__contains='Y') & Q(eav__fever=self.no)
q2 = Q(eav__age=3)