From: Mike Bayer Date: Mon, 6 Feb 2006 00:51:51 +0000 (+0000) Subject: docs X-Git-Tag: rel_0_1_0~48 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d090510532fa6d212bfd401c1541f795f536f515;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git docs --- diff --git a/doc/build/content/datamapping.myt b/doc/build/content/datamapping.myt index e8169d6373..4130168d12 100644 --- a/doc/build/content/datamapping.myt +++ b/doc/build/content/datamapping.myt @@ -348,7 +348,31 @@ DELETE FROM addresses WHERE addresses.address_id = :address_id

Note that when overriding a backreferenced property, we re-specify the backreference as well. This will not override the existing 'addresses' property on the User class, but just sends a message to the attribute-management system that it should continue to maintain this backreference.

+<&|doclib.myt:item, name="cascade", description="Creating Relationships Automatically with cascade_mappers" &> +

The mapper package has a helper function cascade_mappers() which can simplify the task of linking several mappers together. Given a list of classes and/or mappers, it identifies the foreign key relationships between the given mappers or corresponding class mappers, and creates relation() objects representing those relationships, including a backreference. Attempts to find +the "secondary" table in a many-to-many relationship as well. The names of the relations +are a lowercase version of the related class. In the case of one-to-many or many-to-many, +the name is "pluralized", which currently is based on the English language (i.e. an 's' or +'es' added to it):

+ <&|formatting.myt:code&> + # create two mappers. the 'users' and 'addresses' tables have a foreign key + # relationship + mapper1 = mapper(User, users) + mapper2 = mapper(Address, addresses) + + # cascade the two mappers together (can also specify User, Address as the arguments) + cascade_mappers(mapper1, mapper2) + + # two new object instances + u = User('user1') + a = Address('test') + + # "addresses" and "user" property are automatically added + u.addresses.append(a) + print a.user + + <&|doclib.myt:item, name="lazyload", description="Selecting from Relationships: Lazy Load" &>

We've seen how the relation specifier affects the saving of an object and its child items, how does it affect selecting them? By default, the relation keyword indicates that the related property should be attached a Lazy Loader when instances of the parent object are loaded from the database; this is just a callable function that when accessed will invoke a second SQL query to load the child objects of the parent.

diff --git a/doc/build/content/unitofwork.myt b/doc/build/content/unitofwork.myt index 83f78e804b..548149001b 100644 --- a/doc/build/content/unitofwork.myt +++ b/doc/build/content/unitofwork.myt @@ -112,8 +112,62 @@ <&|doclib.myt:item, name="identity", description="The Identity Map" &> +

All object instances which are saved to the database, or loaded from the database, are given an identity by the mapper/objectstore. This identity is available via the _identity_key property attached to each object instance, and is a tuple consisting of the table's class, the SQLAlchemy-specific "hash key" of the table its persisted to, and an additional tuple of primary key values, in the order that they appear within the table definition:

+ <&|formatting.myt:code&> + >>> obj._instance_key + (, "Table('users',SQLiteSQLEngine(([':memory:'], {})),schema=None)", (7,)) + +

Note that this identity is a database identity, not an in-memory identity. An application can have several different objects in different unit-of-work scopes that have the same database identity, or an object can be removed from memory, and constructed again later, with the same database identity. What can never happen is for two copies of the same object to exist in the same unit-of-work scope with the same database identity; this is guaranteed by the identity map. +

+

+ At the moment that an object is assigned this key, it is also added to the current thread's unit-of-work's identity map. The identity map is just a WeakValueDictionary which maintains the one and only reference to a particular object within the current unit of work scope. It is used when result rows are fetched from the database to insure that only one copy of a particular object actually comes from that result set in the case that eager loads or other joins are used, or if the object had already been loaded from a previous result set. The get() method on a mapper, which retrieves an object based on primary key identity, also checks in the current identity map first to save a database round-trip if possible. In the case of an object lazy-loading a single child object, the get() method is also used. +

+

Methods on mappers and the objectstore module, which are relevant to identity include the following:

+ <&|formatting.myt:code&> + # assume 'm' is a mapper + m = mapper(User, users) + + # get the identity key corresponding to a primary key + key = m.identity_key(7) + + # for composite key, list out the values in the order they + # appear in the table + key = m.identity_key(12, 'rev2') + + # get the identity key given a primary key + # value as a tuple, a class, and a table + key = objectstore.get_id_key((12, 'rev2'), User, users) + + # get the identity key for an object, whether or not it actually + # has one attached to it (m is the mapper for obj's class) + key = m.instance_key(obj) + + # same thing, from the objectstore (works for any obj type) + key = objectstore.instance_key(obj) + + # is this key in the current identity map? + objectstore.has_key(key) + + # is this object in the current identity map? + objectstore.has_instance(obj) + + # get this object from the current identity map based on + # singular/composite primary key, or if not go + # and load from the database + obj = m.get(12, 'rev2') + <&|doclib.myt:item, name="import", description="Bringing External Instances into the UnitOfWork" &> +

The _identity_key attribute is designed to work with objects that are serialized into strings and brought back again. As it contains no references to internal structures or database connections, applications that use caches or session storage which require serialization (i.e. pickling) can store SQLAlchemy-loaded objects. However, as mentioned earlier, an object with a particular database identity is only allowed to exist uniquely within the current unit-of-work scope. So, upon deserializing such an object, it has to "check in" with the current unit-of-work/identity map combination, to insure that it is the only unique instance. This is achieved via the import_instance() function in objectstore:

+ <&|formatting.myt:code&> + # deserialize an object + myobj = pickle.loads(mystring) + + # "import" it. if the objectstore already had this object in the + # identity map, then you get back the one from the current session. + myobj = objectstore.import_instance(myobj) + +

Note that the import_instance() function will either mark the deserialized object as the official copy in the current identity map, which includes updating its _identity_key with the current application's class instance, or it will discard it and return the corresponding object that was already present.

<&|doclib.myt:item, name="rollback", description="Rollback" &>