summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm/__init__.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2007-07-27 04:08:53 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2007-07-27 04:08:53 +0000
commited4fc64bb0ac61c27bc4af32962fb129e74a36bf (patch)
treec1cf2fb7b1cafced82a8898e23d2a0bf5ced8526 /lib/sqlalchemy/orm/__init__.py
parent3a8e235af64e36b3b711df1f069d32359fe6c967 (diff)
downloadsqlalchemy-ed4fc64bb0ac61c27bc4af32962fb129e74a36bf.tar.gz
merging 0.4 branch to trunk. see CHANGES for details. 0.3 moves to maintenance branch in branches/rel_0_3.
Diffstat (limited to 'lib/sqlalchemy/orm/__init__.py')
-rw-r--r--lib/sqlalchemy/orm/__init__.py486
1 files changed, 365 insertions, 121 deletions
diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py
index 7ef2da897..1982a94f7 100644
--- a/lib/sqlalchemy/orm/__init__.py
+++ b/lib/sqlalchemy/orm/__init__.py
@@ -11,58 +11,229 @@ packages and tying operations to class properties and constructors.
from sqlalchemy import exceptions
from sqlalchemy import util as sautil
-from sqlalchemy.orm.mapper import *
+from sqlalchemy.orm.mapper import Mapper, object_mapper, class_mapper, mapper_registry
+from sqlalchemy.orm.interfaces import SynonymProperty, MapperExtension, EXT_PASS, ExtensionOption, PropComparator
+from sqlalchemy.orm.properties import PropertyLoader, ColumnProperty, CompositeProperty, BackRef
from sqlalchemy.orm import mapper as mapperlib
+from sqlalchemy.orm import collections, strategies
from sqlalchemy.orm.query import Query
from sqlalchemy.orm.util import polymorphic_union
-from sqlalchemy.orm import properties, strategies, interfaces
from sqlalchemy.orm.session import Session as create_session
from sqlalchemy.orm.session import object_session, attribute_manager
-__all__ = ['relation', 'column_property', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer', 'extension',
- 'mapper', 'clear_mappers', 'compile_mappers', 'clear_mapper', 'class_mapper', 'object_mapper', 'MapperExtension', 'Query',
- 'cascade_mappers', 'polymorphic_union', 'create_session', 'synonym', 'contains_alias', 'contains_eager', 'EXT_PASS', 'object_session'
- ]
+__all__ = ['relation', 'column_property', 'composite', 'backref', 'eagerload',
+ 'eagerload_all', 'lazyload', 'noload', 'deferred', 'defer', 'undefer',
+ 'undefer_group', 'extension', 'mapper', 'clear_mappers',
+ 'compile_mappers', 'class_mapper', 'object_mapper',
+ 'MapperExtension', 'Query', 'polymorphic_union', 'create_session',
+ 'synonym', 'contains_alias', 'contains_eager', 'EXT_PASS',
+ 'object_session', 'PropComparator'
+ ]
-def relation(*args, **kwargs):
+def relation(argument, secondary=None, **kwargs):
"""Provide a relationship of a primary Mapper to a secondary Mapper.
- This corresponds to a parent-child or associative table relationship.
+ This corresponds to a parent-child or associative table relationship.
+ The constructed class is an instance of [sqlalchemy.orm.properties#PropertyLoader].
+
+ argument
+ a class or Mapper instance, representing the target of the relation.
+
+ secondary
+ for a many-to-many relationship, specifies the intermediary table. The
+ ``secondary`` keyword argument should generally only be used for a table
+ that is not otherwise expressed in any class mapping. In particular,
+ using the Association Object Pattern is
+ generally mutually exclusive against using the ``secondary`` keyword
+ argument.
+
+ \**kwargs follow:
+
+ association
+ Deprecated; as of version 0.3.0 the association keyword is synonomous
+ with applying the "all, delete-orphan" cascade to a "one-to-many"
+ relationship. SA can now automatically reconcile a "delete" and
+ "insert" operation of two objects with the same "identity" in a flush()
+ operation into a single "update" statement, which is the pattern that
+ "association" used to indicate.
+
+ backref
+ indicates the name of a property to be placed on the related mapper's
+ class that will handle this relationship in the other direction,
+ including synchronizing the object attributes on both sides of the
+ relation. Can also point to a ``backref()`` construct for more
+ configurability.
+
+ cascade
+ a string list of cascade rules which determines how persistence
+ operations should be "cascaded" from parent to child.
+
+ collection_class
+ a class or function that returns a new list-holding object. will be
+ used in place of a plain list for storing elements.
+
+ foreign_keys
+ a list of columns which are to be used as "foreign key" columns.
+ this parameter should be used in conjunction with explicit
+ ``primaryjoin`` and ``secondaryjoin`` (if needed) arguments, and the
+ columns within the ``foreign_keys`` list should be present within
+ those join conditions. Normally, ``relation()`` will inspect the
+ columns within the join conditions to determine which columns are
+ the "foreign key" columns, based on information in the ``Table``
+ metadata. Use this argument when no ForeignKey's are present in the
+ join condition, or to override the table-defined foreign keys.
+
+ foreignkey
+ deprecated. use the ``foreign_keys`` argument for foreign key
+ specification, or ``remote_side`` for "directional" logic.
+
+ lazy=True
+ specifies how the related items should be loaded. a value of True
+ indicates they should be loaded lazily when the property is first
+ accessed. A value of False indicates they should be loaded by joining
+ against the parent object query, so parent and child are loaded in one
+ round trip (i.e. eagerly). A value of None indicates the related items
+ are not loaded by the mapper in any case; the application will manually
+ insert items into the list in some other way. In all cases, items added
+ or removed to the parent object's collection (or scalar attribute) will
+ cause the appropriate updates and deletes upon flush(), i.e. this
+ option only affects load operations, not save operations.
+
+ order_by
+ indicates the ordering that should be applied when loading these items.
+
+ passive_deletes=False
+ Indicates if lazy-loaders should not be executed during the ``flush()``
+ process, which normally occurs in order to locate all existing child
+ items when a parent item is to be deleted. Setting this flag to True is
+ appropriate when ``ON DELETE CASCADE`` rules have been set up on the
+ actual tables so that the database may handle cascading deletes
+ automatically. This strategy is useful particularly for handling the
+ deletion of objects that have very large (and/or deep) child-object
+ collections.
+
+ post_update
+ this indicates that the relationship should be handled by a second
+ UPDATE statement after an INSERT or before a DELETE. Currently, it also
+ will issue an UPDATE after the instance was UPDATEd as well, although
+ this technically should be improved. This flag is used to handle saving
+ bi-directional dependencies between two individual rows (i.e. each row
+ references the other), where it would otherwise be impossible to INSERT
+ or DELETE both rows fully since one row exists before the other. Use
+ this flag when a particular mapping arrangement will incur two rows
+ that are dependent on each other, such as a table that has a
+ one-to-many relationship to a set of child rows, and also has a column
+ that references a single child row within that list (i.e. both tables
+ contain a foreign key to each other). If a ``flush()`` operation returns
+ an error that a "cyclical dependency" was detected, this is a cue that
+ you might want to use ``post_update`` to "break" the cycle.
+
+ primaryjoin
+ a ClauseElement that will be used as the primary join of this child
+ object against the parent object, or in a many-to-many relationship the
+ join of the primary object to the association table. By default, this
+ value is computed based on the foreign key relationships of the parent
+ and child tables (or association table).
+
+ private=False
+ deprecated. setting ``private=True`` is the equivalent of setting
+ ``cascade="all, delete-orphan"``, and indicates the lifecycle of child
+ objects should be contained within that of the parent.
+
+ remote_side
+ used for self-referential relationships, indicates the column or list
+ of columns that form the "remote side" of the relationship.
+
+ secondaryjoin
+ a ClauseElement that will be used as the join of an association table
+ to the child object. By default, this value is computed based on the
+ foreign key relationships of the association and child tables.
+
+ uselist=(True|False)
+ a boolean that indicates if this property should be loaded as a list or
+ a scalar. In most cases, this value is determined automatically by
+ ``relation()``, based on the type and direction of the relationship - one
+ to many forms a list, many to one forms a scalar, many to many is a
+ list. If a scalar is desired where normally a list would be present,
+ such as a bi-directional one-to-one relationship, set uselist to False.
+
+ viewonly=False
+ when set to True, the relation is used only for loading objects within
+ the relationship, and has no effect on the unit-of-work flush process.
+ Relations with viewonly can specify any kind of join conditions to
+ provide additional views of related objects onto a parent object. Note
+ that the functionality of a viewonly relationship has its limits -
+ complicated join conditions may not compile into eager or lazy loaders
+ properly. If this is the case, use an alternative method.
+
"""
- if len(args) > 1 and isinstance(args[0], type):
- raise exceptions.ArgumentError("relation(class, table, **kwargs) is deprecated. Please use relation(class, **kwargs) or relation(mapper, **kwargs).")
- return _relation_loader(*args, **kwargs)
+ return PropertyLoader(argument, secondary=secondary, **kwargs)
+
+# return _relation_loader(argument, secondary=secondary, **kwargs)
+
+#def _relation_loader(mapper, secondary=None, primaryjoin=None, secondaryjoin=None, lazy=True, **kwargs):
def column_property(*args, **kwargs):
"""Provide a column-level property for use with a Mapper.
+
+ Column-based properties can normally be applied to the mapper's
+ ``properties`` dictionary using the ``schema.Column`` element directly.
+ Use this function when the given column is not directly present within
+ the mapper's selectable; examples include SQL expressions, functions,
+ and scalar SELECT queries.
+
+ Columns that arent present in the mapper's selectable won't be persisted
+ by the mapper and are effectively "read-only" attributes.
+
+ \*cols
+ list of Column objects to be mapped.
- Normally, custom column-level properties that represent columns
- directly or indirectly present within the mapped selectable
- can just be added to the ``properties`` dictionary directly,
- in which case this function's usage is not necessary.
-
- In the case of a ``ColumnElement`` directly present within the
- ``properties`` dictionary, the given column is converted to be the exact column
- located within the mapped selectable, in the case that the mapped selectable
- is not the exact parent selectable of the given column, but shares a common
- base table relationship with that column.
+ group
+ a group name for this property when marked as deferred.
+
+ deferred
+ when True, the column property is "deferred", meaning that
+ it does not load immediately, and is instead loaded when the
+ attribute is first accessed on an instance. See also
+ [sqlalchemy.orm#deferred()].
+
+ """
- Use this function when the column expression being added does not
- correspond to any single column within the mapped selectable,
- such as a labeled function or scalar-returning subquery, to force the element
- to become a mapped property regardless of it not being present within the
- mapped selectable.
+ return ColumnProperty(*args, **kwargs)
+
+def composite(class_, *cols, **kwargs):
+ """Return a composite column-based property for use with a Mapper.
+
+ This is very much like a column-based property except the given class
+ is used to construct values composed of one or more columns. The class must
+ implement a constructor with positional arguments matching the order of
+ columns given, as well as a __colset__() method which returns its attributes
+ in column order.
- Note that persistence of instances is driven from the collection of columns
- within the mapped selectable, so column properties attached to a Mapper which have
- no direct correspondence to the mapped selectable will effectively be non-persisted
- attributes.
+ class\_
+ the "composite type" class.
+
+ \*cols
+ list of Column objects to be mapped.
+
+ group
+ a group name for this property when marked as deferred.
+
+ deferred
+ when True, the column property is "deferred", meaning that
+ it does not load immediately, and is instead loaded when the
+ attribute is first accessed on an instance. See also
+ [sqlalchemy.orm#deferred()].
+
+ comparator
+ an optional instance of [sqlalchemy.orm#PropComparator] which
+ provides SQL expression generation functions for this composite
+ type.
"""
- return properties.ColumnProperty(*args, **kwargs)
-def _relation_loader(mapper, secondary=None, primaryjoin=None, secondaryjoin=None, lazy=True, **kwargs):
- return properties.PropertyLoader(mapper, secondary, primaryjoin, secondaryjoin, lazy=lazy, **kwargs)
+ return CompositeProperty(class_, *cols, **kwargs)
+
def backref(name, **kwargs):
"""Create a BackRef object with explicit arguments, which are the same arguments one
@@ -72,7 +243,7 @@ def backref(name, **kwargs):
place of a string argument.
"""
- return properties.BackRef(name, **kwargs)
+ return BackRef(name, **kwargs)
def deferred(*columns, **kwargs):
"""Return a ``DeferredColumnProperty``, which indicates this
@@ -82,15 +253,141 @@ def deferred(*columns, **kwargs):
Used with the `properties` dictionary sent to ``mapper()``.
"""
- return properties.ColumnProperty(deferred=True, *columns, **kwargs)
-
-def mapper(class_, table=None, *args, **params):
- """Return a new ``Mapper`` object.
-
- See the ``Mapper`` class for a description of arguments.
+ return ColumnProperty(deferred=True, *columns, **kwargs)
+
+def mapper(class_, local_table=None, *args, **params):
+ """Return a new [sqlalchemy.orm#Mapper] object.
+
+ class\_
+ The class to be mapped.
+
+ local_table
+ The table to which the class is mapped, or None if this
+ mapper inherits from another mapper using concrete table
+ inheritance.
+
+ entity_name
+ A name to be associated with the `class`, to allow alternate
+ mappings for a single class.
+
+ always_refresh
+ If True, all query operations for this mapped class will
+ overwrite all data within object instances that already
+ exist within the session, erasing any in-memory changes with
+ whatever information was loaded from the database. Usage
+ of this flag is highly discouraged; as an alternative,
+ see the method `populate_existing()` on [sqlalchemy.orm.query#Query].
+
+ allow_column_override
+ If True, allows the usage of a ``relation()`` which has the
+ same name as a column in the mapped table. The table column
+ will no longer be mapped.
+
+ allow_null_pks
+ Indicates that composite primary keys where one or more (but
+ not all) columns contain NULL is a valid primary key.
+ Primary keys which contain NULL values usually indicate that
+ a result row does not contain an entity and should be
+ skipped.
+
+ batch
+ Indicates that save operations of multiple entities can be
+ batched together for efficiency. setting to False indicates
+ that an instance will be fully saved before saving the next
+ instance, which includes inserting/updating all table rows
+ corresponding to the entity as well as calling all
+ ``MapperExtension`` methods corresponding to the save
+ operation.
+
+ column_prefix
+ A string which will be prepended to the `key` name of all
+ Columns when creating column-based properties from the given
+ Table. Does not affect explicitly specified column-based
+ properties
+
+ concrete
+ If True, indicates this mapper should use concrete table
+ inheritance with its parent mapper.
+
+ extension
+ A [sqlalchemy.orm#MapperExtension] instance or list of
+ ``MapperExtension`` instances which will be applied to all
+ operations by this ``Mapper``.
+
+ inherits
+ Another ``Mapper`` for which this ``Mapper`` will have an
+ inheritance relationship with.
+
+ inherit_condition
+ For joined table inheritance, a SQL expression (constructed
+ ``ClauseElement``) which will define how the two tables are
+ joined; defaults to a natural join between the two tables.
+
+ order_by
+ A single ``Column`` or list of ``Columns`` for which
+ selection operations should use as the default ordering for
+ entities. Defaults to the OID/ROWID of the table if any, or
+ the first primary key column of the table.
+
+ non_primary
+ Construct a ``Mapper`` that will define only the selection
+ of instances, not their persistence. Any number of non_primary
+ mappers may be created for a particular class.
+
+ polymorphic_on
+ Used with mappers in an inheritance relationship, a ``Column``
+ which will identify the class/mapper combination to be used
+ with a particular row. requires the polymorphic_identity
+ value to be set for all mappers in the inheritance
+ hierarchy.
+
+ _polymorphic_map
+ Used internally to propigate the full map of polymorphic
+ identifiers to surrogate mappers.
+
+ polymorphic_identity
+ A value which will be stored in the Column denoted by
+ polymorphic_on, corresponding to the *class identity* of
+ this mapper.
+
+ polymorphic_fetch
+ specifies how subclasses mapped through joined-table
+ inheritance will be fetched. options are 'union',
+ 'select', and 'deferred'. if the select_table argument
+ is present, defaults to 'union', otherwise defaults to
+ 'select'.
+
+ properties
+ A dictionary mapping the string names of object attributes
+ to ``MapperProperty`` instances, which define the
+ persistence behavior of that attribute. Note that the
+ columns in the mapped table are automatically converted into
+ ``ColumnProperty`` instances based on the `key` property of
+ each ``Column`` (although they can be overridden using this
+ dictionary).
+
+ primary_key
+ A list of ``Column`` objects which define the *primary key*
+ to be used against this mapper's selectable unit. This is
+ normally simply the primary key of the `local_table`, but
+ can be overridden here.
+
+ select_table
+ A [sqlalchemy.schema#Table] or any [sqlalchemy.sql#Selectable]
+ which will be used to select instances of this mapper's class.
+ usually used to provide polymorphic loading among several
+ classes in an inheritance hierarchy.
+
+ version_id_col
+ A ``Column`` which must have an integer type that will be
+ used to keep a running *version id* of mapped entities in
+ the database. this is used during save operations to ensure
+ that no other thread or process has updated the instance
+ during the lifetime of the entity, else a
+ ``ConcurrentModificationError`` exception is thrown.
"""
- return Mapper(class_, table, *args, **params)
+ return Mapper(class_, local_table, *args, **params)
def synonym(name, proxy=False):
"""Set up `name` as a synonym to another ``MapperProperty``.
@@ -98,7 +395,7 @@ def synonym(name, proxy=False):
Used with the `properties` dictionary sent to ``mapper()``.
"""
- return interfaces.SynonymProperty(name, proxy=proxy)
+ return SynonymProperty(name, proxy=proxy)
def compile_mappers():
"""Compile all mappers that have been defined.
@@ -120,32 +417,13 @@ def clear_mappers():
mapperlib._COMPILE_MUTEX.acquire()
try:
for mapper in mapper_registry.values():
- attribute_manager.reset_class_managed(mapper.class_)
- if hasattr(mapper.class_, 'c'):
- del mapper.class_.c
+ mapper.dispose()
mapper_registry.clear()
# TODO: either dont use ArgSingleton, or
# find a way to clear only ClassKey instances from it
sautil.ArgSingleton.instances.clear()
finally:
mapperlib._COMPILE_MUTEX.release()
-
-def clear_mapper(m):
- """Remove the given mapper from the storage of mappers.
-
- When a new mapper is created for the previous mapper's class, it
- will be used as that classes' new primary mapper.
- """
-
- mapperlib._COMPILE_MUTEX.acquire()
- try:
- del mapper_registry[m.class_key]
- attribute_manager.reset_class_managed(m.class_)
- if hasattr(m.class_, 'c'):
- del m.class_.c
- m.class_key.dispose()
- finally:
- mapperlib._COMPILE_MUTEX.release()
def extension(ext):
"""Return a ``MapperOption`` that will insert the given
@@ -166,6 +444,22 @@ def eagerload(name):
return strategies.EagerLazyOption(name, lazy=False)
+def eagerload_all(name):
+ """Return a ``MapperOption`` that will convert all
+ properties along the given dot-separated path into an
+ eager load.
+
+ e.g::
+ query.options(eagerload_all('orders.items.keywords'))...
+
+ will set all of 'orders', 'orders.items', and 'orders.items.keywords'
+ to load in one eager load.
+
+ Used with ``query.options()``.
+ """
+
+ return strategies.EagerLazyOption(name, lazy=False, chained=True)
+
def lazyload(name):
"""Return a ``MapperOption`` that will convert the property of the
given name into a lazy load.
@@ -175,6 +469,9 @@ def lazyload(name):
return strategies.EagerLazyOption(name, lazy=True)
+def fetchmode(name, type):
+ return strategies.FetchModeOption(name, type)
+
def noload(name):
"""Return a ``MapperOption`` that will convert the property of the
given name into a non-load.
@@ -250,64 +547,11 @@ def undefer(name):
return strategies.DeferredOption(name, defer=False)
+def undefer_group(name):
+ """Return a ``MapperOption`` that will convert the given
+ group of deferred column properties into a non-deferred (regular column) load.
-def cascade_mappers(*classes_or_mappers):
- """Attempt to create a series of ``relations()`` between mappers
- automatically, via introspecting the foreign key relationships of
- the underlying tables.
-
- Given a list of classes and/or mappers, identify the foreign key
- relationships between the given mappers or corresponding class
- mappers, and create ``relation()`` objects representing those
- relationships, including a backreference. Attempt to find the
- *secondary* table in a many-to-many relationship as well.
-
- The names of the relations will be a lowercase version of the
- related class. In the case of one-to-many or many-to-many, the
- name will be *pluralized*, which currently is based on the English
- language (i.e. an 's' or 'es' added to it).
-
- NOTE: this method usually works poorly, and its usage is generally
- not advised.
+ Used with ``query.options()``.
"""
-
- table_to_mapper = {}
- for item in classes_or_mappers:
- if isinstance(item, Mapper):
- m = item
- else:
- klass = item
- m = class_mapper(klass)
- table_to_mapper[m.mapped_table] = m
-
- def pluralize(name):
- # oh crap, do we need locale stuff now
- if name[-1] == 's':
- return name + "es"
- else:
- return name + "s"
-
- for table,mapper in table_to_mapper.iteritems():
- for fk in table.foreign_keys:
- if fk.column.table is table:
- continue
- secondary = None
- try:
- m2 = table_to_mapper[fk.column.table]
- except KeyError:
- if len(fk.column.table.primary_key):
- continue
- for sfk in fk.column.table.foreign_keys:
- if sfk.column.table is table:
- continue
- m2 = table_to_mapper.get(sfk.column.table)
- secondary = fk.column.table
- if m2 is None:
- continue
- if secondary:
- propname = pluralize(m2.class_.__name__.lower())
- propname2 = pluralize(mapper.class_.__name__.lower())
- else:
- propname = m2.class_.__name__.lower()
- propname2 = pluralize(mapper.class_.__name__.lower())
- mapper.add_property(propname, relation(m2, secondary=secondary, backref=propname2))
+ return strategies.UndeferGroupOption(name)
+