diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-07-18 19:08:31 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-07-18 19:08:31 -0400 |
commit | e8ff3047c6596d39bb38956eb5aba5651c104e63 (patch) | |
tree | cbcc704e9388fba6f16bd914c2beede244be9dc8 | |
parent | 9c0de7fcf7fc56701af446742fe876335aef15b1 (diff) | |
download | sqlalchemy-e8ff3047c6596d39bb38956eb5aba5651c104e63.tar.gz |
a lot of docs
-rw-r--r-- | doc/build/core/expression_api.rst | 6 | ||||
-rw-r--r-- | doc/build/core/inspection.rst | 5 | ||||
-rw-r--r-- | doc/build/orm/index.rst | 1 | ||||
-rw-r--r-- | doc/build/orm/interfaces.rst | 1 | ||||
-rw-r--r-- | doc/build/orm/internals.rst | 12 | ||||
-rw-r--r-- | doc/build/orm/query.rst | 4 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/attributes.py | 48 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/state.py | 17 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/util.py | 135 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/expression.py | 80 |
10 files changed, 213 insertions, 96 deletions
diff --git a/doc/build/core/expression_api.rst b/doc/build/core/expression_api.rst index a49fa38fb..de9181252 100644 --- a/doc/build/core/expression_api.rst +++ b/doc/build/core/expression_api.rst @@ -106,6 +106,10 @@ Classes :members: :show-inheritance: +.. autoclass:: BinaryExpression + :members: + :show-inheritance: + .. autoclass:: BindParameter :members: :show-inheritance: @@ -195,7 +199,7 @@ Classes :members: :show-inheritance: -.. autoclass:: sqlalchemy.sql.expression.Operators +.. autoclass:: sqlalchemy.sql.operators.Operators :members: :undoc-members: diff --git a/doc/build/core/inspection.rst b/doc/build/core/inspection.rst index 2fc371d84..777ee944e 100644 --- a/doc/build/core/inspection.rst +++ b/doc/build/core/inspection.rst @@ -18,7 +18,10 @@ Following is a listing of all inspection targets. meaning any of these objects passed to :func:`.inspect` return themselves. * ``object`` - an object given will be checked by the ORM for a mapping - if so, an :class:`.InstanceState` is returned representing the mapped - state of the object. + state of the object. The :class:`.InstanceState` also provides access + to per attribute state via the :class:`.AttributeState` interface as well + as the per-flush "history" of any attribute via the :class:`.History` + object. * ``type`` (i.e. a class) - a class given will be checked by the ORM for a mapping - if so, a :class:`.Mapper` for that class is returned. * mapped attribute - passing a mapped attribute to :func:`.inspect`, such diff --git a/doc/build/orm/index.rst b/doc/build/orm/index.rst index 0c461fb6d..6c12ebd38 100644 --- a/doc/build/orm/index.rst +++ b/doc/build/orm/index.rst @@ -22,6 +22,5 @@ tutorial. events extensions/index examples - interfaces exceptions internals diff --git a/doc/build/orm/interfaces.rst b/doc/build/orm/interfaces.rst deleted file mode 100644 index bedbeca40..000000000 --- a/doc/build/orm/interfaces.rst +++ /dev/null @@ -1 +0,0 @@ -This page has moved to :ref:`dep_interfaces_orm_toplevel`. diff --git a/doc/build/orm/internals.rst b/doc/build/orm/internals.rst index 301156f8b..4f640b265 100644 --- a/doc/build/orm/internals.rst +++ b/doc/build/orm/internals.rst @@ -3,10 +3,15 @@ ORM Internals ============= -Some key internal constructs are listed here. +Key ORM constructs, not otherwise covered in other +sections, are listed here. .. currentmodule: sqlalchemy.orm +.. autoclass:: sqlalchemy.orm.state.AttributeState + :members: + :show-inheritance: + .. autoclass:: sqlalchemy.orm.instrumentation.ClassManager :members: :show-inheritance: @@ -23,10 +28,6 @@ Some key internal constructs are listed here. :members: :show-inheritance: -.. autoclass:: sqlalchemy.orm.state.InspectAttr - :members: - :show-inheritance: - .. autoclass:: sqlalchemy.orm.interfaces.MapperProperty :members: :show-inheritance: @@ -39,7 +40,6 @@ Some key internal constructs are listed here. :members: :show-inheritance: - .. autoclass:: sqlalchemy.orm.descriptor_props.SynonymProperty :members: :show-inheritance: diff --git a/doc/build/orm/query.rst b/doc/build/orm/query.rst index d143908eb..dcd7ec40e 100644 --- a/doc/build/orm/query.rst +++ b/doc/build/orm/query.rst @@ -26,9 +26,7 @@ Following is the full interface for the :class:`.Query` object. ORM-Specific Query Constructs ----------------------------- -.. class:: aliased - -The public name of the :class:`.AliasedClass` class. +.. autofunction:: sqlalchemy.orm.aliased .. autoclass:: sqlalchemy.orm.util.AliasedClass diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index d335d8996..47349e64a 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -786,9 +786,12 @@ class CollectionAttributeImpl(AttributeImpl): original_set = dict(original_states) return \ - [(s, o) for s, o in current_states if s not in original_set] + \ - [(s, o) for s, o in current_states if s in original_set] + \ - [(s, o) for s, o in original_states if s not in current_set] + [(s, o) for s, o in current_states + if s not in original_set] + \ + [(s, o) for s, o in current_states + if s in original_set] + \ + [(s, o) for s, o in original_states + if s not in current_set] return [(instance_state(o), o) for o in current] @@ -1036,32 +1039,35 @@ _NO_STATE_SYMBOLS = frozenset([ id(PASSIVE_NO_RESULT), id(NO_VALUE), id(NEVER_SET)]) -class History(tuple): + +History = util.namedtuple("History", [ + "added", "unchanged", "deleted" + ]) + +class History(History): """A 3-tuple of added, unchanged and deleted values, representing the changes which have occurred on an instrumented attribute. - Each tuple member is an iterable sequence. + The easiest way to get a :class:`.History` object for a particular + attribute on an object is to use the :func:`.inspect` function:: - """ + from sqlalchemy import inspect - __slots__ = () + hist = inspect(myobject).attr.myattribute.history - added = property(itemgetter(0)) - """Return the collection of items added to the attribute (the first tuple - element).""" + Each tuple member is an iterable sequence: - unchanged = property(itemgetter(1)) - """Return the collection of items that have not changed on the attribute - (the second tuple element).""" + * ``added`` - the collection of items added to the attribute (the first + tuple element). + * ``unchanged`` - the collection of items that have not changed on the + attribute (the second tuple element). - deleted = property(itemgetter(2)) - """Return the collection of items that have been removed from the - attribute (the third tuple element).""" + * ``deleted`` - the collection of items that have been removed from the + attribute (the third tuple element). - def __new__(cls, added, unchanged, deleted): - return tuple.__new__(cls, (added, unchanged, deleted)) + """ def __nonzero__(self): return self != HISTORY_BLANK @@ -1178,10 +1184,12 @@ class History(tuple): return cls((), list(current), ()) else: - current_states = [((c is not None) and instance_state(c) or None, c) + current_states = [((c is not None) and instance_state(c) + or None, c) for c in current ] - original_states = [((c is not None) and instance_state(c) or None, c) + original_states = [((c is not None) and instance_state(c) + or None, c) for c in original ] diff --git a/lib/sqlalchemy/orm/state.py b/lib/sqlalchemy/orm/state.py index 9307c94da..1c7fc2c41 100644 --- a/lib/sqlalchemy/orm/state.py +++ b/lib/sqlalchemy/orm/state.py @@ -54,12 +54,12 @@ class InstanceState(interfaces._InspectionAttr): the mapped object, including its current value and history. - The returned object is an instance of :class:`.InspectAttr`. + The returned object is an instance of :class:`.AttributeState`. """ return util.ImmutableProperties( dict( - (key, InspectAttr(self, key)) + (key, AttributeState(self, key)) for key in self.manager ) ) @@ -508,13 +508,18 @@ class InstanceState(interfaces._InspectionAttr): state.modified = state.expired = False state._strong_obj = None -class InspectAttr(object): +class AttributeState(object): """Provide an inspection interface corresponding to a particular attribute on a particular mapped object. - The :class:`.InspectAttr` object is created by - accessing the :attr:`.InstanceState.attr` - collection. + The :class:`.AttributeState` object is accessed + via the :attr:`.InstanceState.attr` collection + of a particular :class:`.InstanceState`:: + + from sqlalchemy import inspect + + insp = inspect(some_mapped_object) + attr_state = insp.attr.some_attribute """ diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py index 97bf4e7a9..8f340d366 100644 --- a/lib/sqlalchemy/orm/util.py +++ b/lib/sqlalchemy/orm/util.py @@ -386,7 +386,10 @@ class AliasedClass(object): __getattr__ scheme and maintains a reference to a real :class:`~sqlalchemy.sql.expression.Alias` object. - Usage is via the :class:`~sqlalchemy.orm.aliased()` synonym:: + Usage is via the :func:`.orm.aliased` function, or alternatively + via the :func:`.orm.with_polymorphic` function. + + Usage example:: # find all pairs of users with the same name user_alias = aliased(User) @@ -394,51 +397,25 @@ class AliasedClass(object): join((user_alias, User.id > user_alias.id)).\\ filter(User.name==user_alias.name) - The resulting object is an instance of :class:`.AliasedClass`, however - it implements a ``__getattribute__()`` scheme which will proxy attribute - access to that of the ORM class being aliased. All classmethods - on the mapped entity should also be available here, including - hybrids created with the :ref:`hybrids_toplevel` extension, - which will receive the :class:`.AliasedClass` as the "class" argument - when classmethods are called. - - :param cls: ORM mapped entity which will be "wrapped" around an alias. - :param alias: a selectable, such as an :func:`.alias` or :func:`.select` - construct, which will be rendered in place of the mapped table of the - ORM entity. If left as ``None``, an ordinary :class:`.Alias` of the - ORM entity's mapped table will be generated. - :param name: A name which will be applied both to the :class:`.Alias` - if one is generated, as well as the name present in the "named tuple" - returned by the :class:`.Query` object when results are returned. - :param adapt_on_names: if True, more liberal "matching" will be used when - mapping the mapped columns of the ORM entity to those of the - given selectable - a name-based match will be performed if the - given selectable doesn't otherwise have a column that corresponds - to one on the entity. The use case for this is when associating - an entity with some derived selectable such as one that uses - aggregate functions:: - - class UnitPrice(Base): - __tablename__ = 'unit_price' - ... - unit_id = Column(Integer) - price = Column(Numeric) + The resulting object is an instance of :class:`.AliasedClass`. + This object implements an attribute scheme which produces the + same attribute and method interface as the original mapped + class, allowing :class:`.AliasedClass` to be compatible + with any attribute technique which works on the original class, + including hybrid attributes (see :ref:`hybrids_toplevel`). - aggregated_unit_price = Session.query( - func.sum(UnitPrice.price).label('price') - ).group_by(UnitPrice.unit_id).subquery() + The :class:`.AliasedClass` can be inspected for its underlying + :class:`.Mapper`, aliased selectable, and other information + using :func:`.inspect`:: - aggregated_unit_price = aliased(UnitPrice, - alias=aggregated_unit_price, adapt_on_names=True) + from sqlalchemy import inspect + my_alias = aliased(MyClass) + insp = inspect(my_alias) - Above, functions on ``aggregated_unit_price`` which refer to - ``.price`` will return the - ``fund.sum(UnitPrice.price).label('price')`` column, as it is - matched on the name "price". Ordinarily, the "price" function - wouldn't have any "column correspondence" to the actual - ``UnitPrice.price`` column as it is not a proxy of the original. + The resulting inspection object is an instance of :class:`.AliasedInsp`. - .. versionadded:: 0.7.3 + See :func:`.aliased` and :func:`.with_polymorphic` for construction + argument descriptions. """ def __init__(self, cls, alias=None, @@ -601,6 +578,74 @@ class AliasedInsp(_InspectionAttr, AliasedInsp): inspection._inspects(AliasedClass)(lambda target: target._aliased_insp) def aliased(element, alias=None, name=None, adapt_on_names=False): + """Produce an alias of the given element, usually an :class:`.AliasedClass` + instance. + + E.g.:: + + my_alias = aliased(MyClass) + + session.query(MyClass, my_alias).filter(MyClass.id > my_alias.id) + + The :func:`.aliased` function is used to create an ad-hoc mapping + of a mapped class to a new selectable. By default, a selectable + is generated from the normally mapped selectable (typically a + :class:`.Table`) using the :meth:`.FromClause.alias` method. + However, :func:`.aliased` can also be used to link the class to + a new :func:`.select` statement. Also, the :func:`.with_polymorphic` + function is a variant of :func:`.aliased` that is intended to specify + a so-called "polymorphic selectable", that corresponds to the union + of several joined-inheritance subclasses at once. + + For convenience, the :func:`.aliased` function also accepts plain + :class:`.FromClause` constructs, such as a :class:`.Table` or + :func:`.select` construct. In those cases, the :meth:`.FromClause.alias` + method is called on the object and the new :class:`.Alias` object + returned. The returned :class:`.Alias` is not ORM-mapped in this case. + + :param element: element to be aliased. Is normally a mapped class, + but for convenience can also be a :class:`.FromClause` element. + :param alias: Optional selectable unit to map the element to. This should + normally be a :class:`.Alias` object corresponding to the :class:`.Table` + to which the class is mapped, or to a :func:`.select` construct that + is compatible with the mapping. By default, a simple anonymous + alias of the mapped table is generated. + :param name: optional string name to use for the alias, if not specified + by the ``alias`` parameter. The name, among other things, forms the + attribute name that will be accessible via tuples returned by a + :class:`.Query` object. + :param adapt_on_names: if True, more liberal "matching" will be used when + mapping the mapped columns of the ORM entity to those of the + given selectable - a name-based match will be performed if the + given selectable doesn't otherwise have a column that corresponds + to one on the entity. The use case for this is when associating + an entity with some derived selectable such as one that uses + aggregate functions:: + + class UnitPrice(Base): + __tablename__ = 'unit_price' + ... + unit_id = Column(Integer) + price = Column(Numeric) + + aggregated_unit_price = Session.query( + func.sum(UnitPrice.price).label('price') + ).group_by(UnitPrice.unit_id).subquery() + + aggregated_unit_price = aliased(UnitPrice, + alias=aggregated_unit_price, adapt_on_names=True) + + Above, functions on ``aggregated_unit_price`` which refer to + ``.price`` will return the + ``fund.sum(UnitPrice.price).label('price')`` column, as it is + matched on the name "price". Ordinarily, the "price" function + wouldn't have any "column correspondence" to the actual + ``UnitPrice.price`` column as it is not a proxy of the original. + + .. versionadded:: 0.7.3 + + + """ if isinstance(element, expression.FromClause): if adapt_on_names: raise sa_exc.ArgumentError( @@ -904,7 +949,7 @@ def object_mapper(instance): inspect(instance).mapper - Using the inspection system will raise plain + Using the inspection system will raise :class:`sqlalchemy.exc.NoInspectionAvailable` if the instance is not part of a mapping. @@ -947,8 +992,8 @@ def class_mapper(class_, configure=True): inspect(some_mapped_class) - Using the inspection system will raise plain - :class:`.InvalidRequestError` if the class is not mapped. + Using the inspection system will raise + :class:`sqlalchemy.exc.NoInspectionAvailable` if the class is not mapped. """ mapper = _inspect_mapped_class(class_, configure=configure) diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index a97ef5ff2..ae25e8c7f 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -1872,8 +1872,11 @@ class Immutable(object): class CompareMixin(ColumnOperators): - """Defines comparison and math operations for :class:`.ClauseElement` - instances. + """Defines comparison and math operations. + + The :class:`.CompareMixin` is part of the interface provided + by the :class:`.ColumnElement` class, which provides the base class + for all SQL expression units. See :class:`.ColumnOperators` and :class:`.Operators` for descriptions of all operations. @@ -2132,21 +2135,40 @@ class ColumnElement(ClauseElement, CompareMixin): """Represent an element that is usable within the "column clause" portion of a ``SELECT`` statement. - This includes columns associated with tables, aliases, and + While the most familiar kind of :class:`.ColumnElement` is the + :class:`.Column` object, :class:`.ColumnElement` serves as the basis + for any unit that may be present in a SQL expression, including + the columns associated with tables, aliases, and subqueries, expressions, function calls, SQL keywords such as ``NULL``, literals, etc. :class:`.ColumnElement` is the ultimate base class for all such elements. + A :class:`.ColumnElement`, by subclassing the :class:`.CompareMixin` mixin + class, provides the ability to generate new :class:`.ClauseElement` + objects using Python expressions. This means that Python operators + such as ``==``, ``!=`` and ``<`` are overloaded to mimic SQL operations, + and allow the construction of :class:`.ColumnElement` constructs which + are composed from other, more fundamental :class:`.ColumnElement` + objects. For example, two :class:`.ColumnClause` objects can be added + together with the addition operator ``+`` to produce + a :class:`.BinaryExpression`. + Both :class:`.ColumnClause` and :class:`.BinaryExpression` are subclasses + of :class:`.ColumnElement`:: + + >>> from sqlalchemy.sql import column + >>> column('a') + column('b') + <sqlalchemy.sql.expression.BinaryExpression object at 0x101029dd0> + >>> print column('a') + column('b') + a + b + :class:`.ColumnElement` supports the ability to be a *proxy* element, which indicates that the :class:`.ColumnElement` may be associated with a :class:`.Selectable` which was derived from another :class:`.Selectable`. An example of a "derived" :class:`.Selectable` is an :class:`.Alias` of a - :class:`~sqlalchemy.schema.Table`. + :class:`~sqlalchemy.schema.Table`. For the ambitious, an in-depth + discussion of this concept can be found at + `Expression Transformations <http://techspot.zzzeek.org/2008/01/23/expression-transformations/>`_. - A :class:`.ColumnElement`, by subclassing the :class:`CompareMixin` mixin - class, provides the ability to generate new :class:`.ClauseElement` - objects using Python expressions. See the :class:`CompareMixin` - docstring for more details. """ @@ -2420,6 +2442,19 @@ class FromClause(Selectable): """Represent an element that can be used within the ``FROM`` clause of a ``SELECT`` statement. + The most common forms of :class:`.FromClause` are the + :class:`.Table` and the :func:`.select` constructs. Key + features common to all :class:`.FromClause` objects include: + + * a :attr:`.c` collection, which provides per-name access to a collection + of :class:`.ColumnElement` objects. + * a :attr:`.primary_key` attribute, which is a collection of all those + :class:`.ColumnElement` objects that indicate the ``primary_key`` flag. + * Methods to generate various derivations of a "from" clause, including + :meth:`.FromClause.alias`, :meth:`.FromClause.join`, + :meth:`.FromClause.select`. + + """ __visit_name__ = 'fromclause' named_with_column = False @@ -2603,8 +2638,16 @@ class FromClause(Selectable): @_memoized_property def columns(self): - """Return the collection of Column objects contained by this - FromClause.""" + """A named-based collection of :class:`.ColumnElement` objects + maintained by this :class:`.FromClause`. + + The :attr:`.columns`, or :attr:`.c` collection, is the gateway + to the construction of SQL expressions using table-bound or + other selectable-bound columns:: + + select([mytable]).where(mytable.c.somecolumn == 5) + + """ if '_columns' not in self.__dict__: self._init_collections() @@ -2629,7 +2672,8 @@ class FromClause(Selectable): self._populate_column_collection() return self.foreign_keys - c = property(attrgetter('columns')) + c = property(attrgetter('columns'), + doc="An alias for the :attr:`.columns` attribute.") _select_iterable = property(attrgetter('columns')) def _init_collections(self): @@ -3447,7 +3491,19 @@ class UnaryExpression(ColumnElement): class BinaryExpression(ColumnElement): - """Represent an expression that is ``LEFT <operator> RIGHT``.""" + """Represent an expression that is ``LEFT <operator> RIGHT``. + + A :class:`.BinaryExpression` is generated automatically + whenever two objects that subclass the :class:`.CompareMixin` + mixin are used in a Python binary expresion:: + + >>> from sqlalchemy.sql import column + >>> column('a') + column('b') + <sqlalchemy.sql.expression.BinaryExpression object at 0x101029dd0> + >>> print column('a') + column('b') + a + b + + """ __visit_name__ = 'binary' |