mirror of
https://github.com/ansible/awx.git
synced 2026-03-18 17:37:30 -02:30
boolean logic performed on filter results
* Before, the boolean logic operators were performed against the Q() objects, iteratively. Now, boolean logic is done after Host.objects.filter(Q()). This results in the wanted and expected results.
This commit is contained in:
@@ -1827,8 +1827,8 @@ class HostList(ListCreateAPIView):
|
||||
qs = super(HostList, self).get_queryset()
|
||||
filter_string = self.request.query_params.get('host_filter', None)
|
||||
if filter_string:
|
||||
filter_q = DynamicFilter.query_from_string(filter_string)
|
||||
qs = qs.filter(filter_q)
|
||||
filter_qs = DynamicFilter.query_from_string(filter_string)
|
||||
qs &= filter_qs
|
||||
return qs
|
||||
|
||||
def list(self, *args, **kwargs):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
# Python
|
||||
import pytest
|
||||
import mock
|
||||
|
||||
# AWX
|
||||
from awx.main.utils.filters import DynamicFilter
|
||||
@@ -9,6 +10,18 @@ from awx.main.utils.filters import DynamicFilter
|
||||
from django.db.models import Q
|
||||
|
||||
|
||||
class mockObjects:
|
||||
def filter(self, *args, **kwargs):
|
||||
return Q(*args, **kwargs)
|
||||
|
||||
|
||||
class mockHost:
|
||||
def __init__(self):
|
||||
print("Host mock created")
|
||||
self.objects = mockObjects()
|
||||
|
||||
|
||||
@mock.patch('awx.main.utils.filters.get_host_model', return_value=mockHost())
|
||||
class TestDynamicFilterQueryFromString():
|
||||
@pytest.mark.parametrize("filter_string,q_expected", [
|
||||
('facts__facts__blank=""', Q(**{u"facts__facts__blank": u""})),
|
||||
@@ -22,7 +35,7 @@ class TestDynamicFilterQueryFromString():
|
||||
#('"a__b\"__c"="true"', Q(**{u"a__b\"__c": "true"})),
|
||||
#('a__b\"__c="true"', Q(**{u"a__b\"__c": "true"})),
|
||||
])
|
||||
def test_query_generated(self, filter_string, q_expected):
|
||||
def test_query_generated(self, mock_get_host_model, filter_string, q_expected):
|
||||
q = DynamicFilter.query_from_string(filter_string)
|
||||
assert unicode(q) == unicode(q_expected)
|
||||
|
||||
@@ -30,7 +43,7 @@ class TestDynamicFilterQueryFromString():
|
||||
'ansible_facts__facts__facts__blank='
|
||||
'ansible_facts__a__b__c__ space =ggg',
|
||||
])
|
||||
def test_invalid_filter_strings(self, filter_string):
|
||||
def test_invalid_filter_strings(self, mock_get_host_model, filter_string):
|
||||
with pytest.raises(RuntimeError) as e:
|
||||
DynamicFilter.query_from_string(filter_string)
|
||||
assert e.value.message == u"Invalid query " + filter_string
|
||||
@@ -39,7 +52,7 @@ class TestDynamicFilterQueryFromString():
|
||||
(u'(a=abc\u1F5E3def)', Q(**{u"a": u"abc\u1F5E3def"})),
|
||||
(u'(ansible_facts__a=abc\u1F5E3def)', Q(**{u"ansible_facts__contains": {u"a": u"abc\u1F5E3def"}})),
|
||||
])
|
||||
def test_unicode(self, filter_string, q_expected):
|
||||
def test_unicode(self, mock_get_host_model, filter_string, q_expected):
|
||||
q = DynamicFilter.query_from_string(filter_string)
|
||||
assert unicode(q) == unicode(q_expected)
|
||||
|
||||
@@ -53,7 +66,7 @@ class TestDynamicFilterQueryFromString():
|
||||
('(a=b) and not (c=d or (e=f and (g=h or i=j))) or (y=z)', Q(**{u"a": u"b"}) & ~(Q(**{u"c": u"d"}) | (Q(**{u"e": u"f"}) & (Q(**{u"g": u"h"}) | Q(**{u"i": u"j"})))) | Q(**{u"y": u"z"})),
|
||||
('a=b or a=d or a=e or a=z and b=h and b=i and b=j and b=k', Q(**{u"a": u"b"}) | Q(**{u"a": u"d"}) | Q(**{u"a": u"e"}) | Q(**{u"a": u"z"}) & Q(**{u"b": u"h"}) & Q(**{u"b": u"i"}) & Q(**{u"b": u"j"}) & Q(**{u"b": u"k"}))
|
||||
])
|
||||
def test_boolean_parenthesis(self, filter_string, q_expected):
|
||||
def test_boolean_parenthesis(self, mock_get_host_model, filter_string, q_expected):
|
||||
q = DynamicFilter.query_from_string(filter_string)
|
||||
assert unicode(q) == unicode(q_expected)
|
||||
|
||||
@@ -73,7 +86,7 @@ class TestDynamicFilterQueryFromString():
|
||||
#('"a__b\"__c"="true"', Q(**{u"a__b\"__c": "true"})),
|
||||
#('a__b\"__c="true"', Q(**{u"a__b\"__c": "true"})),
|
||||
])
|
||||
def test_contains_query_generated(self, filter_string, q_expected):
|
||||
def test_contains_query_generated(self, mock_get_host_model, filter_string, q_expected):
|
||||
q = DynamicFilter.query_from_string(filter_string)
|
||||
assert unicode(q) == unicode(q_expected)
|
||||
|
||||
@@ -83,7 +96,7 @@ class TestDynamicFilterQueryFromString():
|
||||
#('"a__b\"__c"="true"', Q(**{u"a__b\"__c": "true"})),
|
||||
#('a__b\"__c="true"', Q(**{u"a__b\"__c": "true"})),
|
||||
])
|
||||
def test_contains_query_generated_unicode(self, filter_string, q_expected):
|
||||
def test_contains_query_generated_unicode(self, mock_get_host_model, filter_string, q_expected):
|
||||
q = DynamicFilter.query_from_string(filter_string)
|
||||
assert unicode(q) == unicode(q_expected)
|
||||
|
||||
@@ -91,7 +104,7 @@ class TestDynamicFilterQueryFromString():
|
||||
('ansible_facts__a=null', Q(**{u"ansible_facts__contains": {u"a": u"null"}})),
|
||||
('ansible_facts__c="null"', Q(**{u"ansible_facts__contains": {u"c": u"\"null\""}})),
|
||||
])
|
||||
def test_contains_query_generated_null(self, filter_string, q_expected):
|
||||
def test_contains_query_generated_null(self, mock_get_host_model, filter_string, q_expected):
|
||||
q = DynamicFilter.query_from_string(filter_string)
|
||||
assert unicode(q) == unicode(q_expected)
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@ from pyparsing import (
|
||||
CharsNotIn,
|
||||
)
|
||||
|
||||
from django.db import models
|
||||
|
||||
import django
|
||||
|
||||
__all__ = ['DynamicFilter']
|
||||
|
||||
@@ -32,6 +31,10 @@ def string_to_type(t):
|
||||
return t
|
||||
|
||||
|
||||
def get_host_model():
|
||||
return django.apps.apps.get_model('main', 'host')
|
||||
|
||||
|
||||
class DynamicFilter(object):
|
||||
SEARCHABLE_RELATIONSHIP = 'ansible_facts'
|
||||
|
||||
@@ -41,7 +44,10 @@ class DynamicFilter(object):
|
||||
k, v = self._extract_key_value(t)
|
||||
k, v = self._json_path_to_contains(k, v)
|
||||
kwargs[k] = v
|
||||
self.result = models.Q(**kwargs)
|
||||
|
||||
# Avoid import circular dependency
|
||||
Host = get_host_model()
|
||||
self.result = Host.objects.filter(**kwargs)
|
||||
|
||||
def strip_quotes_traditional_logic(self, v):
|
||||
if type(v) is unicode and v.startswith('"') and v.endswith('"'):
|
||||
|
||||
Reference in New Issue
Block a user