git-svn-id: http://svn.sqlalchemy.org/sqlalchemy/trunk@635 8cd8332f-0806-0410-a4b6-96f4b9520244

<%flags>inherit='document_base.myt'</%flags>
<&|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&>
        </&>
    </&>
</&>

</&>

<%flags>inherit='document_base.myt'</%flags>
<&|doclib.myt:item, name="adv_datamapping", description="Advanced Data Mapping" &>

<&|doclib.myt:item, name="creating", description="Creating Mappers" &>
    <&|doclib.myt:item, name="customjoin", description="Custom Join Conditions" &>
    </&>

    <&|doclib.myt:item, name="loadingoptions", description="Loading Options" &>
    </&>

</&>

<&|doclib.myt:item, name="options", description="Mapper Options" &>
</&>

<&|doclib.myt:item, name="custom", description="Custom Queries" &>
</&>

<&|doclib.myt:item, name="joins", description="Mapping a Class against Multiple Tables" &>
</&>
<&|doclib.myt:item, name="multiple", description="Multiple Mappers for One Class" &>
</&>
<&|doclib.myt:item, name="recursive", description="Self Referential Mappers" &>
</&>
<&|doclib.myt:item, name="resultset", description="Result-Set Mapping" &>
</&>

<&|doclib.myt:item, name="extending", description="Extending Mapper" &>
    <&|doclib.myt:item, name="class", description="How Mapper Modifies Mapped Classes" &>
    </&>
    <&|doclib.myt:item, name="mapperextension", description="Adding/Replacing Functionality with Mapper Extension" &>
    </&>
</&>

</&>


doc

doc

git-svn-id: 655ff90ec95d1eeadb1ee4bb9db742a3c015d499@452 8cd8332f-0806-0410-a4b6-96f4b9520244

<%flags>inherit='document_base.myt'</%flags>
<&|doclib.myt:item, name="types", description="The Types System" &>

The package sqlalchemy.types defines the datatype identifiers which may be used when defining <&formatting.myt:link, path="metadata", text="table metadata"&>.  This package includes a set of generic types, a set of SQL-specific subclasses of those types, and a small extension system used by specific database connectors to adapt these generic types into database-specific type objects.

<&|doclib.myt:item, name="standard", description="Built-in Types" &>

SQLAlchemy comes with a set of standard generic datatypes, which are defined as classes.  They are specified to table meta data using either the class itself, or an instance of the class.  Creating an instance of the class allows you to specify parameters for the type, such as string length, numerical precision, etc.

The standard set of generic types are:

<&|formatting.myt:code&>
# sqlalchemy.types package:
class String: pass
class Integer: pass
class Numeric: pass
class DateTime: pass
class Binary: pass
class Boolean: pass
</&>

More specific subclasses of these types are available, to allow finer grained control over types:

<&|formatting.myt:code&>
class FLOAT(Numeric):pass
class TEXT(String):pass
class DECIMAL(Numeric):pass
class INT(Integer):pass
INTEGER = INT
class TIMESTAMP(DateTime): pass
class DATETIME(DateTime): pass
class CLOB(String): pass
class VARCHAR(String): pass
class CHAR(String):pass
class BLOB(Binary): pass
class BOOLEAN(Boolean): pass
</&>

When using a specific database engine, these types are adapted even further via a set of database-specific subclasses defined by the database engine.

</&>

<&|doclib.myt:item, name="custom", description="Creating your Own Types" &>

Types also support pre-processing of query parameters as well as post-processing of result set data.  You can make your own type classes and specify them just like the standard types:

<&|formatting.myt:code&>
    import sqlalchemy.types as types

    class MyType(types.String):
        """basic type that subclasses String, prefixes values with "PREFIX:" on
        the way in and strips it off on the way out."""
        def convert_bind_param(self, value):
            return "PREFIX:" + value
        def convert_result_value(self, value):
            return value[7:]
</&>

Another example, which illustrates a fully defined datatype:

<&|formatting.myt:code&>
    import sqlalchemy.types as types

    class MyType(types.TypeEngine):
        def __init__(self, precision = 8):
            self.precision = precision
        def get_col_spec(self):
            return "MYTYPE(%s)" % self.precision
        def convert_bind_param(self, value):
            return value
        def convert_result_value(self, value):
            return value
        def adapt(self, typeobj):
            """produces an adaptation of this object given a type which is a subclass of this object"""
            return typeobj(self.precision)
        def adapt_args(self):
            """allows for the adaptation of this TypeEngine object into a new kind of type depending on its arguments."""
            return self
</&>
</&>
</&>

<%flags>inherit='document_base.myt'</%flags>
<&|doclib.myt:item, name="types", description="The Types System" &>

SQLAlchemy comes with a set of standard generic datatypes, which are defined as classes.  They are specified to table meta data using either the class itself, or an instance of the class.  Creating an instance of the class allows you to specify parameters for the type, such as string length, numerical precision, etc.

The standard set of generic types are:

<&|formatting.myt:code&>
# sqlalchemy.types package:
class String: pass
class Integer: pass
class Numeric: pass
class DateTime: pass
class Binary: pass
class Boolean: pass
</&>

More specific subclasses of these types are available, to allow finer grained control over types:

<&|formatting.myt:code&>
class FLOAT(Numeric):pass
class TEXT(String):pass
class DECIMAL(Numeric):pass
class INT(Integer):pass
INTEGER = INT
class TIMESTAMP(DateTime): pass
class DATETIME(DateTime): pass
class CLOB(String): pass
class VARCHAR(String): pass
class CHAR(String):pass
class BLOB(Binary): pass
class BOOLEAN(Boolean): pass
</&>

When using a specific database engine, these types are adapted even further via a set of database-specific subclasses defined by the database engine.

<&|doclib.myt:item, name="custom", description="Creating your Own Types" &>

Types also support pre-processing of query parameters as well as post-processing of result set data.  You can make your own type classes and specify them just like the standard types:

<&|formatting.myt:code&>
    import sqlalchemy.types as types

    class MyType(types.String):
        """basic type that subclasses String, prefixes values with "PREFIX:" on
        the way in and strips it off on the way out."""
        def convert_bind_param(self, value):
            return "PREFIX:" + value
        def convert_result_value(self, value):
            return value[7:]
</&>

Another example, which illustrates a fully defined datatype:

<&|formatting.myt:code&>
    import sqlalchemy.types as types

    class MyType(types.TypeEngine):
        def __init__(self, precision = 8):
            self.precision = precision
        def get_col_spec(self):
            return "MYTYPE(%s)" % self.precision
        def convert_bind_param(self, value):
            return value
        def convert_result_value(self, value):
            return value
        def adapt(self, typeobj):
            """produces an adaptation of this object given a type which is a subclass of this object"""
            return typeobj(self.precision)
        def adapt_args(self):
            """allows for the adaptation of this TypeEngine object into a new kind of type depending on its arguments."""
            return self
</&>
</&>
</&>