From: Mike Bayer Date: Sun, 22 May 2011 19:54:17 +0000 (-0400) Subject: - add some docs to hybrid comparators, operators/comparator logic at the base X-Git-Tag: rel_0_7_1~34 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=74a6cb17c875cdce4c6b8a4b8a8a8498b1b767cd;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - add some docs to hybrid comparators, operators/comparator logic at the base --- diff --git a/doc/build/core/expression_api.rst b/doc/build/core/expression_api.rst index b7004220b9..cfa22591de 100644 --- a/doc/build/core/expression_api.rst +++ b/doc/build/core/expression_api.rst @@ -135,6 +135,26 @@ Classes :members: :undoc-members: :inherited-members: + :show-inheritance: + + .. automethod:: __eq__ + .. automethod:: __ne__ + .. automethod:: __gt__ + .. automethod:: __ge__ + .. automethod:: __lt__ + .. automethod:: __le__ + .. automethod:: __neg__ + .. automethod:: __add__ + .. automethod:: __mul__ + .. automethod:: __div__ + .. automethod:: __truediv__ + .. automethod:: __sub__ + .. automethod:: __radd__ + .. automethod:: __rsub__ + .. automethod:: __rtruediv__ + .. automethod:: __rdiv__ + .. automethod:: __rmul__ + .. automethod:: __mod__ .. autoclass:: CompoundSelect :members: @@ -168,6 +188,14 @@ Classes :members: :show-inheritance: +.. autoclass:: Operators + :members: + :undoc-members: + + .. automethod:: __and__ + .. automethod:: __or__ + .. automethod:: __invert__ + .. autoclass:: Select :members: :show-inheritance: diff --git a/lib/sqlalchemy/ext/hybrid.py b/lib/sqlalchemy/ext/hybrid.py index c16c38b2cf..2c353f11da 100644 --- a/lib/sqlalchemy/ext/hybrid.py +++ b/lib/sqlalchemy/ext/hybrid.py @@ -259,7 +259,12 @@ some highly idiosyncratic behavior on the SQL side. The example class below allows case-insensitive comparisons on the attribute named ``word_insensitive``:: - from sqlalchemy.ext.hybrid import Comparator + from sqlalchemy.ext.hybrid import Comparator, hybrid_property + from sqlalchemy import func, Column, Integer, String + from sqlalchemy.orm import Session + from sqlalchemy.ext.declarative import declarative_base + + Base = declarative_base() class CaseInsensitiveComparator(Comparator): def __eq__(self, other): @@ -286,6 +291,15 @@ SQL function to both sides:: FROM searchword WHERE lower(searchword.word) = lower(:lower_1) +The ``CaseInsensitiveComparator`` above implements part of the :class:`.ColumnOperators` +interface. A "coercion" operation like lowercasing can be applied to all comparison operations +(i.e. ``eq``, ``lt``, ``gt``, etc.) using :meth:`.Operators.operate`:: + + class CaseInsensitiveComparator(Comparator): + def operate(self, op, other): + return op(func.lower(self.__clause_element__()), func.lower(other)) + + """ from sqlalchemy import util from sqlalchemy.orm import attributes, interfaces diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index e06eb61bef..0f06e9ee76 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -1631,6 +1631,15 @@ class _Immutable(object): return self class Operators(object): + """Base of comparison and logical operators. + + Implements base methods :meth:`operate` and :meth:`reverse_operate`, + as well as :meth:`__and__`, :meth:`__or__`, :meth:`__invert__`. + + Usually is used via its most common subclass + :class:`.ColumnOperators`. + + """ def __and__(self, other): return self.operate(operators.and_, other) @@ -1646,13 +1655,65 @@ class Operators(object): return op def operate(self, op, *other, **kwargs): + """Operate on an argument. + + This is the lowest level of operation, raises + :class:`NotImplementedError` by default. + + Overriding this on a subclass can allow common + behavior to be applied to all operations. + For example, overriding :class:`.ColumnOperators` + to apply ``func.lower()`` to the left and right + side:: + + class MyComparator(ColumnOperators): + def operate(self, op, other): + return op(func.lower(self), func.lower(other)) + + :param op: Operator callable. + :param \*other: the 'other' side of the operation. Will + be a single scalar for most operations. + :param \**kwargs: modifiers. These may be passed by special + operators such as :meth:`ColumnOperators.contains`. + + + """ raise NotImplementedError(str(op)) def reverse_operate(self, op, other, **kwargs): + """Reverse operate on an argument. + + Usage is the same as :meth:`operate`. + + """ raise NotImplementedError(str(op)) class ColumnOperators(Operators): - """Defines comparison and math operations.""" + """Defines comparison and math operations. + + By default all methods call down to + :meth:`Operators.operate` or :meth:`Operators.reverse_operate` + passing in the appropriate operator function from the + Python builtin ``operator`` module or + a SQLAlchemy-specific operator function from + :mod:`sqlalchemy.expression.operators`. For example + the ``__eq__`` function:: + + def __eq__(self, other): + return self.operate(operators.eq, other) + + Where ``operators.eq`` is essentially:: + + def eq(a, b): + return a == b + + A SQLAlchemy construct like :class:`.ColumnElement` ultimately + overrides :meth:`.Operators.operate` and others + to return further :class:`.ClauseElement` constructs, + so that the ``==`` operation above is replaced by a clause + construct. + + """ timetuple = None """Hack, allows datetime objects to be compared on the LHS."""