]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
docs: [ticket:345], [ticket:356], [ticket:48], [ticket:403], [ticket:394],
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 21 Jan 2007 00:09:25 +0000 (00:09 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 21 Jan 2007 00:09:25 +0000 (00:09 +0000)
cleanup/completion of keyword arg documentation for create_engine(), mapper(), and
relation()

doc/build/content/adv_datamapping.txt
doc/build/content/dbengine.txt
doc/build/content/metadata.txt
doc/build/content/sqlconstruction.txt
doc/docs.css
examples/collections/large_collection.py
lib/sqlalchemy/orm/mapper.py
test/orm/mapper.py
test/orm/unitofwork.py

index de10b7b20fe85fe96d73bab99587ac256bb77683..e206686503b7c4eb7e8f345e48b74ed17b6c3584 100644 (file)
@@ -74,7 +74,7 @@ A common request is the ability to create custom class properties that override
     class MyClass(object):
         def _set_email(self, email):
            self._email = email
-        def _get_email(self, email):
+        def _get_email(self):
            return self._email
         email = property(_get_email, _set_email)
     
@@ -90,7 +90,7 @@ It is also possible to route the the `select_by` and `get_by` functions on `Quer
     mapper(MyClass, mytable, properties = {
         # map the '_email' attribute to the "email" column
         # on the table
-        '_email': mytable.c.email
+        '_email': mytable.c.email,
 
         # make a synonym 'email'
         'email' : synonym('_email')
@@ -99,7 +99,7 @@ It is also possible to route the the `select_by` and `get_by` functions on `Quer
     # now you can select_by(email)
     result = session.query(MyClass).select_by(email='john@smith.com')
 
-Synonym can be established with the flag "proxy=True", to create a class-level proxy to the actual property:
+Synonym can be established with the flag "proxy=True", to create a class-level proxy to the actual property.  This has the effect of creating a fully functional synonym on class instances:
 
     {python}
     mapper(MyClass, mytable, properties = {
@@ -112,8 +112,6 @@ Synonym can be established with the flag "proxy=True", to create a class-level p
     
     >>> x._email
     'john@doe.com'
-    
-The `synonym` keyword is currently an [Alpha Feature][alpha_api].
 
 #### Custom List Classes {@name=customlist}
 
@@ -154,7 +152,7 @@ In this example, a class `MyClass` is defined, which is associated with a parent
 
 
 #### Custom Join Conditions {@name=customjoin}
-        
+
 When creating relations on a mapper, most examples so far have illustrated the mapper and relationship joining up based on the foreign keys of the tables they represent.  in fact, this "automatic" inspection can be completely circumvented using the `primaryjoin` and `secondaryjoin` arguments to `relation`, as in this example which creates a User object which has a relationship to all of its Addresses which are in Boston:
 
     {python}
@@ -268,7 +266,7 @@ So there are several techniques that can be used individually or combined togeth
 
         {python}
         mapper(MyClass, table, properties=relation{
-            'children':relation(MyObj, lazy=None)
+            'children':relation(MyOtherClass, lazy=None)
         })
 
 * To load child objects, just use a query:
@@ -281,17 +279,26 @@ So there are several techniques that can be used individually or combined togeth
                 """locate a subset of the members associated with this Organization"""
                 return object_session(self).query(Member).select(and_(member_table.c.name.like(criterion), org_table.c.org_id==self.org_id), from_obj=[org_table.join(member_table)])
     
-* Use `passive_updates=True` to disable child object loading on a DELETE operation (noload also accomplishes this)
-* Use "ON DELETE (CASCADE|SET NULL)" on your database to automatically cascade deletes to child objects (this is the best way to do it)
+* Use `passive_deletes=True` to disable child object loading on a DELETE operation, in conjunction with "ON DELETE (CASCADE|SET NULL)" on your database to automatically cascade deletes to child objects.   Note that "ON DELETE" is not supported on SQLite, and requires `InnoDB` tables when using MySQL:
 
         {python}
-        mytable = Table('sometable', meta,
+        mytable = Table('mytable', meta,
+            Column('id', Integer, primary_key=True),
+            )
+
+        myothertable = Table('myothertable', meta,
             Column('id', Integer, primary_key=True),
             Column('parent_id', Integer),
-            ForeignKeyConstraint(['parent_id'],['parenttable.id'], ondelete="CASCADE"),
+            ForeignKeyConstraint(['parent_id'],['mytable.id'], ondelete="CASCADE"),
             )
+
+        mmapper(MyOtherClass, myothertable)
+        
+        mapper(MyClass, mytable, properties={
+            'children':relation(MyOtherClass, passive_deletes=True)
+        })
         
-* Alternatively, you can create a simple `MapperExtension` that will issue a DELETE for child objects:
+* As an alternative to using "ON DELETE CASCADE", for very simple scenarios you can create a simple `MapperExtension` that will issue a DELETE for child objects before the parent object is deleted:
 
         {python}
         class DeleteMemberExt(MapperExtension):
@@ -302,28 +309,30 @@ So there are several techniques that can be used individually or combined togeth
             'members' : relation(Member, lazy=None, passive_deletes=True, cascade="all, delete-orphan")
         })
 
+Note that this approach is not nearly as efficient or general-purpose as "ON DELETE CASCADE", since the database itself can cascade the operation along any number of tables.
+
 The latest distribution includes an example `examples/collection/large_collection.py` which illustrates most of these techniques.    
 
 #### Relation Options {@name=relationoptions}
 
-Keyword options to the `relation` function include:
-
-* lazy=(True|False|None) - specifies how the related items should be loaded.  a value of True indicates they should be loaded 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.  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.  A relationship with lazy=None is still important; items added to the list or removed will cause the appropriate updates and deletes upon flush().  Future capabilities for lazy might also include "lazy='extra'", which would allow lazy loading of child elements one at a time, for very large collections.
-* cascade - a string list of **cascade rules** which determines how persistence operations should be "cascaded" from parent to child.  For a description of cascade rules, see [datamapping_relations_cycle](rel:datamapping_relations_lifecycle) and [unitofwork_cascade](rel:unitofwork_cascade).
-* secondary - for a many-to-many relationship, specifies the intermediary table.
-* 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).
-* 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.
-* remote_side - used for self-referential relationships, indicates the column or list of columns that form the "remote side" of the relationship.
-* foreignkey - deprecated.  As of 0.3.2, use remote_side to indicate the direction of self-referential relationships.  For ForeignKey specification, this field is only partially functional (i.e. does not work for many-to-many relationships).  a new field "foreign_keys" will be added in a future release to implement this functionality.
-* uselist - a boolean that indicates if this property should be loaded as a list or a scalar.  In most cases, this value is determined 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.
-* private - 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.   See the example in [datamapping_relations_cycle](rel:datamapping_relations_lifecycle).
-* 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.  See [datamapping_relations_backreferences](rel:datamapping_relations_backreferences).
-* order_by - indicates the ordering that should be applied when loading these items.  See the section [advdatamapping_orderby](rel:advdatamapping_orderby) for details.
-* 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.  See the updated example of association mappings in [datamapping_association](rel:datamapping_association).
-* post_update - this indicates that the relationship should be handled by a second UPDATE statement after an INSERT, or before a DELETE.  using this flag essentially means the relationship will not incur any "dependency" between parent and child item, as the particular foreign key relationship between them is handled by a second statement.  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.
-* viewonly=(True|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.
-* collection_class = None - a class or function that returns a new list-holding object.  will be used in place of a plain list for storing elements.
-* passive_deletes = False - if False, child instances will be loaded from the database into the current session when a parent instance is deleted so that delete cascade can be processed on those child instances, even if they were not loaded already.  When set to True, lazy loaders will not be fired off in order to process a "delete" cascade, which is appropriate if "ON DELETE" rules have been set up on the database tables themselves.
+Options which can be sent to the `relation()` function.  For arguments to `mapper()`, see [advdatamapping_mapperoptions](rel:advdatamapping_mapperoptions).
+
+* **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.  See the updated example of association mappings in [datamapping_association](rel:datamapping_association).
+* **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.  See [datamapping_relations_backreferences](rel:datamapping_relations_backreferences).
+* **cascade** - a string list of cascade rules which determines how persistence operations should be "cascaded" from parent to child. For a description of cascade rules, see [datamapping_relations_lifecycle](rel:datamapping_relations_lifecycle) and [unitofwork_cascade](rel:unitofwork_cascade).
+* **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.  See [advdatamapping_properties_customlist](rel:advdatamapping_properties_customlist).
+* **foreignkey** - deprecated for most uses.  As of 0.3.2, use `remote_side` to indicate the direction of self-referential relationships.  For `ForeignKey` specification, this field is only partially functional (i.e. does not work for many-to-many relationships), but is needed in rare cases when both sides of the primaryjoin condition contain foreign keys to either side of the relationship, and `relation()` cannot determine which side of the relationship is "foreign".  a new field `foreign_keys` will be added in a future release to fully implement this functionality.
+* **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.  See the section [advdatamapping_orderby](rel:advdatamapping_orderby) for details.
+* **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.  See the example in [advdatamapping_properties_working](rel:advdatamapping_properties_working).
+* **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.   See the example in [datamapping_relations_lifecycle](rel:datamapping_relations_lifecycle).
+* **remote_side** - used for self-referential relationships, indicates the column or list of columns that form the "remote side" of the relationship.  See the examples in [advdatamapping_selfreferential](rel:advdatamapping_selfreferential).
+* **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](rel:datamapping_association) is generally mutually exclusive against using the `secondary` keyword argument.
+* **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, such as those described in [advdatamapping_properties_working](rel:advdatamapping_properties_working), [advdatamapping_resultset](rel:advdatamapping_resultset), or [advdatamapping_selects](rel:advdatamapping_selects).
 
 ### Controlling Ordering {@name=orderby}
 
@@ -641,7 +650,7 @@ example:
     # select from the alternate mapper
     session.query(User, entity_name='alt').select()
 
-### Self Referential Mappers {@name=recursive}
+### Self Referential Mappers {@name=selfreferential}
 
 A self-referential mapper is a mapper that is designed to operate with an *adjacency list* table.  This is a table that contains one or more foreign keys back to itself, and is usually used to create hierarchical tree structures.  SQLAlchemy's default model of saving items based on table dependencies is not sufficient in this case, as an adjacency list table introduces dependencies between individual rows.  Fortunately, SQLAlchemy will automatically detect a self-referential mapper and do the extra lifting to make it work.  
 
@@ -668,14 +677,6 @@ A self-referential mapper is a mapper that is designed to operate with an *adjac
             }
         )
         
