Added documentation and tests

This commit is contained in:
David Gelvin 2010-09-17 12:50:28 +03:00
parent 9a5e3574e1
commit 39e6a21403
2 changed files with 98 additions and 4 deletions

View file

@ -5,16 +5,23 @@ from django.db import models
from .models import EavAttribute, EavValue
def eav_filter(func):
'''
Decorator used to wrap filter and exlclude methods. Passes args through
expand_q_filters and kwargs through expand_eav_filter. Returns the
called function (filter or exclude) .distinct()
'''
@wraps(func)
def wrapper(self, *args, **kwargs):
new_args = []
for arg in args:
if isinstance(arg, models.Q):
# modify Q objects (warning: recursion ahead)
new_args.append(expand_q_filters(arg, self.model))
arg = expand_q_filters(arg, self.model)
new_args.append(arg)
new_kwargs = {}
for key, value in kwargs.items():
# modify kwargs (warning: recursion ahead)
new_key, new_value = expand_eav_filter(self.model, key, value)
new_kwargs.update({new_key: new_value})
@ -23,6 +30,11 @@ def eav_filter(func):
def expand_q_filters(q, root_cls):
'''
Takes a Q object and a model class.
Recursivley passes each filter / value in the Q object tree leaf nodes
through expand_eav_filter
'''
new_children = []
for qi in q.children:
if type(qi) is tuple:
@ -38,6 +50,17 @@ def expand_q_filters(q, root_cls):
def expand_eav_filter(model_cls, key, value):
'''
Accepts a model class and a key, value.
Recurisively replaces any eav filter with a subquery.
For example:
key = 'eav__height'
value = 5
Would return:
key = 'eav_values__in'
value = EavValues.objects.filter(value_int=5, attribute__slug='height')
'''
from .utils import EavRegistry
fields = key.split('__')

View file

@ -161,10 +161,81 @@ class EavFilterTests(TestCase):
self.assertEqual(User.objects.exclude(eav__city='Paris').count(), 2)
#TODO Exclude and EAV Q objects are broken!
#self.assertEqual(User.objects.exclude(Q(eav__city='Paris')).count(), 2)
self.assertEqual(User.objects.filter(Q(eav__city='Paris') & \
Q(username='Bob')).count(), 1)
self.assertEqual(User.objects.filter(Q(eav__city='Paris', username='Jack')).count(), 0)
def test_you_can_filter_entity_by_q_objects_with_lookups(self):
class UserEav(EavConfig):
manager_field_name = 'eav_objects'
EavRegistry.register(User, UserEav)
EavAttribute.objects.create(datatype=EavAttribute.TYPE_INT,
name='Height')
EavAttribute.objects.create(datatype=EavAttribute.TYPE_FLOAT,
name='Weight')
u = User.objects.create(username='Bob')
u.eav.height = 10
u.eav.weight = 20
u.save()
u = User.objects.create(username='Jack')
u.eav.height = 20
u.eav.weight = 10
u.save()
u = User.objects.create(username='Fred')
u.eav.height = 15
u.eav.weight = 15
u.save()
'''
This is what we have now:
Username Hieght Weight
-------- ------ ------
Bob 10 20
Jack 20 10
Fred 15 15
'''
self.assertEqual(User.eav_objects.filter(eav__height__gt=12).count(), 2)
self.assertEqual(User.eav_objects.filter(Q(eav__height__gt=12)).count(), 2)
self.assertEqual(User.eav_objects.filter(eav__height__gte=20).count(), 1)
self.assertEqual(User.eav_objects.filter(Q(eav__height__gte=20)).count(), 1)
self.assertEqual(User.eav_objects.filter(Q(eav__height__gte=20) & Q(username='Fred')).count(), 0)
self.assertEqual(User.eav_objects.filter(Q(eav__height=15) & Q(username='Fred')).count(), 1)
self.assertEqual(User.eav_objects.filter(eav__height=20, eav__weight=10).count(), 1)
self.assertEqual(User.eav_objects.filter(Q(eav__height=20) | Q(eav__weight=10) | Q(eav__weight=15)).count(), 2)
def test_broken_eav_filters(self):
EavRegistry.register(User)
EavAttribute.objects.create(datatype=EavAttribute.TYPE_INT,
name='Height')
EavAttribute.objects.create(datatype=EavAttribute.TYPE_FLOAT,
name='Weight')
u = User.objects.create(username='Bob')
u.eav.height = 10
u.eav.weight = 20
u.eav.city = 'Paris'
u.eav.country = 'France'
u.save()
u = User.objects.create(username='Jack')
u.eav.height = 20
u.eav.weight = 10
u.eav.city = 'New York'
u.eav.country = 'Paris'
u.save()
u = User.objects.create(username='Fred')
u.eav.height = 15
u.eav.weight = 15
u.eav.city = 'Georgetown'
u.eav.country = 'Guyana'
u.save()
self.assertEqual(User.objects.exclude(Q(eav__city='Paris')).count(), 2)
self.assertEqual(User.objects.filter(Q(eav__height=20) & Q(eav__weight=10)).count(), 1)