summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2012-08-16 12:36:13 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2012-08-16 12:36:13 -0400
commita180239d8eda8c20ea39f4f5190abf51deba8b05 (patch)
tree864da3ec361f13f2fbce02735a2c8c6c54b7f16a
parent0e29e6e7abea6ae7235de302c966ab9fb343819c (diff)
downloadsqlalchemy-a180239d8eda8c20ea39f4f5190abf51deba8b05.tar.gz
docs for custom ops...
-rw-r--r--doc/build/core/expression_api.rst3
-rw-r--r--doc/build/core/tutorial.rst14
-rw-r--r--doc/build/core/types.rst62
-rw-r--r--doc/build/orm/mapper_config.rst63
-rw-r--r--lib/sqlalchemy/sql/operators.py17
5 files changed, 107 insertions, 52 deletions
diff --git a/doc/build/core/expression_api.rst b/doc/build/core/expression_api.rst
index 132b38b6a..66338efae 100644
--- a/doc/build/core/expression_api.rst
+++ b/doc/build/core/expression_api.rst
@@ -145,6 +145,9 @@ Classes
:members:
:show-inheritance:
+.. autoclass:: sqlalchemy.sql.operators.custom_op
+ :members:
+
.. autoclass:: CTE
:members:
:show-inheritance:
diff --git a/doc/build/core/tutorial.rst b/doc/build/core/tutorial.rst
index 143f55df9..dad1aa68d 100644
--- a/doc/build/core/tutorial.rst
+++ b/doc/build/core/tutorial.rst
@@ -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
=============
diff --git a/doc/build/core/types.rst b/doc/build/core/types.rst
index 8471ac29e..6518a5e9c 100644
--- a/doc/build/core/types.rst
+++ b/doc/build/core/types.rst
@@ -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
~~~~~~~~~~~~~~~~~~
diff --git a/doc/build/orm/mapper_config.rst b/doc/build/orm/mapper_config.rst
index 55c5af979..62515d214 100644
--- a/doc/build/orm/mapper_config.rst
+++ b/doc/build/orm/mapper_config.rst
@@ -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:
diff --git a/lib/sqlalchemy/sql/operators.py b/lib/sqlalchemy/sql/operators.py
index 560f723f2..ba1117ef6 100644
--- a/lib/sqlalchemy/sql/operators.py
+++ b/lib/sqlalchemy/sql/operators.py
@@ -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):