-    # or, specify the circular relationship after establishing the original mapper:
-    mymapper = mapper(TreeNode, trees)
-    
-    mymapper.add_property('children', relation(
-                            mymapper, 
-                            cascade="all"
-                         ))
-        
 This kind of mapper goes through a lot of extra effort when saving and deleting items, to determine the correct dependency graph of nodes within the tree.
     
 A self-referential mapper where there is more than one relationship on the table requires that all join conditions be explicitly spelled out.  Below is a self-referring table that contains a "parent_node_id" column to reference parent/child relationships, and a "root_node_id" column which points child nodes back to the ultimate root node:
@@ -737,7 +738,8 @@ Take any result set and feed it into a Query to produce objects.  Multiple mappe
     mapper(Address, addresses_table)
 
     # select users and addresses in one query
-    s = select([users_table, addresses_table], users_table.c.user_id==addresses_table.c.user_id)
+    # use_labels is so that the user_id column in both tables are distinguished
+    s = select([users_table, addresses_table], users_table.c.user_id==addresses_table.c.user_id, use_labels=True)
 
     # execute it, and process the results with the User mapper, chained to the Address mapper
     r = session.query(User).instances(s.execute(), class_mapper(Address))
@@ -747,15 +749,76 @@ Take any result set and feed it into a Query to produce objects.  Multiple mappe
         user = r[0]
         address = r[1]
 
