0.4 release
This commit is contained in:
@@ -5,6 +5,13 @@ Here you can see the full list of changes between each SQLAlchemy-Utils release.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
0.4.0 (2013-03-01)
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
- Renamed SmartList to InstrumentedList
|
||||||
|
- Added instrumented_list decorator
|
||||||
|
|
||||||
|
|
||||||
0.3.0 (2013-03-01)
|
0.3.0 (2013-03-01)
|
||||||
^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
2
setup.py
2
setup.py
@@ -24,7 +24,7 @@ class PyTest(Command):
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='SQLAlchemy-Utils',
|
name='SQLAlchemy-Utils',
|
||||||
version='0.3',
|
version='0.4',
|
||||||
url='https://github.com/kvesteri/sqlalchemy-utils',
|
url='https://github.com/kvesteri/sqlalchemy-utils',
|
||||||
license='BSD',
|
license='BSD',
|
||||||
author='Konsta Vesterinen',
|
author='Konsta Vesterinen',
|
||||||
|
@@ -1,39 +1,28 @@
|
|||||||
|
from functools import wraps
|
||||||
from sqlalchemy.orm import defer
|
from sqlalchemy.orm import defer
|
||||||
from sqlalchemy.orm.collections import InstrumentedList
|
from sqlalchemy.orm.collections import InstrumentedList as _InstrumentedList
|
||||||
from sqlalchemy.orm.mapper import Mapper
|
from sqlalchemy.orm.mapper import Mapper
|
||||||
from sqlalchemy.orm.query import _ColumnEntity
|
from sqlalchemy.orm.query import _ColumnEntity
|
||||||
from sqlalchemy.orm.properties import ColumnProperty
|
from sqlalchemy.orm.properties import ColumnProperty
|
||||||
from sqlalchemy.sql.expression import desc, asc
|
from sqlalchemy.sql.expression import desc, asc
|
||||||
|
|
||||||
|
|
||||||
class SmartList(InstrumentedList):
|
class InstrumentedList(_InstrumentedList):
|
||||||
def has(self, attr):
|
"""Enhanced version of SQLAlchemy InstrumentedList. Provides some
|
||||||
"""
|
additional functionality."""
|
||||||
Returns True if any member of this collection has given attribute
|
|
||||||
defined.
|
|
||||||
|
|
||||||
Example syntax:
|
def any(self, attr):
|
||||||
|
return any(getattr(item, attr) for item in self)
|
||||||
|
|
||||||
>>> Category.articles.has('name')
|
def all(self, attr):
|
||||||
|
return all(getattr(item, attr) for item in self)
|
||||||
|
|
||||||
:param attr: collection member attribute name
|
|
||||||
"""
|
|
||||||
adapter = self._sa_adapter
|
|
||||||
owner_class = adapter.owner_state.class_
|
|
||||||
relation = getattr(owner_class, adapter._key).property
|
|
||||||
relation_class = relation.mapper.class_
|
|
||||||
|
|
||||||
if not hasattr(relation_class, attr):
|
def instrumented_list(f):
|
||||||
raise AttributeError(
|
@wraps(f)
|
||||||
'Class %s does not have attribute named %s' %
|
def wrapper(*args, **kwargs):
|
||||||
(relation_class.__name__, attr)
|
return InstrumentedList([item for item in f(*args, **kwargs)])
|
||||||
)
|
return wrapper
|
||||||
|
|
||||||
for record in self:
|
|
||||||
if getattr(record, attr):
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def sort_query(query, sort):
|
def sort_query(query, sort):
|
||||||
|
20
tests.py
20
tests.py
@@ -1,11 +1,10 @@
|
|||||||
from pytest import raises
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
from sqlalchemy.orm import sessionmaker
|
from sqlalchemy.orm import sessionmaker
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
|
||||||
from sqlalchemy_utils import escape_like, sort_query, SmartList
|
from sqlalchemy_utils import escape_like, sort_query, InstrumentedList
|
||||||
|
|
||||||
|
|
||||||
engine = create_engine(
|
engine = create_engine(
|
||||||
@@ -34,27 +33,22 @@ class Article(Base):
|
|||||||
primaryjoin=category_id == Category.id,
|
primaryjoin=category_id == Category.id,
|
||||||
backref=sa.orm.backref(
|
backref=sa.orm.backref(
|
||||||
'articles',
|
'articles',
|
||||||
collection_class=SmartList
|
collection_class=InstrumentedList
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestSmartList(object):
|
class TestInstrumentedList(object):
|
||||||
def test_has_raises_error_for_unknown_attribute(self):
|
def test_any_returns_true_if_member_has_attr_defined(self):
|
||||||
category = Category()
|
|
||||||
with raises(AttributeError):
|
|
||||||
category.articles.has('unknown_column')
|
|
||||||
|
|
||||||
def test_has_returns_true_if_member_has_attr_defined(self):
|
|
||||||
category = Category()
|
category = Category()
|
||||||
category.articles.append(Article())
|
category.articles.append(Article())
|
||||||
category.articles.append(Article(name=u'some name'))
|
category.articles.append(Article(name=u'some name'))
|
||||||
assert category.articles.has('name')
|
assert category.articles.any('name')
|
||||||
|
|
||||||
def test_has_returns_false_if_no_member_has_attr_defined(self):
|
def test_any_returns_false_if_no_member_has_attr_defined(self):
|
||||||
category = Category()
|
category = Category()
|
||||||
category.articles.append(Article())
|
category.articles.append(Article())
|
||||||
assert not category.articles.has('name')
|
assert not category.articles.any('name')
|
||||||
|
|
||||||
|
|
||||||
class TestEscapeLike(object):
|
class TestEscapeLike(object):
|
||||||
|
Reference in New Issue
Block a user