Changed filter_by() to filter() during filtering instances in db API

When we use filter_by() fields for filtering are extracted
from the primary entity of the query, or the last entity
that was the target of a call to Query.join().

If db query contains filters 'project_id' and 'tags' (or 'tags-any')
db error will be raised:

  Entity '<AliasedClass at 0x7fb09ba18b50; Tag>' has no property 'project_id'

It happens because we use join(models.Tag) to filter instances by tags.
Sqlalchemy try to find 'project_id' field in Tag model. To fix this
issue we should use filter() instead of filter_by().
filter() works fine with join().

Closes-Bug: #1516546

Change-Id: Ibcac2a08aa51f698c690399f65c77130ff2de173
This commit is contained in:
Sergey Nikitin
2015-11-16 12:39:36 +03:00
parent 39919b5058
commit 4f75ba6729
2 changed files with 29 additions and 2 deletions

View File

@@ -2252,8 +2252,8 @@ def _exact_instance_filter(query, filters, legal_keys):
# Apply simple exact matches
if filter_dict:
query = query.filter_by(**filter_dict)
query = query.filter(*[getattr(models.Instance, k) == v
for k, v in filter_dict.items()])
return query

View File

@@ -2589,6 +2589,33 @@ class InstanceTestCase(test.TestCase, ModelsObjectComparatorMixin):
ignored_keys=['deleted', 'deleted_at', 'metadata', 'extra',
'system_metadata', 'info_cache', 'pci_devices'])
def test_instance_get_all_by_filters_tags_and_project_id(self):
context1 = context.RequestContext('user1', 'p1')
context2 = context.RequestContext('user2', 'p2')
inst1 = self.create_instance_with_args(context=context1,
project_id='p1')
inst2 = self.create_instance_with_args(context=context1,
project_id='p1')
inst3 = self.create_instance_with_args(context=context2,
project_id='p2')
t1 = u'tag1'
t2 = u'tag2'
t3 = u'tag3'
t4 = u'tag4'
db.instance_tag_set(context1, inst1.uuid, [t1, t2])
db.instance_tag_set(context1, inst2.uuid, [t1, t2, t4])
db.instance_tag_set(context2, inst3.uuid, [t1, t2, t3, t4])
result = db.instance_get_all_by_filters(self.ctxt,
{'tags': [t1, t2],
'tags-any': [t3, t4],
'project_id': 'p1'})
self._assertEqualListsOfObjects([inst2], result,
ignored_keys=['deleted', 'deleted_at', 'metadata', 'extra',
'system_metadata', 'info_cache', 'pci_devices'])
def test_instance_get_all_by_host_and_node_no_join(self):
instance = self.create_instance_with_args()
result = db.instance_get_all_by_host_and_node(self.ctxt, 'h1', 'n1')