-### Mapper Arguments {@name=arguments}
+#### Combining Eager Loads with Result Set Mappings
 
-Other arguments not covered above include:
+When result-set mapping is used with a particular Mapper, SQLAlchemy has no access to the query being used and therefore has no way of tacking on its own `LEFT OUTER JOIN` conditions that are normally used to eager load relationships.  If the query being constructed is created in such a way that it returns rows not just from a parent table (or tables) but also returns rows from child tables, the result-set mapping can be notified as to which additional properties are contained within the result set.  This is done using the `contains_eager()` query option, which specifies the name of the relationship to be eagerly loaded, and optionally a **decorator function** that can translate aliased column names when results are received.
 
-* select\_table=None - often used with polymorphic mappers, this is a `Selectable` which will take the place of the `Mapper`'s main table argument when performing queries.
-* version\_id\_col=None - an integer-holding Column object that will be assigned an incrementing
-counter, which is added to the WHERE clause used by UPDATE and DELETE statements.  The matching row
-count returned by the database is compared to the expected row count, and an exception is raised if they dont match.  This is a basic "optimistic concurrency" check.  Without the version id column, SQLAlchemy still compares the updated rowcount.
-* always\_refresh=False - this option will cause the mapper to refresh all the attributes of all objects loaded by select/get statements, regardless of if they already exist in the current session.  this includes all lazy- and eager-loaded relationship attributes, and will also overwrite any changes made to attributes on the column. 
+    {python}
+    # mapping is the users->addresses mapping
+    mapper(User, users_table, properties={
+        'addresses':relation(Address, addresses_table, lazy=False)
+    })
+    
+    # define a query on USERS with an outer join to ADDRESSES
+    statement = users_table.outerjoin(addresses_table).select(use_labels=True)
+    
+    # construct a Query object which expects the "addresses" results 
+    query = session.query(User).options(contains_eager('addresses'))
+    
+    # get results normally
+    r = query.instances(statement.execute())
+
+It is often the case with large queries that some of the tables within the query need to be aliased in order to distinguish them from other occurences of the same table within the query.  A query that attempts to add eagerly loaded child items will often have this condition.  Therefore with a little more effort a decorator function can be used to produce translated rows (in the form of a dictionary which accepts Column instances), in the case that aliasing is used for the relationship tables.
+
+    {python}
+    # use an alias of the addresses table
+    adalias = addresses_table.alias('adalias')
+    
+    # define a query on USERS with an outer join to adalias
+    statement = users_table.outerjoin(adalias).select(use_labels=True)
+
+    # define row-translation function.  this should return 
+    # a dictionary-like object that will receive Column instances from the normally expected
+    # table (i.e. addreses_table), and produce results from the actual result set
+    def adtranslator(row):
+        d = {}
+        for c in addresses_table.columns:
+            d[c] = row[adalias.corresponding_column(addresses_table)]
+        return d
+        
+    # construct a Query object which expects the "addresses" results 
+    query = session.query(User).options(contains_eager('addresses', decorator=adtranslator))
+
+    # get results normally
+    {sql}r = query.instances(statement.execute())
+    SELECT users.user_id AS users_user_id, users.user_name AS users_user_name, adalias.address_id AS adalias_address_id, 
+    adalias.user_id AS adalias_user_id, adalias.email_address AS adalias_email_address, (...other columns...)
+    FROM users LEFT OUTER JOIN email_addresses AS adalias ON users.user_id = adalias.user_id
+
+### Mapper Options {@name=mapperoptions}
+
+Options which can be sent to the `mapper()` function.  For arguments to `relation()`, see [advdatamapping_properties_relationoptions](rel:advdatamapping_properties_relationoptions).
+
+* **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=False** - 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.
+* **always_refresh=False** - 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.
+* **batch=True** - when False, indicates that when a mapper is persisting a list of instances, each instance will be fully saved to the database before moving onto the next instance. Normally, inserts and updates are batched together per-table, such as for an inheriting mapping that spans multiple tables. This flag is for rare circumstances where custom `MapperExtension` objects are used to attach logic to `before_insert()`, `before_update()`, etc., and the user-defined logic requires that the full persistence of each instance must be completed before moving onto the next (such as logic which queries the tables for the most recent ID). Note that this flag has a significant impact on the efficiency of a large 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. Setting `column_prefix='_'` is equivalent to defining all column-based properties as `_columnname=table.c.columnname`. See [advdatamapping_properties_colname](rel:advdatamapping_properties_colname) for information on overriding column-based attribute names.
+* **concrete** - if True, indicates this mapper should use concrete table inheritance with its parent mapper.  Requires `inherits` to be set.
+* **entity_name** - defines this mapping as local to a particular class of entities within a single class.  Allows alternate persistence mappings for a single class. See [advdatamapping_multiple](rel:advdatamapping_multiple).
+* **extension** - a MapperExtension instance or list of MapperExtension instances which will be applied to all operations by this Mapper. See [advdatamapping_extending](rel:advdatamapping_extending).
+* **inherits** - another Mapper or class for which this Mapper will have an inheritance relationship with. See the examples in [advdatamapping_inheritance](rel:advdatamapping_inheritance).
+* **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.
+* **non_primary=False** - if True, construct a Mapper that will define only the selection of instances, not their persistence. It essentially creates a mapper that can be used for querying but does not define how instances of the class are stored. A non_primary mapper is always created after a regular primary mapper has already been created for the class. To use one, send it in place of the class argument when creating a query, such as `session.query(somemapper)`. Note that it is usually invalid to define additional relationships on a non_primary mapper as they will conflict with those of the primary. See [advdatamapping_multiple](rel:advdatamapping_multiple).
+* **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. See [advdatamapping_orderby](rel:advdatamapping_orderby).
+* **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_identity** - a value which will be stored in the Column denoted by `polymorphic_on`, corresponding to the "class identity" of this mapper.  See [advdatamapping_inheritance](rel:advdatamapping_inheritance).
+* **primary_key** - a list of Column objects which define the "primary key" to be used against this mapper's selectable unit. The mapper normally determines these automatically from the given `local_table` of the mapper combined against any inherited tables. When this argument is specified, the primary keys of the mapped table if any are disregarded in place of the columns given. This can be used to provide primary key identity to a table that has no PKs defined at the schema level, or to modify what defines "identity" for a particular table.
+* **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).
+* **select_table** - used with polymorphic mappers, this is a `Selectable` which will take the place of the `Mapper`'s main table argument when
+performing queries.
+* **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.
 
 ### Extending Mapper {@name=extending}
 
