diff --git a/CHANGES.rst b/CHANGES.rst index 0f54467..395dc24 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.27.8 (2014-11-13) +^^^^^^^^^^^^^^^^^^^ + +- Added is_loaded utility function + + 0.27.7 (2014-11-03) ^^^^^^^^^^^^^^^^^^^ diff --git a/docs/orm_helpers.rst b/docs/orm_helpers.rst index f2605b6..76dc044 100644 --- a/docs/orm_helpers.rst +++ b/docs/orm_helpers.rst @@ -76,6 +76,12 @@ identity .. autofunction:: identity +is_loaded +^^^^^^^^^ + +.. autofunction:: is_loaded + + make_order_by_deterministic ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/sqlalchemy_utils/__init__.py b/sqlalchemy_utils/__init__.py index 0da4b90..f23471a 100644 --- a/sqlalchemy_utils/__init__.py +++ b/sqlalchemy_utils/__init__.py @@ -29,6 +29,7 @@ from .functions import ( has_index, has_unique_index, identity, + is_loaded, merge_references, mock_engine, naturally_equivalent, @@ -118,6 +119,7 @@ __all__ = ( has_index, identity, instrumented_list, + is_loaded, merge_references, mock_engine, naturally_equivalent, diff --git a/sqlalchemy_utils/functions/__init__.py b/sqlalchemy_utils/functions/__init__.py index 3d9fedd..766d7b6 100644 --- a/sqlalchemy_utils/functions/__init__.py +++ b/sqlalchemy_utils/functions/__init__.py @@ -38,6 +38,7 @@ from .orm import ( has_any_changes, has_changes, identity, + is_loaded, naturally_equivalent, quote, table_name, @@ -65,6 +66,7 @@ __all__ = ( 'has_any_changes', 'has_changes', 'identity', + 'is_loaded', 'is_auto_assigned_date_column', 'is_indexed_foreign_key', 'make_order_by_deterministic', diff --git a/sqlalchemy_utils/functions/orm.py b/sqlalchemy_utils/functions/orm.py index 457211c..a4f6d05 100644 --- a/sqlalchemy_utils/functions/orm.py +++ b/sqlalchemy_utils/functions/orm.py @@ -755,6 +755,39 @@ def has_any_changes(obj, columns): return any(has_changes(obj, column) for column in columns) +def is_loaded(obj, prop): + """ + Return whether or not given property of given object has been loaded. + + :: + + class Article(Base): + __tablename__ = 'article' + id = sa.Column(sa.Integer, primary_key=True) + name = sa.Column(sa.String) + content = sa.orm.deferred(sa.Column(sa.String)) + + + article = session.query(Article).get(5) + + # name gets loaded since its not a deferred property + assert is_loaded(article, 'name') + + # content has not yet been loaded since its a deferred property + assert not is_loaded(article, 'content') + + + .. versionadded: 0.27.8 + + :param obj: SQLAlchemy declarative model object + :param prop: Name of the property or InstrumentedAttribute + """ + return not isinstance( + getattr(sa.inspect(obj).attrs, prop).loaded_value, + sa.util.langhelpers._symbol + ) + + def identity(obj_or_class): """ Return the identity of given sqlalchemy declarative model class or instance diff --git a/tests/functions/test_is_loaded.py b/tests/functions/test_is_loaded.py new file mode 100644 index 0000000..1996c86 --- /dev/null +++ b/tests/functions/test_is_loaded.py @@ -0,0 +1,25 @@ +import sqlalchemy as sa +from sqlalchemy.ext.declarative import declarative_base + +from sqlalchemy_utils import is_loaded + + +class TestIsLoaded(object): + def setup_method(self, method): + Base = declarative_base() + + class Article(Base): + __tablename__ = 'article_translation' + id = sa.Column(sa.Integer, primary_key=True) + title = sa.orm.deferred(sa.Column(sa.String(100))) + + self.Article = Article + + def test_loaded_property(self): + article = self.Article(id=1) + assert is_loaded(article, 'id') + + def test_unloaded_property(self): + article = self.Article(id=4) + assert not is_loaded(article, 'title') +