From: Mike Bayer Date: Fri, 18 Nov 2005 00:52:05 +0000 (+0000) Subject: (no commit message) X-Git-Tag: rel_0_1_0~315 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fd4da2390a25ebf1d449ed3b5575e70b7f87fb04;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git --- diff --git a/doc/build/content/adv_datamapping.myt b/doc/build/content/adv_datamapping.myt index 5d8658a60e..146a56a36f 100644 --- a/doc/build/content/adv_datamapping.myt +++ b/doc/build/content/adv_datamapping.myt @@ -1,34 +1,237 @@ <%flags>inherit='document_base.myt' <&|doclib.myt:item, name="adv_datamapping", description="Advanced Data Mapping" &> +

This section is under construction. For now, it has just the basic recipe for each concept without much else.

+ +

To start, heres the tables we will work with again:

+ <&|formatting.myt:code&> + from sqlalchemy.schema import * + import sqlalchemy.engine as engine + db = engine.create_engine('sqlite', 'mydb', {}, echo=True) + + # a table to store users + users = Table('users', db, + Column('user_id', Integer, primary_key = True), + Column('user_name', String(40)), + Column('password', String(80)) + ) + + # a table that stores mailing addresses associated with a specific user + addresses = Table('addresses', db, + Column('address_id', Integer, primary_key = True), + Column('user_id', Integer, ForeignKey("users.user_id")), + Column('street', String(100)), + Column('city', String(80)), + Column('state', String(2)), + Column('zip', String(10)) + ) + + # a table that stores keywords + keywords = Table('keywords', db, + Column('keyword_id', Integer, primary_key = True), + Column('name', VARCHAR(50)) + ) + + # a table that associates keywords with users + userkeywords = Table('userkeywords', db, + Column('user_id', INT, ForeignKey("users")), + Column('keyword_id', INT, ForeignKey("keywords")) + ) + + <&|doclib.myt:item, name="creating", description="Creating Mappers" &> <&|doclib.myt:item, name="customjoin", description="Custom Join Conditions" &> + <&|formatting.myt:code&> + <&|doclib.myt:item, name="loadingoptions", description="Loading Options" &> + <&|formatting.myt:code&> + <&|doclib.myt:item, name="options", description="Mapper Options" &> + <&|formatting.myt:code&> + <&|doclib.myt:item, name="custom", description="Custom Queries" &> + <&|formatting.myt:code&> + # a class + class User(object): + pass + + # basic mapper + User.mapper = mapper(User, users) + + # basic select with criterion + User.mapper.select(and_(users.c.user_name=='jane', users.c.user_id>12)) + + # select with text criterion + User.mapper.select("user_name='jane' and user_id>12") + + # select with totally textual query + User.mapper.select_text("select user_id, user_name, password from users") + + # select with a Select object + s = users.select(users, users.c.user_id==addresses.c.user_id) + User.mapper.select(s) + + + +<&|doclib.myt:item, name="inheritance", description="Mapping a Class with Table Inheritance" &> + <&|formatting.myt:code&> + class User(object): + """a user object.""" + pass + User.mapper = mapper(User, users) + + class AddressUser(User): + """a user object that also has the users mailing address.""" + pass + + # define a mapper for AddressUser that inherits the User.mapper, and joins on the user_id column + AddressUser.mapper = mapper( + addresses, inherits = User.mapper, + inherit_condition=User.c.user_id==addresses.c.user_id + ) + + items = AddressUser.mapper.select() + <&|doclib.myt:item, name="joins", description="Mapping a Class against Multiple Tables" &> + <&|formatting.myt:code&> + # a class + class AddressUser(object): + pass + + # define a Join + j = join(users, addresses, users.c.address_id==addresses.c.address_id) + + # map to it - the identity of an AddressUser object will be + # based on (user_id, address_id) since those are the primary keys involved + m = mapper(AddressUser, j) + + # more complex join + j = join(users, userkeywords, + users.c.user_id==userkeywords.c.user_id).join(keywords, + userkeywords.c.keyword_id==keywords.c.keyword_id) + + # a class + class KeywordUser(object): + pass + + # map to it - the identity of a KeywordUser object will be + # (user_id, keyword_id) since those are the primary keys involved + m = mapper(KeywordUser, j) + <&|doclib.myt:item, name="multiple", description="Multiple Mappers for One Class" &> + <&|formatting.myt:code&> + class User(object): + pass + + # mapper one - mark it as "primary", meaning this mapper will handle + # saving and class-level properties + m1 = mapper(User, users, is_primary=True) + + # mapper two - this one will also eager-load address objects in + m2 = mapper(User, users, properties={ + 'addresses' : relation(Address, addresses, lazy=False) + }) + + # get a user. this user will not have an 'addreses' property + u1 = m1.select(User.c.user_id==10) + + # get another user. this user will have an 'addreses' property. + u2 = m2.select(User.c.user_id==27) + + # make some modifications, including adding an Address object. + u1.user_name = 'jack' + u2.user_name = 'jane' + u2.addresses.append(Address('123 green street')) + + # upon commit, the User objects will be saved. + # the Address object will not, since the primary mapper for User + # does not have an 'addresses' relationship defined + objectstore.commit() + <&|doclib.myt:item, name="recursive", description="Self Referential Mappers" &> + <&|formatting.myt:code&> + # define a self-referential table + trees = Table('treenodes', engine, + Column('node_id', Integer, primary_key=True), + Column('parent_node_id', Integer, ForeignKey('treenodes.node_id'), nullable=True), + Column('node_name', String(50), nullable=False), + ) + + # treenode class + class TreeNode(object): + pass + + # mapper defines "children" property, pointing back to TreeNode class, + # with the mapper unspecified. it will point back to the primary + # mapper on the TreeNode class. + TreeNode.mapper = mapper(TreeNode, trees, properties={ + 'children' : relation( + TreeNode, + primaryjoin=tables.trees.c.parent_node_id==tables.trees.c.node_id, + lazy=True, uselist=True, private=True + ), + ) + ) + + # or, specify the circular relationship after establishing the original mapper: + mymapper = mapper(TreeNode, trees) + + mymapper.add_property('children', relation( + mymapper, + primaryjoin=tables.trees.c.parent_node_id==tables.trees.c.node_id, + lazy=True, uselist=True, private=True + )) + + +

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.

<&|doclib.myt:item, name="resultset", description="Result-Set Mapping" &> +

Take any result set and feed it into a mapper to produce objects. Multiple mappers can be combined to retrieve unrelated objects from the same row in one step.

+ <&|formatting.myt:code&> + class User(object): + pass + class Address(object): + pass + User.mapper = mapper(User, users) + Address.mapper = mapper(Address, addresses) + + # select users + c = users.select().execute() + # get objects + userlist = User.mapper.instances(c) + + # select users and addresses in one query + s = select([users, addresses], users.c.user_id==addresses.c.user_id) + + # execute it, and process the results with the User mapper, chained to the Address mapper + r = User.mapper.instances(s.execute(), Address.mapper) + # results rows are an array of objects, one for each mapper used + for entry in r: + user = r[0] + address = r[1] + <&|doclib.myt:item, name="extending", description="Extending Mapper" &> <&|doclib.myt:item, name="class", description="How Mapper Modifies Mapped Classes" &> + <&|formatting.myt:code&> + <&|doclib.myt:item, name="mapperextension", description="Adding/Replacing Functionality with Mapper Extension" &> + <&|formatting.myt:code&> +