index c11bf6df7e3382eb20d3ed5053905359a4a8ea5d..60a3fefd8ce71a92230adb73c64d5492b4d2a972 100644 (file)
@@ -89,10 +89,15 @@ Keyword options can also be specified to `create_engine()`, following the string
     {python}
     db = create_engine('postgres://...', encoding='latin1', echo=True, module=psycopg1)
 
-Options that can be specified include the following:
+A list of all standard options, as well as several that are used by particular database dialects, is as follows:
 
-* poolclass=None : a `sqlalchemy.pool.Pool` subclass (or duck-typed equivalent) that will be instantated in place of the default connection pool.
-* pool=None : an actual pool instance.  Note that an already-constructed pool should already know how to create database connections, so this option supercedes any other connect options specified.  Typically, it is an instance of `sqlalchemy.pool.Pool` to be used as the underlying source for connections.  For more on connection pooling, see [pooling](rel:pooling).
+* **convert_unicode=False** - if set to True, all String/character based types will convert Unicode values to raw byte values going into the database, and all raw byte values to Python Unicode coming out in result sets.  This is an engine-wide method to provide unicode conversion across the board.  For unicode conversion on a column-by-column level, use the `Unicode` column type instead, described in [types](rel:types).
+* **echo=False** - if True, the Engine will log all statements as well as a repr() of their parameter lists to the engines logger, which defaults to sys.stdout.  The `echo` attribute of `Engine` can be modified at any time to turn logging on and off.  If set to the string `"debug"`, result rows will be printed to the standard output as well.  This flag ultimately controls a Python logger; see [dbengine_logging](rel:dbengine_logging) for information on how to configure logging directly.
+* **echo_pool=False** - if True, the connection pool will log all checkouts/checkins to the logging stream, which defaults to sys.stdout.  This flag ultimately controls a Python logger; see [dbengine_logging](rel:dbengine_logging) for information on how to configure logging directly.
+* **encoding='utf-8'** - the encoding to use for all Unicode translations, both by engine-wide unicode conversion as well as the `Unicode` type object.
+* **max_overflow=10** - the number of connections to allow in connection pool "overflow", that is connections that can be opened above and beyond the initial setting, which defaults to five.  this is only used with `QueuePool`.
+* **module=None** - used by database implementations which support multiple DBAPI modules, this is a reference to a DBAPI2 module to be used instead of the engine's default module.  For Postgres, the default is psycopg2, or psycopg1 if 2 cannot be found.  For Oracle, its cx_Oracle.
+* **pool=None** - an actual pool instance.  Note that an already-constructed pool should already know how to create database connections, so this option supercedes any other connect options specified.  Typically, it is an instance of `sqlalchemy.pool.Pool` to be used as the underlying source for connections.  For more on connection pooling, see [pooling](rel:pooling).
 
 Example of a manual invocation of `pool.QueuePool` (which is the pool instance used for all databases except sqlite):
 
