diff --git a/neutron_lib/db/model_query.py b/neutron_lib/db/model_query.py index 25bee229f..7091d87dd 100644 --- a/neutron_lib/db/model_query.py +++ b/neutron_lib/db/model_query.py @@ -17,7 +17,6 @@ NOTE: This module is a temporary shim until networking projects move to """ from oslo_db.sqlalchemy import utils as sa_utils from sqlalchemy import sql, or_, and_ -from sqlalchemy.ext import associationproxy from neutron_lib._i18n import _ from neutron_lib.api import attributes @@ -26,6 +25,7 @@ from neutron_lib import exceptions as n_exc from neutron_lib.objects import utils as obj_utils from neutron_lib.utils import helpers + # Classes implementing extensions will register hooks into this dictionary # for "augmenting" the "core way" of building a query for retrieving objects # from a model class. Hooks are registered by invoking register_hook(). @@ -180,7 +180,10 @@ def apply_filters(query, model, filters, context=None): if not value: query = query.filter(sql.false()) return query - if isinstance(column, associationproxy.AssociationProxy): + if not hasattr(column, 'in_'): + # NOTE(ralonsoh): since SQLAlchemy==1.3.0, a column is an + # AssociationProxyInstance and inherits in_() method from + # ColumnOperators. # association proxies don't support in_ so we have to # do multiple equals matches query = query.filter( diff --git a/neutron_lib/db/utils.py b/neutron_lib/db/utils.py index 28d6ffd30..28b65b23e 100644 --- a/neutron_lib/db/utils.py +++ b/neutron_lib/db/utils.py @@ -14,7 +14,8 @@ import six from oslo_db import exception as db_exc from oslo_utils import excutils -from sqlalchemy.ext import associationproxy +import sqlalchemy +from sqlalchemy.ext.associationproxy import ASSOCIATION_PROXY from sqlalchemy.orm import exc from sqlalchemy.orm import properties @@ -148,11 +149,12 @@ def filter_non_model_columns(data, model): :returns: A new dict who's keys are columns in model or are association proxies of the model. """ - columns = [c.name for c in model.__table__.columns] - return dict((k, v) for (k, v) in - data.items() if k in columns or - isinstance(getattr(model, k, None), - associationproxy.AssociationProxy)) + mapper = sqlalchemy.inspect(model) + columns = set(c.name for c in mapper.columns) + columns.update(d.value_attr for d in mapper.all_orm_descriptors + if d.extension_type is ASSOCIATION_PROXY) + return dict((k, v) for (k, v) + in data.items() if k in columns) def model_query_scope_is_project(context, model): diff --git a/releasenotes/notes/sqlalchemy-1-3-0-b0a2b15b10ae526f.yaml b/releasenotes/notes/sqlalchemy-1-3-0-b0a2b15b10ae526f.yaml new file mode 100644 index 000000000..16cc2aaac --- /dev/null +++ b/releasenotes/notes/sqlalchemy-1-3-0-b0a2b15b10ae526f.yaml @@ -0,0 +1,7 @@ +--- +other: + - | + Since `commit `_, + an AssociationProxy proxy instance is an AssociationProxyInstance + derivative object. In order to import versions SQLAlchemy>=1.3.x, we need + to handle both implementations.