diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2006-11-03 01:17:28 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2006-11-03 01:17:28 +0000 |
| commit | 695f65db853a7b74a1ce2da75d8e3c55bbafae81 (patch) | |
| tree | dd66a8f126fa1c44cfb7349c319168ab6c3b0a0f /doc | |
| parent | 14845494113b5327b2ed7f8ed13aed9bc9ce27b2 (diff) | |
| download | sqlalchemy-695f65db853a7b74a1ce2da75d8e3c55bbafae81.tar.gz | |
- added an assertion within the "cascade" step of ORM relationships to check
that the class of object attached to a parent object is appropriate
(i.e. if A.items stores B objects, raise an error if a C is appended to A.items)
- new extension sqlalchemy.ext.associationproxy, provides transparent "association object"
mappings. new example examples/association/proxied_association.py illustrates.
- some example cleanup
Diffstat (limited to 'doc')
| -rw-r--r-- | doc/build/content/datamapping.txt | 3 | ||||
| -rw-r--r-- | doc/build/content/plugins.txt | 80 |
2 files changed, 82 insertions, 1 deletions
diff --git a/doc/build/content/datamapping.txt b/doc/build/content/datamapping.txt index f2c20814b..d9a94556f 100644 --- a/doc/build/content/datamapping.txt +++ b/doc/build/content/datamapping.txt @@ -700,7 +700,7 @@ The `relation` function handles a basic many-to-many relationship when you speci ### Association Object {@name=association} -Many to Many can also be done with an association object, that adds additional information about how two items are related. In this pattern, the "secondary" option is no longer used; instead, the association object becomes a mapped entity itself, mapped to the association table. If the association table has no explicit primary key columns defined, you also have to tell the mapper what columns will compose its "primary key", which are typically the two (or more) columns involved in the association. Also, the relation between the parent and association mapping is typically set up with a cascade of `all, delete-orphan`. This is to insure that when an association object is removed from its parent collection, it is deleted (otherwise, the unit of work tries to null out one of the foreign key columns, which raises an error condition since that column is also part of its "primary key"). +Many to Many can also be done with an association object, that adds additional information about how two items are related. In this pattern, the "secondary" option to `relation()` is no longer used; instead, the association object becomes a mapped entity itself, mapped to the association table. If the association table has no explicit primary key columns defined, you also have to tell the mapper what columns will compose its "primary key", which are typically the two (or more) columns involved in the association. Also, the relation between the parent and association mapping is typically set up with a cascade of `all, delete-orphan`. This is to insure that when an association object is removed from its parent collection, it is deleted (otherwise, the unit of work tries to null out one of the foreign key columns, which raises an error condition since that column is also part of its "primary key"). {python} from sqlalchemy import * @@ -812,5 +812,6 @@ Keep in mind that the association object works a little differently from a plain session.flush() +SQLAlchemy includes an extension module which can be used in some cases to decrease the explicitness of the association object pattern; this extension is described in [plugins_associationproxy](rel:plugins_associationproxy).
\ No newline at end of file diff --git a/doc/build/content/plugins.txt b/doc/build/content/plugins.txt index c5b094e54..8229693c1 100644 --- a/doc/build/content/plugins.txt +++ b/doc/build/content/plugins.txt @@ -287,6 +287,86 @@ It should be noted that the `flush()` method on the instance need not be called. # commit all changes ctx.current.flush() +### associationproxy + +**Author:** Mike Bayer<br/> +**Version:** 0.3.1 or greater + +`associationproxy` is used to create a transparent proxy to the associated object in an association relationship, thereby decreasing the verbosity of the pattern in cases where explicit access to the association object is not required. The association relationship pattern is a richer form of a many-to-many relationship, which is described in [datamapping_association](rel:datamapping_association). It is strongly recommended to fully understand the association object pattern in its explicit form before using this extension; see the examples in the SQLAlchemy distribution under the directory `examples/association/`. + +When dealing with association relationships, the **association object** refers to the object that maps to a row in the association table (i.e. the many-to-many table), while the **associated object** refers to the "endpoint" of the association, i.e. the ultimate object referenced by the parent. The proxy can return collections of objects attached to association objects, and can also create new association objects given only the associated object. An example using the Keyword mapping described in the data mapping documentation is as follows: + + {python} + from sqlalchemy.ext.associationproxy import AssociationProxy + + class User(object): + pass + + class Keyword(object): + def __init__(self, name): + self.keyword_name = name + + class Article(object): + # create "keywords" proxied association. + # the collection is called 'keyword_associations', the endpoint + # attribute of each association object is called 'keyword'. the + # class itself of the association object will be figured out automatically . + keywords = AssociationProxy('keyword_associations', 'keyword') + + class KeywordAssociation(object): + pass + + # create mappers normally + # note that we set up 'keyword_associations' on Article, + # and 'keyword' on KeywordAssociation. + mapper(Article, articles_table, properties={ + 'keyword_associations':relation(KeywordAssociation, lazy=False, cascade="all, delete-orphan") + } + ) + mapper(KeywordAssociation, itemkeywords_table, + primary_key=[itemkeywords_table.c.article_id, itemkeywords_table.c.keyword_id], + properties={ + 'keyword' : relation(Keyword, lazy=False), + 'user' : relation(User, lazy=False) + } + ) + mapper(User, users_table) + mapper(Keyword, keywords_table) + + # now, Keywords can be attached to an Article directly; + # KeywordAssociation will be created by the AssociationProxy, and have the + # 'keyword' attribute set to the new Keyword. + # note that these KeywordAssociation objects will not have a User attached to them. + article = Article() + article.keywords.append(Keyword('blue')) + article.keywords.append(Keyword('red')) + session.save(article) + session.flush() + + # the "keywords" collection also returns the underlying Keyword objects + article = session.query(Article).get_by(id=12) + for k in article.keywords: + print "Keyword:", k.keyword_name + + # the original 'keyword_associations' relation exists normally with no awareness of the proxy + article.keyword_associations.append(KeywordAssociation()) + print [ka for ka in article.keyword_associations] + +Note that the above operations on the `keywords` collection are proxying operations to and from the `keyword_associations` collection, which exists normally and can be accessed directly. `AssociationProxy` will also detect if the collection is list or scalar based and will configure the proxied property to act the same way. + +For the common case where the association object's creation needs to be specified by the application, `AssociationProxy` takes an optional callable `creator()` which takes a single associated object as an argument, and returns a new association object. + + {python} + def create_keyword_association(keyword): + ka = KeywordAssociation() + ka.keyword = keyword + return ka + + class Article(object): + # create "keywords" proxied association + keywords = AssociationProxy('keyword_associations', 'keyword', creator=create_keyword_association) + + ### threadlocal **Author:** Mike Bayer and Daniel Miller |