@@ -106,19 +111,42 @@ Example of a manual invocation of `pool.QueuePool` (which is the pool instance u
     
     engine = create_engine('mysql://', pool=pool.QueuePool(getconn, pool_size=20, max_overflow=40))
 
-* pool_size=5 : the number of connections to keep open inside the connection pool.  This used with `QueuePool` as well as `SingletonThreadPool`.
-* max_overflow=10 : the number of connections to allow in "overflow", that is connections that can be opened above and beyond the initial five.  this is only used with `QueuePool`.
-* pool_timeout=30 : number of seconds to wait before giving up on getting a connection from the pool.  This is only used with `QueuePool`.
-* pool_recycle=-1 : this setting causes the pool to recycle connections after the given number of seconds has passed.  It defaults to -1, or no timeout.  For example, setting to 3600 means connections will be recycled after one hour.  Note that MySQL in particular will disconnect automatically if no activity is detected on a connection for eight hours (although this is configurable with the MySQLDB connection itself and the  server configuration as well).
-* echo=False : if True, the Engine will log all statements as well as a repr() of their parameter lists to the engines logger, which defaults to sys.stdout.  The `echo` attribute of `Engine` can be modified at any time to turn logging on and off.  If set to the string `"debug"`, result rows will be printed to the standard output as well.
-* logger=None : a file-like object where logging output can be sent, if echo is set to True.  Newlines will not be sent with log messages.  This defaults to an internal logging object which references `sys.stdout`.
-* module=None : used by database implementations which support multiple DBAPI modules, this is a reference to a DBAPI2 module to be used instead of the engine's default module.  For Postgres, the default is psycopg2, or psycopg1 if 2 cannot be found.  For Oracle, its cx_Oracle.
-* use_ansi=True : used only by Oracle;  when False, the Oracle driver attempts to support a particular "quirk" of Oracle versions 8 and previous, that the LEFT OUTER JOIN SQL syntax is not supported, and the "Oracle join" syntax of using `&lt;column1&gt;(+)=&lt;column2&gt;` must be used in order to achieve a LEFT OUTER JOIN.  
-* threaded=True : used by cx_Oracle; sets the `threaded` parameter of the connection indicating thread-safe usage.  cx_Oracle docs indicate setting this flag to `False` will speed performance by 10-15%.  While this defaults to `False` in cx_Oracle, SQLAlchemy defaults it to `True`, preferring stability over early optimization.
-* strategy='plain' : the Strategy argument is used to select alternate implementations of the underlying Engine object, which coordinates operations between dialects, compilers, connections, and so on.  Currently, the only alternate strategy besides the default value of "plain" is the "threadlocal" strategy, which selects the usage of the `TLEngine` class that provides a modified connection scope for implicit executions.  Implicit execution as well as further detail on this setting are described in [dbengine_implicit](rel:dbengine_implicit).
-* use_oids=False : used only by Postgres, will enable the column name "oid" as the object ID column, which is also used for the default sort order of tables.  Postgres as of 8.1 has object IDs disabled by default.
-* convert_unicode=False : if set to True, all String/character based types will convert Unicode values to raw byte values going into the database, and all raw byte values to Python Unicode coming out in result sets.  This is an engine-wide method to provide unicode across the board.  For unicode conversion on a column-by-column level, use the `Unicode` column type instead.
-* encoding='utf-8' : the encoding to use for all Unicode translations, both by engine-wide unicode conversion as well as the `Unicode` type object.
+* **poolclass=None** - a `sqlalchemy.pool.Pool` subclass that will be instantated in place of the default connection pool.
+* **pool_size=5** - the number of connections to keep open inside the connection pool.  This used with `QueuePool` as well as `SingletonThreadPool`.
+* **pool_recycle=-1** - this setting causes the pool to recycle connections after the given number of seconds has passed.  It defaults to -1, or no timeout.  For example, setting to 3600 means connections will be recycled after one hour.  Note that MySQL in particular will disconnect automatically if no activity is detected on a connection for eight hours (although this is configurable with the MySQLDB connection itself and the  server configuration as well).
+* **pool_timeout=30** - number of seconds to wait before giving up on getting a connection from the pool.  This is only used with `QueuePool`.
+* **strategy='plain'** - the Strategy argument is used to select alternate implementations of the underlying Engine object, which coordinates operations between dialects, compilers, connections, and so on.  Currently, the only alternate strategy besides the default value of "plain" is the "threadlocal" strategy, which selects the usage of the `TLEngine` class that provides a modified connection scope for implicit executions.  Implicit execution as well as further detail on this setting are described in [dbengine_implicit](rel:dbengine_implicit).
+* **threaded=True** - used by cx_Oracle; sets the `threaded` parameter of the connection indicating thread-safe usage.  cx_Oracle docs indicate setting this flag to `False` will speed performance by 10-15%.  While this defaults to `False` in cx_Oracle, SQLAlchemy defaults it to `True`, preferring stability over early optimization.
+* **use_ansi=True** - used only by Oracle;  when False, the Oracle driver attempts to support a particular "quirk" of Oracle versions 8 and previous, that the LEFT OUTER JOIN SQL syntax is not supported, and the "Oracle join" syntax of using `&lt;column1&gt;(+)=&lt;column2&gt;` must be used in order to achieve a LEFT OUTER JOIN.
+* **use_oids=False** - used only by Postgres, will enable the column name "oid" as the object ID column, which is also used for the default sort order of tables.  Postgres as of 8.1 has object IDs disabled by default.
+
+### Configuring Logging {@name=logging}
+
+As of the 0.3 series of SQLAlchemy, Python's standard [logging](http://www.python.org/doc/lib/module-logging.html) module is used to implement informational and debug log output.  This allows SQLAlchemy's logging to integrate in a standard way with other applications and libraries.  The `echo` and `echo_pool` flags that are present on `create_engine()`, as well as the `echo_uow` flag used on `Session`, all interact with regular loggers.
+
+This section assumes familiarity with the above linked logging module.  All logging performed by SQLAlchemy exists underneath the `sqlalchemy` namespace, as used by `logging.getLogger('sqlalchemy')`.  When logging has been configured (i.e. such as via `logging.basicConfig()`), the general namespace of SA loggers that can be turned on is as follows:
+
+* `sqlalchemy.engine` - controls SQL echoing.  set to `logging.INFO` for SQL query output, `logging.DEBUG` for query + result set output.
+* `sqlalchemy.pool` - controls connection pool logging.  set to `logging.INFO` or lower to log connection pool checkouts/checkins.
+* `sqlalchemy.orm` - controls logging of various ORM functions.  set to `logging.INFO` for configurational logging as well as unit of work dumps, `logging.DEBUG` for extensive logging during query and flush() operations.  Subcategories of `sqlalchemy.orm` include:
+    * `sqlalchemy.orm.attributes` - logs certain instrumented attribute operations, such as triggered callables
+    * `sqlalchemy.orm.mapper` - logs Mapper configuration and operations
+    * `sqlalchemy.orm.unitofwork` - logs flush() operations, including dependency sort graphs and other operations
+    * `sqlalchemy.orm.strategies` - logs relation loader operations (i.e. lazy and eager loads)
+    * `sqlalchemy.orm.sync` - logs synchronization of attributes from parent to child instances during a flush()
+
+For example, to log SQL queries as well as unit of work debugging:
+
+    {python}
+    import logging
+    
+    logging.basicConfig()
+    logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
+    logging.getLogger('sqlalchemy.orm.unitofwork').setLevel(logging.DEBUG)
+    
+By default, the log level is set to `logging.ERROR` within the entire `sqlalchemy` namespace so that no log operations occur, even within an application that has logging enabled otherwise.
+
+The `echo` flags present as keyword arguments to `create_engine()` and others as well as the `echo` property on `Engine`, when set to `True`, will first attempt to insure that logging is enabled.  Unfortunately, the `logging` module provides no way of determining if output has already been configured (note we are referring to if a logging configuration has been set up, not just that the logging level is set).  For this reason, any `echo=True` flags will result in a call to `logging.basicConfig()` using sys.stdout as the destination.  It also sets up a default format using the level name, timestamp, and logger name.  Note that this configuration has the affect of being configured **in addition** to any existing logger configurations.  Therefore, **when using Python logging, insure all echo flags are set to False at all times**, to avoid getting duplicate log lines.  
 
 ### Using Connections {@name=connections}
 
index b3f7774f8c89816b45c07952d4a547ac435c8b34..23c411412cc1be96b00c3b1c083198725e139734 100644 (file)
@@ -234,6 +234,8 @@ Within the `MetaData` collection, this table will be identified by the combinati
         Column('lala', String(40)),
         ForeignKeyConstraint(['lala'],['hoho.lala'], onupdate="CASCADE", ondelete="CASCADE"))
 
