diff --git a/CHANGES.rst b/CHANGES.rst index 21db1cf..2da3d69 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,12 @@ Changelog Here you can see the full list of changes between each SQLAlchemy-Utils release. +0.26.7 (2014-07-29) +^^^^^^^^^^^^^^^^^^^ + +- Made sort_query support hybrid properties where function name != property name + + 0.26.6 (2014-07-22) ^^^^^^^^^^^^^^^^^^^ diff --git a/setup.py b/setup.py index 102ce87..9fecfe7 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ for name, requirements in extras_require.items(): setup( name='SQLAlchemy-Utils', - version='0.26.6', + version='0.26.7', url='https://github.com/kvesteri/sqlalchemy-utils', license='BSD', author='Konsta Vesterinen, Ryan Leckey, Janne Vanhala, Vesa Uimonen', diff --git a/sqlalchemy_utils/__init__.py b/sqlalchemy_utils/__init__.py index 90614d1..ab21bbf 100644 --- a/sqlalchemy_utils/__init__.py +++ b/sqlalchemy_utils/__init__.py @@ -15,6 +15,7 @@ from .functions import ( get_column_key, get_columns, get_declarative_base, + get_hybrid_properties, get_mapper, get_primary_keys, get_referencing_foreign_keys, @@ -73,7 +74,7 @@ from .types import ( from .models import Timestamp -__version__ = '0.26.6' +__version__ = '0.26.7' __all__ = ( @@ -96,6 +97,7 @@ __all__ = ( get_column_key, get_columns, get_declarative_base, + get_hybrid_properties, get_mapper, get_primary_keys, get_referencing_foreign_keys, diff --git a/sqlalchemy_utils/functions/__init__.py b/sqlalchemy_utils/functions/__init__.py index ba5ebae..8405c33 100644 --- a/sqlalchemy_utils/functions/__init__.py +++ b/sqlalchemy_utils/functions/__init__.py @@ -23,6 +23,7 @@ from .orm import ( get_column_key, get_columns, get_declarative_base, + get_hybrid_properties, get_mapper, get_primary_keys, get_tables, @@ -46,6 +47,7 @@ __all__ = ( 'get_bind', 'get_columns', 'get_declarative_base', + 'get_hybrid_properties', 'get_mapper', 'get_primary_keys', 'get_referencing_foreign_keys', diff --git a/sqlalchemy_utils/functions/orm.py b/sqlalchemy_utils/functions/orm.py index f7b017a..a51edbd 100644 --- a/sqlalchemy_utils/functions/orm.py +++ b/sqlalchemy_utils/functions/orm.py @@ -416,10 +416,55 @@ def get_attrs(expr): return inspect(expr).attrs -def get_hybrid_properties(class_): - for prop in sa.inspect(class_).all_orm_descriptors: - if isinstance(prop, hybrid_property): - yield prop +def get_hybrid_properties(model): + """ + Returns a dictionary of hybrid property keys and hybrid properties for + given SQLAlchemy declarative model / mapper. + + + Consider the following model + + :: + + + from sqlalchemy.ext.hybrid import hybrid_property + + + class Category(Base): + __tablename__ = 'category' + id = sa.Column(sa.Integer, primary_key=True) + name = sa.Column(sa.Unicode(255)) + + @hybrid_property + def lowercase_name(self): + return self.name.lower() + + @lowercase_name.expression + def lowercase_name(cls): + return sa.func.lower(cls.name) + + + You can now easily get a list of all hybrid property names + + :: + + + from sqlalchemy_utils import get_hybrid_properties + + + get_hybrid_properties(Category).keys() # ['lowercase_name'] + + + .. versionchanged: 0.26.7 + This function now returns a dictionary instead of generator + + :param model: SQLAlchemy declarative model or mapper + """ + return dict( + (key, prop) + for key, prop in sa.inspect(model).all_orm_descriptors.items() + if isinstance(prop, hybrid_property) + ) def get_expr_attr(expr, attr_name): @@ -483,10 +528,10 @@ def getdotattr(obj_or_class, dot_path): def has_changes(obj, attrs=None, exclude=None): """ - Simple shortcut function for checking if given attribute(s) of given - declarative model object has changed during the transaction. Without + Simple shortcut function for checking if given attributes of given + declarative model object have changed during the session. Without parameters this checks if given object has any modificiations. Additionally - exclude parameter can be given which check if given object has any changes + exclude parameter can be given to check if given object has any changes in any attributes other than the ones given in exclude. @@ -552,7 +597,7 @@ def has_changes(obj, attrs=None, exclude=None): ) -def has_any_changes(model, columns): +def has_any_changes(obj, columns): """ Simple shortcut function for checking if any of the given attributes of given declarative model object have changes. @@ -580,7 +625,7 @@ def has_any_changes(model, columns): :param obj: SQLAlchemy declarative model object :param attrs: Names of the attributes """ - return any(has_changes(model, column) for column in columns) + return any(has_changes(obj, column) for column in columns) def identity(obj_or_class): diff --git a/sqlalchemy_utils/functions/sort_query.py b/sqlalchemy_utils/functions/sort_query.py index 9d8944b..15b77b1 100644 --- a/sqlalchemy_utils/functions/sort_query.py +++ b/sqlalchemy_utils/functions/sort_query.py @@ -59,8 +59,8 @@ class QuerySorter(object): mapper = mapper.mapper entity = mapper.entity - for prop in get_hybrid_properties(mapper): - if attr == prop.__name__: + for key in get_hybrid_properties(mapper).keys(): + if attr == key: return getattr(entity, attr) def parse_sort_arg(self, arg): diff --git a/tests/test_sort_query.py b/tests/test_sort_query.py index 516e7c7..1d8a38f 100644 --- a/tests/test_sort_query.py +++ b/tests/test_sort_query.py @@ -137,6 +137,17 @@ class TestSortQuery(TestCase): ) in str(query) assert ' DESC' in str(query) + def test_assigned_hybrid_property(self): + def getter(self): + return self.name + + self.Article.some_hybrid = sa.ext.hybrid.hybrid_property( + fget=getter + ) + query = self.session.query(self.Article) + query = sort_query(query, 'some_hybrid') + assert 'ORDER BY article.name ASC' in str(query) + def test_relation_hybrid_property(self): query = ( self.session.query(self.Article)