next compile() call. This issue occurs frequently
when using declarative.
+ - Custom comparator classes used in conjunction with
+ column_property(), relation() etc. can define
+ new comparison methods on the Comparator, which will
+ become available via __getattr__() on the
+ InstrumentedAttribute. In the case of synonym()
+ or comparable_property(), attributes are resolved first
+ on the user-defined descriptor, then on the user-defined
+ comparator.
+
- Added ScopedSession.is_active accessor. [ticket:976]
- Can pass mapped attributes and column objects as keys
"""Intercepts standard Column operators on mapped class attributes
and overrides their behavior.
- The PropComparator API currently does not allow "custom"
- operators to be added, so only those operators which
- already exist on Column can be overridden here. Additional
- GIS-specific operators can be implemented as standalone
- functions.
"""
def __eq__(self, other):
return self.__clause_element__().op('~=')(_to_postgis(other))
-def intersects(x, y):
- """An example standalone GIS-specific comparison operator."""
-
- return _to_postgis(x).op('&&')(_to_postgis(y))
+ def intersects(self, other):
+ return self.__clause_element__().op('&&')(_to_postgis(other))
class gis_element(object):
"""Represents a geometry value.
assert r1 is r2 is r3
# illustrate the "intersects" operator
- print session.query(Road).filter(intersects(Road.road_geom, r1.road_geom)).all()
+ print session.query(Road).filter(Road.road_geom.intersects(r1.road_geom)).all()
# illustrate usage of the "wkt" accessor. this requires a DB
# execution to call the AsText() function so we keep this explicit.
def hasparent(self, state, optimistic=False):
return self.impl.hasparent(state, optimistic=optimistic)
-
+
+ def __getattr__(self, key):
+ try:
+ return getattr(self.comparator, key)
+ except AttributeError:
+ raise AttributeError('Neither %r object nor %r object has an attribute %r' % (
+ type(self).__name__,
+ type(self.comparator).__name__,
+ key)
+ )
+
def __str__(self):
return repr(self.parententity) + "." + self.property.key
return descriptor.__delete__(instance)
def __getattr__(self, attribute):
- """Delegate __getattr__ to the original descriptor."""
- return getattr(descriptor, attribute)
+ """Delegate __getattr__ to the original descriptor and/or comparator."""
+
+ try:
+ return getattr(descriptor, attribute)
+ except AttributeError:
+ try:
+ return getattr(self._comparator, attribute)
+ except AttributeError:
+ raise AttributeError('Neither %r object nor %r object has an attribute %r' % (
+ type(descriptor).__name__,
+ type(self._comparator).__name__,
+ attribute)
+ )
def _property(self):
return self._parententity.get_property(self.key, resolve_synonyms=True)
self.group = kwargs.pop('group', None)
self.deferred = kwargs.pop('deferred', False)
self.comparator_factory = kwargs.pop('comparator_factory', self.__class__.Comparator)
+ self.descriptor = kwargs.pop('descriptor', None)
self.extension = kwargs.pop('extension', None)
util.set_creation_order(self)
if self.deferred:
if obj is None:
return s
return getattr(obj, self.name)
+
self.descriptor = SynonymProp()
def comparator_callable(prop, mapper):
def test_comparable(self):
class extendedproperty(property):
attribute = 123
+
+ def method1(self):
+ return "method1"
+
def __getitem__(self, key):
return 'value'
class UCComparator(sa.orm.PropComparator):
__hash__ = None
+
+ def method1(self):
+ return "uccmethod1"
+
+ def method2(self, other):
+ return "method2"
+
def __eq__(self, other):
cls = self.prop.parent.class_
col = getattr(cls, 'name')
assert hasattr(User, 'name')
assert hasattr(User, 'uc_name')
+ eq_(User.uc_name.method1(), "method1")
+ eq_(User.uc_name.method2('x'), "method2")
+
+ self.assertRaisesMessage(
+ AttributeError,
+ "Neither 'extendedproperty' object nor 'UCComparator' object has an attribute 'nonexistent'",
+ getattr, User.uc_name, 'nonexistent')
+
# test compile
assert not isinstance(User.uc_name == 'jack', bool)
u = q.filter(User.uc_name=='JACK').one()
eq_(User.uc_name['key'], 'value')
sess.rollback()
+ @testing.resolve_artifact_names
+ def test_comparable_column(self):
+ class MyComparator(sa.orm.properties.ColumnProperty.Comparator):
+ def __eq__(self, other):
+ # lower case comparison
+ return func.lower(self.__clause_element__()) == func.lower(other)
+
+ def intersects(self, other):
+ # non-standard comparator
+ return self.__clause_element__().op('&=')(other)
+
+ mapper(User, users, properties={
+ 'name':sa.orm.column_property(users.c.name, comparator_factory=MyComparator)
+ })
+
+ self.assertRaisesMessage(
+ AttributeError,
+ "Neither 'InstrumentedAttribute' object nor 'MyComparator' object has an attribute 'nonexistent'",
+ getattr, User.name, "nonexistent")
+
+ eq_(str((User.name == 'ed').compile(dialect=sa.engine.default.DefaultDialect())) , "lower(users.name) = lower(:lower_1)")
+ eq_(str((User.name.intersects('ed')).compile(dialect=sa.engine.default.DefaultDialect())), "users.name &= :name_1")
+
+
@testing.resolve_artifact_names
def test_reconstructor(self):
recon = []