+Note that these clauses are not supported on SQLite, and require `InnoDB` tables when used with MySQL.  They may also not be supported on other databases.
+
 #### Enabling Table / Column Quoting {@name=quoting}
 
 Feature Status: [Alpha Implementation][alpha_implementation] 
index 224eab1837b4fbcfbd797a85d0b6562a6b2ef1b3..507f97ebf1b802d3e1e39f840b426e4f3180b48b 100644 (file)
@@ -170,7 +170,7 @@ Labels are also generated in such a way as to never go beyond 30 characters.  Mo
 
     {python title="use_labels Generates Abbreviated Labels"}
     long_named_table = users.alias('this_is_the_person_table')
-    {sql}c = select([person], use_labels=True).execute()  
+    {sql}c = select([long_named_table], use_labels=True).execute()  
     SELECT this_is_the_person_table.user_id AS this_is_the_person_table_b36c, 
     this_is_the_person_table.user_name AS this_is_the_person_table_f76a, 
     this_is_the_person_table.password AS this_is_the_person_table_1e7c
index 087d0adba1303b4e78efd95bfee24ae284752994..25fa1624b2b00ef7bf61d1f06c7743850b5c1420 100644 (file)
        padding: 5px 0px 0px 0px;
 }
 
-.code {
-       font-family: courier, "courier new", serif;
-       font-size:12px;
-       background-color: #f0f0f0;      
-       padding:2px 2px 2px 10px;
-       margin: 5px 5px 5px 5px;
-}
-
-.sliding_code {
-       font-family: courier, "courier new", serif;
-       font-size:12px;
-       background-color: #f0f0f0;      
-       padding:2px 2px 2px 10px;
-       margin: 5px 5px 5px 5px;
-       overflow:auto;
-}
 
 .codetitle {
        font-family: verdana, sans-serif;
@@ -237,6 +221,16 @@ h3, .sectionheadertext {
        border: solid 1px #ccc;
 }
 
+.sliding_code {
+       font-family: courier, "courier new", serif;
+       font-size:12px;
+       background-color: #f0f0f0;      
+       border: solid 1px #ccc;
+       padding:2px 2px 2px 10px;
+       margin: 5px 5px 5px 5px;
+       overflow:auto;
+}
+
 .code {
        font-family: courier, "courier new", serif;
        background-color: #f0f0f0;      
index ea0dd236ee16bfcce929af9fd83106d0c9b19685..39357970770b1229adb3057a56f69d6a0a5baa12 100644 (file)
@@ -5,12 +5,14 @@ meta = BoundMetaData('sqlite://', echo=True)
 
 org_table = Table('organizations', meta, 
     Column('org_id', Integer, primary_key=True),
-    Column('org_name', String(50), nullable=False, key='name'))
+    Column('org_name', String(50), nullable=False, key='name'),
+    mysql_engine='InnoDB')
     
 member_table = Table('members', meta,
     Column('member_id', Integer, primary_key=True),
     Column('member_name', String(50), nullable=False, key='name'),
-    Column('org_id', Integer, ForeignKey('organizations.org_id')))
+    Column('org_id', Integer, ForeignKey('organizations.org_id')), 
+    mysql_engine='InnoDB')
 meta.create_all()    
     
 class Organization(object):
