diff --git a/billingstack/sqlalchemy/api.py b/billingstack/sqlalchemy/api.py new file mode 100644 index 0000000..e67147b --- /dev/null +++ b/billingstack/sqlalchemy/api.py @@ -0,0 +1,144 @@ +from sqlalchemy import or_ +from sqlalchemy.orm import exc + +from billingstack import exceptions +from billingstack.sqlalchemy.model_base import ModelBase + + +class HelpersMixin(object): + def _save(self, obj, save=True): + if not save: + return obj + try: + obj.save(self.session) + except exceptions.Duplicate: + raise + + def _list(self, cls=None, query=None, criterion=None): + """ + A generic list method + + :param cls: The model to try to delete + :param criterion: Criterion to match objects with + """ + if not cls and not query: + raise ValueError("Need either cls or query") + + query = query or self.session.query(cls) + + if criterion: + query = query.filter_by(**criterion) + + try: + result = query.all() + except exc.NoResultFound: + LOG.debug('No results found querying for %s: %s' % + (cls, criterion)) + return [] + else: + return result + + def _get(self, cls, identifier, by_name=False): + """ + Get an instance of a Model matching ID + + :param cls: The model to try to get + :param identifier: The ID to get + :param by_name: Search by name as well as ID + """ + filters = [cls.id == identifier] + if by_name: + filters.append(cls.name == identifier) + + query = self.session.query(cls)\ + .filter(or_(*filters)) + + try: + obj = query.one() + except exc.NoResultFound: + raise exceptions.NotFound(identifier) + return obj + + def _get_id_or_name(self, *args, **kw): + """ + Same as _get but with by_name on ass default + """ + kw['by_name'] = True + return self._get(*args, **kw) + + def _update(self, cls, id_, values): + """ + Update an instance of a Model matching an ID with values + + :param cls: The model to try to update + :param id_: The ID to update + :param values: The values to update the model instance with + """ + obj = self._get(cls, id_) + obj.update(values) + try: + obj.save(self.session) + except exceptions.Duplicate: + raise + return obj + + def _delete(self, cls, id_): + """ + Delete an instance of a Model matching an ID + + :param cls: The model to try to delete + :param id_: The ID to delete + """ + obj = self._get(cls, id_) + obj.delete(self.session) + + def _get_row(self, obj, cls=None, **kw): + """ + Used to either check that passed 'obj' is a ModelBase inheriting object + and just return it + + :param obj: ID or instance / ref of the object + :param cls: The class to run self._get on if obj is not a ref + """ + if isinstance(obj, ModelBase): + return obj + elif isinstance(obj, basestring) and cls: + return self._get(cls, obj) + else: + msg = 'Missing obj and/or obj and cls...' + raise exceptions.BadRequest(msg) + + def _make_rel_row(self, row, rel_attr, values): + """ + Get the class of the relation attribute in 'rel_attr' and make a + row from values with it. + + :param row: A instance of ModelBase + :param rel_attr: The relation attribute + :param values: The values to create the new row from + """ + cls = row.__mapper__.get_property(rel_attr).mapper.class_ + return cls(**values) + + def _dict(self, row, extra=[]): + data = dict(row) + for key in extra: + if isinstance(row[key], list): + data[key] = map(dict, row[key]) + else: + data[key] = dict(row[key]) + return data + + def _kv_rows(self, rows, key='name', func=lambda i: i): + """ + Return a Key, Value dict where the "key" will be the key and the row as value + """ + data = {} + for row in rows: + if callable(key): + data_key = key(row) + else: + data_key = row[key] + data[data_key] = func(row) + return data + diff --git a/billingstack/storage/impl_sqlalchemy/__init__.py b/billingstack/storage/impl_sqlalchemy/__init__.py index 35690b6..67505b1 100644 --- a/billingstack/storage/impl_sqlalchemy/__init__.py +++ b/billingstack/storage/impl_sqlalchemy/__init__.py @@ -11,13 +11,12 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from sqlalchemy import or_ from sqlalchemy.orm import exc from oslo.config import cfg from billingstack.openstack.common import log as logging from billingstack import exceptions from billingstack import utils as common_utils -from billingstack.sqlalchemy import utils as db_utils +from billingstack.sqlalchemy import utils as db_utils, api from billingstack.storage import base from billingstack.storage.impl_sqlalchemy import models from billingstack.storage.impl_sqlalchemy.session import get_session, SQLOPTS @@ -39,7 +38,7 @@ class SQLAlchemyStorage(base.StorageEngine): return Connection(self.name) -class Connection(base.Connection): +class Connection(base.Connection, api.HelpersMixin): """ SQLAlchemy connection """ @@ -54,142 +53,6 @@ class Connection(base.Connection): """ Semi-Private Method to reset the database schema """ models.BASE.metadata.drop_all(self.session.bind) - def _save(self, obj, save=True): - if not save: - return obj - try: - obj.save(self.session) - except exceptions.Duplicate: - raise - - def _list(self, cls=None, query=None, criterion=None): - """ - A generic list method - - :param cls: The model to try to delete - :param criterion: Criterion to match objects with - """ - if not cls and not query: - raise ValueError("Need either cls or query") - - query = query or self.session.query(cls) - - if criterion: - query = query.filter_by(**criterion) - - try: - result = query.all() - except exc.NoResultFound: - LOG.debug('No results found querying for %s: %s' % - (cls, criterion)) - return [] - else: - return result - - def _get(self, cls, identifier, by_name=False): - """ - Get an instance of a Model matching ID - - :param cls: The model to try to get - :param identifier: The ID to get - :param by_name: Search by name as well as ID - """ - filters = [cls.id == identifier] - if by_name: - filters.append(cls.name == identifier) - - query = self.session.query(cls)\ - .filter(or_(*filters)) - - try: - obj = query.one() - except exc.NoResultFound: - raise exceptions.NotFound(identifier) - return obj - - def _get_id_or_name(self, *args, **kw): - """ - Same as _get but with by_name on ass default - """ - kw['by_name'] = True - return self._get(*args, **kw) - - def _update(self, cls, id_, values): - """ - Update an instance of a Model matching an ID with values - - :param cls: The model to try to update - :param id_: The ID to update - :param values: The values to update the model instance with - """ - obj = self._get(cls, id_) - obj.update(values) - try: - obj.save(self.session) - except exceptions.Duplicate: - raise - return obj - - def _delete(self, cls, id_): - """ - Delete an instance of a Model matching an ID - - :param cls: The model to try to delete - :param id_: The ID to delete - """ - obj = self._get(cls, id_) - obj.delete(self.session) - - def _get_row(self, obj, cls=None, **kw): - """ - Used to either check that passed 'obj' is a ModelBase inheriting object - and just return it - - :param obj: ID or instance / ref of the object - :param cls: The class to run self._get on if obj is not a ref - """ - if isinstance(obj, models.ModelBase): - return obj - elif isinstance(obj, basestring) and cls: - return self._get(cls, obj) - else: - msg = 'Missing obj and/or obj and cls...' - raise exceptions.BadRequest(msg) - - def _make_rel_row(self, row, rel_attr, values): - """ - Get the class of the relation attribute in 'rel_attr' and make a - row from values with it. - - :param row: A instance of ModelBase - :param rel_attr: The relation attribute - :param values: The values to create the new row from - """ - cls = row.__mapper__.get_property(rel_attr).mapper.class_ - return cls(**values) - - def _dict(self, row, extra=[]): - data = dict(row) - for key in extra: - if isinstance(row[key], list): - data[key] = map(dict, row[key]) - else: - data[key] = dict(row[key]) - return data - - def _kv_rows(self, rows, key='name', func=lambda i: i): - """ - Return a Key, Value dict where the "key" will be the key and the row as value - """ - data = {} - for row in rows: - if callable(key): - data_key = key(row) - else: - data_key = row[key] - data[data_key] = func(row) - return data - def set_properties(self, obj, properties, cls=None, rel_attr='properties', purge=False): """ Set's a dict with key values on a relation on the row