]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
docs for custom ops...
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 16 Aug 2012 16:36:13 +0000 (12:36 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 16 Aug 2012 16:36:13 +0000 (12:36 -0400)
doc/build/core/expression_api.rst
doc/build/core/tutorial.rst
doc/build/core/types.rst
doc/build/orm/mapper_config.rst
lib/sqlalchemy/sql/operators.py

index 132b38b6afc26c98249464f89d78d48c4ef0d472..66338efae2c6ce88f2c3aac19f7c077281c6ce58 100644 (file)
@@ -145,6 +145,9 @@ Classes
    :members:
    :show-inheritance:
 
+.. autoclass:: sqlalchemy.sql.operators.custom_op
+   :members:
+
 .. autoclass:: CTE
    :members:
    :show-inheritance:
index 143f55df9af10167f073bc6f07b786a5703c4b6f..dad1aa68d66299d848db750a8fd37dd1bcd6eabe 100644 (file)
@@ -646,7 +646,7 @@ The above illustrates the SQL that's generated for an
 the ``||`` operator now compiles as MySQL's ``concat()`` function.
 
 If you have come across an operator which really isn't available, you can
-always use the ``op()`` method; this generates whatever operator you need:
+always use the :meth:`.ColumnOperators.op` method; this generates whatever operator you need:
 
 .. sourcecode:: pycon+sql
 
@@ -659,6 +659,18 @@ This function can also be used to make bitwise operators explicit. For example::
 
 is a bitwise AND of the value in `somecolumn`.
 
+Operator Customization
+-----------------------
+
+While :meth:`.ColumnOperators.op` is handy to get at a custom operator in a hurry,
+the Core supports fundamental customization and extension of the operator system at
+the type level.   The behavior of existing operators can be modified on a per-type
+basis, and new operations can be defined which become available for all column
+expressions that are part of that particular type.  See the section :ref:`types_operators`
+for a description.
+
+
+
 Conjunctions
 =============
 
index 8471ac29e95fa907e306b4a0062c0dd1537d8466..6518a5e9c74f8365f8162049526b74cca6772b42 100644 (file)
@@ -488,6 +488,68 @@ cursor directly::
       def adapt(self, impltype):
           return MySpecialTime(self.special_argument)
 
+.. _types_operators:
+
+Redefining and Creating New Operators
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+SQLAlchemy Core defines a fixed set of expression operators available to all column expressions.
+Some of these operations have the effect of overloading Python's built in operators;
+examples of such operators include
+:meth:`.ColumnOperators.__eq__` (``table.c.somecolumn == 'foo'``),
+:meth:`.ColumnOperators.neg` (``~table.c.flag``),
+and :meth:`.ColumnOperators.__add__` (``table.c.x + table.c.y``).  Other operators are exposed as
+explicit methods on column expressions, such as
+:meth:`.ColumnOperators.in_` (``table.c.value.in_(['x', 'y'])``) and :meth:`.ColumnOperators.like`
+(``table.c.value.like('%ed%')``).
+
+The Core expression constructs in all cases consult the type of the expression in order to determine
+the behavior of existing operators, as well as to locate additional operators that aren't part of
+the built in set.   The :class:`.TypeEngine` base class defines a root "comparison" implementation
+:class:`.TypeEngine.Comparator`, and many specific types provide their own sub-implementations of this
+class.   User-defined :class:`.TypeEngine.Comparator` implementations can be built directly into a
+simple subclass of a particular type in order to override or define new operations.  Below,
+we create a :class:`.Integer` subclass which overrides the :meth:`.ColumnOperators.__add__` operator::
+
+    from sqlalchemy import Integer
+
+    class MyInt(Integer):
+        class comparator_factory(Integer.Comparator):
+            def __add__(self, other):
+                return self.op("goofy")(other)
+
+The above configuration creates a new class ``MyInt``, which
+establishes the :attr:`.TypeEngine.comparator_factory` attribute as
+referring to a new class, subclassing the ``Comparator`` class
+associated with the :class:`.Integer` type.
+
+The implementation for :meth:`.ColumnOperators.__add__` is consulted
+by an owning SQL expression, by instantiating the ``Comparator`` with
+itself as as the ``expr`` attribute.   The mechanics of the expression
+system are such that operations continue recursively until an
+expression object produces a new SQL expression construct. Above, we
+could just as well have said ``self.expr.op("goofy")(other)`` instead
+of ``self.op("goofy")(other)``.
+
+New methods added to a ``Comparator`` are exposed on an owning SQL expression
+using a ``__getattr__`` scheme.  For example, to add an implementation of the
+Postgresql factorial operator::
+
+    from sqlalchemy import Integer
+    from sqlalchemy.sql import UnaryExpression
+    from sqlalchemy.sql import operators
+
+    class MyInteger(Integer):
+        class comparator_factory(Integer.Comparator):
+            def factorial(self):
+                return UnaryExpression(self.expr,
+                            modifier=operators.custom_op("!"),
+                            type_=MyInteger)
+
+
+
+
+
 
 Creating New Types
 ~~~~~~~~~~~~~~~~~~
index 55c5af979484f26e483a0d22aeb1a39c2f6104b2..62515d214199ddeeb63f430367b18045fa796c15 100644 (file)
@@ -733,57 +733,18 @@ based attribute.
 
 .. _custom_comparators:
 
-Custom Comparators
-------------------
-
-The expressions returned by comparison operations, such as
-``User.name=='ed'``, can be customized, by implementing an object that
-explicitly defines each comparison method needed.
-
-This is a relatively rare use case which generally applies only to
-highly customized types.  Usually, custom SQL behaviors can be
-associated with a mapped class by composing together the classes'
-existing mapped attributes with other expression components,
-using the techniques described in :ref:`mapper_sql_expressions`.
-Those approaches should be considered first before resorting to custom comparison objects.
-
-Each of :func:`.orm.column_property`, :func:`~.composite`, :func:`.relationship`,
-and :func:`.comparable_property` accept an argument called
-``comparator_factory``.   A subclass of :class:`.PropComparator` can be provided
-for this argument, which can then reimplement basic Python comparison methods
-such as ``__eq__()``, ``__ne__()``, ``__lt__()``, and so on.
-
-It's best to subclass the :class:`.PropComparator` subclass provided by
-each type of property.  For example, to allow a column-mapped attribute to
-do case-insensitive comparison::
-
-    from sqlalchemy.orm.properties import ColumnProperty
-    from sqlalchemy.sql import func, Column, Integer, String
-
-    class MyComparator(ColumnProperty.Comparator):
-        def __eq__(self, other):
-            return func.lower(self.__clause_element__()) == func.lower(other)
-
-    class EmailAddress(Base):
-        __tablename__ = 'address'
-        id = Column(Integer, primary_key=True)
-        email = column_property(
-                        Column('email', String),
-                        comparator_factory=MyComparator
-                    )
-
-Above, comparisons on the ``email`` column are wrapped in the SQL lower()
-function to produce case-insensitive matching::
-
-    >>> str(EmailAddress.email == 'SomeAddress@foo.com')
-    lower(address.email) = lower(:lower_1)
-
-When building a :class:`.PropComparator`, the ``__clause_element__()`` method
-should be used in order to acquire the underlying mapped column.  This will
-return a column that is appropriately wrapped in any kind of subquery
-or aliasing that has been applied in the context of the generated SQL statement.
-
-.. autofunction:: comparable_property
+Operator Customization
+----------------------
+
+The "operators" used by the SQLAlchemy ORM and Core expression language
+are fully customizable.  For example, the comparison expression
+``User.name == 'ed'`` makes usage of an operator built into Python
+itself called ``operator.eq`` - the actual SQL construct which SQLAlchemy
+associates with such an operator can be modified.  New
+operations can be associated with column expressions as well.   The operators
+which take place for column expressions are most directly redefined at the
+type level -  see the
+section :ref:`types_operators` for a description.
 
 .. _mapper_composite:
 
index 560f723f266cf103dab929129a8d9830755d25c2..ba1117ef69033a7e2148be1686f956648c01bf76 100644 (file)
@@ -170,6 +170,23 @@ class Operators(object):
 
 
 class custom_op(object):
+    """Represent a 'custom' operator.
+
+    :class:`.custom_op` is normally instantitated when the
+    :meth:`.ColumnOperators.op` method is used to create a
+    custom operator callable.  The class can also be used directly
+    when programmatically constructing expressions.   E.g.
+    to represent the "factorial" operation::
+
+        from sqlalchemy.sql import UnaryExpression
+        from sqlalchemy.sql import operators
+        from sqlalchemy import Numeric
+
+        unary = UnaryExpression(table.c.somecolumn,
+                modifier=operators.custom_op("!"),
+                type_=Numeric)
+
+    """
     __name__ = 'custom_op'
 
     def __init__(self, opstring, precedence=0):