index 1974fdac782c60652ece82230fcd0c137fd607f3..c8c14873f558f33b427760e87fe15ce1ea06e57f 100644 (file)
@@ -87,8 +87,8 @@ class Mapper(object):
         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.
         
-        allow_column_override - if True, allows association relationships to be set up which override the usage of 
-        a column that is on the table (based on key/attribute name).
+        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.
         
         entity_name - a name to be associated with the class, to allow alternate mappings for a single class.
         
index 3ce8c4f336200928374b971ef382f1e772973517..0ad00f8ad45eb69f4403b8bc603384b0dc633abb 100644 (file)
@@ -1010,6 +1010,27 @@ class EagerTest(MapperSuperTest):
             l = q.options(contains_eager('addresses')).instances(selectquery.execute())
             self.assert_result(l, User, *user_address_result)
         self.assert_sql_count(testbase.db, go, 1)
+
+    def testcustomeagerwithdecorator(self):
+        mapper(User, users, properties={
+            'addresses':relation(Address, lazy=False)
+        })
+        mapper(Address, addresses)
+
+        adalias = addresses.alias('adalias')
+        selectquery = users.outerjoin(adalias).select(use_labels=True)
+        def decorate(row):
+            d = {}
+            for c in addresses.columns:
+                d[c] = row[adalias.corresponding_column(c)]
+            return d
+            
+        q = create_session().query(User)
+
+        def go():
+            l = q.options(contains_eager('addresses', decorator=decorate)).instances(selectquery.execute())
+            self.assert_result(l, User, *user_address_result)
+        self.assert_sql_count(testbase.db, go, 1)
         
     def testorderby_desc(self):
         m = mapper(Address, addresses)
index c1f885bb0e8b5b4b921103c2dc3e2607f872fece..bdb5a15c9c7cdee356ea0b4aef2a24fbadc89e45 100644 (file)
@@ -464,6 +464,61 @@ class ForeignPKTest(UnitOfWorkTest):
         p.sites.append(ps)
         ctx.current.flush()
         assert people.count(people.c.person=='im the key').scalar() == peoplesites.count(peoplesites.c.person=='im the key').scalar() == 1
+
+class PassiveDeletesTest(UnitOfWorkTest):
+    def setUpAll(self):
+        UnitOfWorkTest.setUpAll(self)
+        global metadata, mytable,myothertable
+        metadata = BoundMetaData(testbase.db)
+        mytable = Table('mytable', metadata,
+            Column('id', Integer, primary_key=True),
+            Column('data', String(30)),
+            mysql_engine='InnoDB'
+            )
+
+        myothertable = Table('myothertable', metadata,
+            Column('id', Integer, primary_key=True),
+            Column('parent_id', Integer),
+            Column('data', String(30)),
+            ForeignKeyConstraint(['parent_id'],['mytable.id'], ondelete="CASCADE"),
+            mysql_engine='InnoDB'
+            )
+
+        metadata.create_all()
+    def tearDownAll(self):
+        metadata.drop_all()
+        UnitOfWorkTest.tearDownAll(self)
+
+    @testbase.unsupported('sqlite')
+    def testbasic(self):
+        class MyClass(object):
+            pass
+        class MyOtherClass(object):
+            pass
+        
+        mapper(MyOtherClass, myothertable)
+
+        mapper(MyClass, mytable, properties={
+            'children':relation(MyOtherClass, passive_deletes=True, cascade="all")
+        })
+
+        sess = ctx.current
+        mc = MyClass()
+        mc.children.append(MyOtherClass())
+        mc.children.append(MyOtherClass())
+        mc.children.append(MyOtherClass())
+        mc.children.append(MyOtherClass())
+        sess.save(mc)
+        sess.flush()
+        sess.clear()
+        assert myothertable.count().scalar() == 4
+        mc = sess.query(MyClass).get(mc.id)
+        sess.delete(mc)
+        sess.flush()
+        assert mytable.count().scalar() == 0
+        assert myothertable.count().scalar() == 0
+        
+
         
 class PrivateAttrTest(UnitOfWorkTest):
     """tests various things to do with private=True mappers"""
@@ -908,7 +963,8 @@ class SaveTest(UnitOfWorkTest):
         ctx.current.delete(u)
         ctx.current.flush()
         self.assert_(a.address_id is not None and a.user_id is None and not ctx.current.identity_map.has_key(u._instance_key) and ctx.current.identity_map.has_key(a._instance_key))
-        
+    
+    
     def testbackwardsonetoone(self):
         # test 'backwards'
 #        m = mapper(Address, addresses, properties = dict(