Commits

Mike Bayer committed e269433

- tutorial updates
- emphasis on not using a custom contructor by default, making sure it's clear that
you *can* use one
- other separation of narrative from special notes using sidebars

Comments (0)

Files changed (4)

doc/build/faq.rst

 ORM Configuration
 ==================
 
+.. _faq_mapper_primary_key:
+
 How do I map a table that has no primary key?
 ---------------------------------------------
 
+The SQLAlchemy ORM, in order to map to a particular table, needs there to be
+at least one column denoted as a primary key column; multiple-column,
+i.e. composite, primary keys are of course entirely feasible as well.  These
+columns do **not** need to be actually known to the database as primary key
+columns, though it's a good idea that they are.  It's only necessary that the columns
+*behave* as a primary key does, e.g. as a unique and not nullable identifier
+for a row.
+
+Most ORMs require that objects have some kind of primary key defined at the
+because the object in memory must correspond to a uniquely identifiable
+row in the database table; at the very least, this allows the
+object can be targeted for UPDATE and DELETE statements which will affect only
+that object's row and no other.   However, the importance of the primary key
+goes far beyond that.  In SQLAlchemy, all ORM-mapped objects are at all times
+linked uniquely within a :class:`.Session`
+to their specific database row using a pattern called the :term:`identity map`,
+a pattern that's central to the unit of work system employed by SQLAlchemy,
+and is also key to the most common (and not-so-common) patterns of ORM usage.
+
+
+.. note::
+
+	It's important to note that we're only talking about the SQLAlchemy ORM; an
+	application which builds on Core and deals only with :class:`.Table` objects,
+	:func:`.select` constructs and the like, **does not** need any primary key
+	to be present on or associated with a table in any way (though again, in SQL, all tables
+	should really have some kind of primary key, lest you need to actually
+	update or delete specific rows).
+
 In almost all cases, a table does have a so-called :term:`candidate key`, which is a column or series
 of columns that uniquely identify a row.  If a table truly doesn't have this, and has actual
 fully duplicate rows, the table is not corresponding to `first normal form <http://en.wikipedia.org/wiki/First_normal_form>`_ and cannot be mapped.   Otherwise, whatever columns comprise the best candidate key can be

doc/build/orm/tutorial.rst

 "SQL" links to see what's being generated.
 
 The return value of :func:`.create_engine` is an instance of :class:`.Engine`, and it represents
-the core interface to the database, adapted through a **dialect** that handles the details
-of the database and DBAPI in use.  In this case the SQLite dialect will interpret instructions
+the core interface to the database, adapted through a :term:`dialect` that handles the details
+of the database and :term:`DBAPI` in use.  In this case the SQLite dialect will interpret instructions
 to the Python built-in ``sqlite3`` module.
 
-The :class:`.Engine` has not actually tried to connect to the database yet; that happens
-only the first time it is asked to perform a task against the database.   We can illustrate
-this by asking it to perform a simple SELECT statement:
+.. sidebar:: Lazy Connecting
 
-.. sourcecode:: python+sql
-
-    {sql}>>> engine.execute("select 1").scalar()
-    select 1
-    ()
-    {stop}1
+    The :class:`.Engine`, when first returned by :func:`.create_engine`,
+    has not actually tried to connect to the database yet; that happens
+    only the first time it is asked to perform a task against the database.
 
-As the :meth:`.Engine.execute` method is called, the :class:`.Engine` establishes a connection to the
-SQLite database, which is then used to emit the SQL.   The connection is then returned to an internal
-connection pool where it will be reused on subsequent statement executions.  While we illustrate direct usage of the
-:class:`.Engine` here, this isn't typically necessary when using the ORM, where the :class:`.Engine`,
-once created, is used behind the scenes by the ORM as we'll see shortly.
+The first time a method like :meth:`.Engine.execute` or :meth:`.Engine.connect`
+is called, the :class:`.Engine` establishes a real :term:`DBAPI` connection to the
+database, which is then used to emit the SQL.  When using the ORM, we typically
+don't use the :class:`.Engine` directly once created; instead, it's used
+behind the scenes by the ORM as we'll see shortly.
 
 Declare a Mapping
 =================
 Now that we have a "base", we can define any number of mapped classes in terms
 of it.  We will start with just a single table called ``users``, which will store
 records for the end-users using our application.
-A new class called ``User`` will be the class to which we map this table.  The
-imports we'll need to accomplish this include objects that represent the components
-of our table, including the :class:`.Column` class which represents a database column,
-as well as the :class:`.Integer` and :class:`.String` classes that
-represent basic datatypes used in columns::
+A new class called ``User`` will be the class to which we map this table.  Within
+the class, we define details about the table to which we'll be mapping, primarily
+the table name, and names and datatypes of columns::
 
     >>> from sqlalchemy import Column, Integer, String
     >>> class User(Base):
     ...     fullname = Column(String)
     ...     password = Column(String)
     ...
-    ...     def __init__(self, name, fullname, password):
-    ...         self.name = name
-    ...         self.fullname = fullname
-    ...         self.password = password
-    ...
     ...     def __repr__(self):
-    ...        return "<User('%s','%s', '%s')>" % (self.name, self.fullname, self.password)
-
-The above ``User`` class establishes details about the table being mapped, including the name of the table denoted
-by the ``__tablename__`` attribute, a set of columns ``id``, ``name``, ``fullname`` and ``password``,
-where the ``id`` column will also be the primary key of the table.   While its certainly possible
-that some database tables don't have primary key columns (as is also the case with views, which can
-also be mapped), the ORM in order to actually map to a particular table needs there
-to be at least one column denoted as a primary key column; multiple-column, i.e. composite, primary keys
-are of course entirely feasible as well.
-
-We define a constructor via ``__init__()`` and also a ``__repr__()`` method - both are optional.  The
-class of course can have any number of other methods and attributes as required by the application,
-as it's basically just a plain Python class.   Inheriting from ``Base`` is also only a requirement
-of the declarative configurational system, which itself is optional and relatively open ended; at its
-core, the SQLAlchemy ORM only requires that a class be a so-called "new style class", that is, it inherits
-from ``object`` in Python 2, in order to be mapped.   All classes in Python 3 are "new style" classes.
-
-.. topic:: The Non Opinionated Philosophy
-
-    In our ``User`` mapping example, it was required that we identify the name of the table
-    in use, as well as the names and characteristics of all columns which we care about,
-    including which column or columns
-    represent the primary key, as well as some basic information about the types in use.
-    SQLAlchemy never makes assumptions about these decisions - the developer must
-    always be explicit about specific conventions in use.   However, that doesn't mean the
-    task can't be automated.  While this tutorial will keep things explicit, developers are
-    encouraged to make use of helper functions as well as "Declarative Mixins" to
-    automate their tasks in large scale applications.  The section :ref:`declarative_mixins`
-    introduces many of these techniques.
+    ...        return "<User(name='%s', fullname='%s', password='%s')>" % (
+    ...                             self.name, self.fullname, self.password)
+
+.. sidebar:: Tip
+
+    The ``User`` class defines a ``__repr__()`` method,
+    but note that is **optional**; we only implement it in
+    this tutorial so that our examples show nicely
+    formatted ``User`` objects.
+
+A class using Declarative at a minimum
+needs a ``__tablename__`` attribute, and at least one
+:class:`.Column` which is part of a primary key [#]_.  SQLAlchemy never makes any
+assumptions by itself about the table to which
+a class refers, including that it has no built-in conventions for names,
+datatypes, or constraints.   But this doesn't mean
+boilerplate is required; instead, you're encouraged to create your
+own automated conventions using helper functions and mixin classes, which
+is described in detail at :ref:`declarative_mixins`.
+
+When our class is constructed, Declarative replaces all the :class:`.Column`
+objects with special Python accessors known as :term:`descriptors`; this is a
+process known as :term:`instrumentation`.   The "instrumented" mapped class
+will provide us with the means to refer to our table in a SQL context as well
+as to persist and load the values of columns from the database.
+
+Outside of what the mapping process does to our class, the class remains
+otherwise mostly a normal Python class, to which we can define any
+number of ordinary attributes and methods needed by our application.
+
+.. [#] For information on why a primary key is required, see
+   :ref:`faq_mapper_primary_key`.
+
+
+Create a Schema
+===============
 
 With our ``User`` class constructed via the Declarative system, we have defined information about
-our table, known as **table metadata**, as well as a user-defined class which is linked to this
-table, known as a **mapped class**.   Declarative has provided for us a shorthand system for what in SQLAlchemy is
-called a "Classical Mapping", which specifies these two units separately and is discussed
-in :ref:`classical_mapping`.   The table
-is actually represented by a datastructure known as :class:`.Table`, and the mapping represented
-by a :class:`.Mapper` object generated by a function called :func:`.mapper`.  Declarative performs both of
-these steps for us, making available the
-:class:`.Table` it has created via the ``__table__`` attribute::
+our table, known as :term:`table metadata`.   The object used by SQLAlchemy to represent
+this information for a specific table is called the :class:`.Table` object, and here Declarative has made
+one for us.  We can see this object by inspecting the ``__table__`` attribute::
 
     >>> User.__table__ # doctest: +NORMALIZE_WHITESPACE
-    Table('users', MetaData(None),
+    Table('users', MetaData(bind=None),
                 Column('id', Integer(), table=<users>, primary_key=True, nullable=False),
                 Column('name', String(), table=<users>),
                 Column('fullname', String(), table=<users>),
                 Column('password', String(), table=<users>), schema=None)
 
-and while rarely needed, making available the :class:`.Mapper` object via the ``__mapper__`` attribute::
-
-    >>> User.__mapper__ # doctest: +ELLIPSIS
-    <Mapper at 0x...; User>
-
-The Declarative base class also contains a catalog of all the :class:`.Table` objects
-that have been defined called :class:`.MetaData`, available via the ``.metadata``
-attribute.  In this example, we are defining
-new tables that have yet to be created in our SQLite database, so one helpful feature
-the :class:`.MetaData` object offers is the ability to issue CREATE TABLE statements
-to the database for all tables that don't yet exist.   We illustrate this
-by calling the :meth:`.MetaData.create_all` method, passing in our :class:`.Engine`
+.. sidebar:: Classical Mappings
+
+    The Declarative system, though highly recommended,
+    is not required in order to use SQLAlchemy's ORM.
+    Outside of Declarative, any
+    plain Python class can be mapped to any :class:`.Table`
+    using the :func:`.mapper` function directly; this
+    less common usage is described at :ref:`classical_mapping`.
+
+When we declared our class, Declarative used a Python metaclass in order to
+perform additional activities once the class declaration was complete; within
+this phase, it then created a :class:`.Table` object according to our
+specifications, and associated it with the class by constructing
+a :class:`.Mapper` object.  This object is a behind-the-scenes object we normally
+don't need to deal with directly (though it can provide plenty of information
+about our mapping when we need it).
+
+The :class:`.Table` object is a member of a larger collection
+known as :class:`.MetaData`.  When using Declarative,
+this object is available using the ``.metadata``
+attribute of our declarative base class.
+
+The :class:`.MetaData`
+is a :term:`registry` which includes the ability to emit a limited set
+of schema generation commands to the database.  As our SQLite database
+does not actually have a ``users`` table present, we can use :class:`.MetaData`
+to issue CREATE TABLE statements to the database for all tables that don't yet exist.
+Below, we call the :meth:`.MetaData.create_all` method, passing in our :class:`.Engine`
 as a source of database connectivity.  We will see that special commands are
 first emitted to check for the presence of the ``users`` table, and following that
 the actual ``CREATE TABLE`` statement:
             fullname = Column(String(50))
             password = Column(String(12))
 
-            def __init__(self, name, fullname, password):
-                self.name = name
-                self.fullname = fullname
-                self.password = password
-
             def __repr__(self):
-                return "<User('%s','%s', '%s')>" % (self.name, self.fullname, self.password)
+                return "<User(name='%s', fullname='%s', password='%s')>" % (
+                                        self.name, self.fullname, self.password)
 
     We include this more verbose table definition separately
     to highlight the difference between a minimal construct geared primarily
 
 With mappings complete, let's now create and inspect a ``User`` object::
 
-    >>> ed_user = User('ed', 'Ed Jones', 'edspassword')
+    >>> ed_user = User(name='ed', fullname='Ed Jones', password='edspassword')
     >>> ed_user.name
     'ed'
     >>> ed_user.password
     >>> str(ed_user.id)
     'None'
 
-The ``id`` attribute, which while not defined by our ``__init__()`` method,
-exists with a value of ``None`` on our ``User`` instance due to the ``id``
-column we declared in our mapping.  By
-default, the ORM creates class attributes for all columns present
-in the table being mapped.   These class attributes exist as
-:term:`descriptors`, and
-define **instrumentation** for the mapped class. The
-functionality of this instrumentation includes the ability to fire on change
-events, track modifications, and to automatically load new data from the database when
-needed.
-
-Since we have not yet told SQLAlchemy to persist ``Ed Jones`` within the
-database, its id is ``None``. When we persist the object later, this attribute
-will be populated with a newly generated value.
-
-.. topic:: The default ``__init__()`` method
-
-   Note that in our ``User`` example we supplied an ``__init__()`` method,
-   which receives ``name``, ``fullname`` and ``password`` as positional arguments.
-   The Declarative system supplies for us a default constructor if one is
-   not already present, which accepts keyword arguments of the same name
-   as that of the mapped attributes.  Below we define ``User`` without
-   specifying a constructor::
-
-       class User(Base):
-           __tablename__ = 'users'
-           id = Column(Integer, primary_key=True)
-           name = Column(String)
-           fullname = Column(String)
-           password = Column(String)
-
-   Our ``User`` class above will make usage of the default constructor, and provide
-   ``id``, ``name``, ``fullname``, and ``password`` as keyword arguments::
-
-       u1 = User(name='ed', fullname='Ed Jones', password='foobar')
+
+.. sidebar:: the ``__init__()`` method
+
+    Our ``User`` class, as defined using the Declarative system, has
+    been provided with a constructor (e.g. ``__init__()`` method) which automatically
+    accepts keyword names that match the columns we've mapped.    We are free
+    to define any explicit ``__init__()`` method we prefer on our class, which
+    will override the default method provided by Declarative.
+
+Even though we didn't specify it in the constructor, the ``id`` attribute
+still produces a value of ``None`` when we access it (as opposed to Python's
+usual behavior of raising ``AttributeError`` for an undefined attribute).
+SQLAlchemy's :term:`instrumentation` normally produces this default value for
+column-mapped attributes when first accessed.    For those attributes where
+we've actually assigned a value, the instrumentation system is tracking
+those assignments for use within an eventual INSERT statement to be emitted to the
+database.
 
 Creating a Session
 ==================
 
     >>> Session.configure(bind=engine)  # once engine is available
 
+.. sidebar:: Session Lifecycle Patterns
+
+    The question of when to make a :class:`.Session` depends a lot on what
+    kind of application is being built.  Keep in mind,
+    the :class:`.Session` is just a workspace for your objects,
+    local to a particular database connection - if you think of
+    an application thread as a guest at a dinner party, the :class:`.Session`
+    is the guest's plate and the objects it holds are the food
+    (and the database...the kitchen?)!  More on this topic
+    available at :ref:`session_faq_whentocreate`.
+
 This custom-made :class:`~sqlalchemy.orm.session.Session` class will create
 new :class:`~sqlalchemy.orm.session.Session` objects which are bound to our
 database. Other transactional characteristics may be defined when calling
 :class:`.Engine`, and holds onto it until we commit all changes and/or close the
 session object.
 
-.. topic:: Session Creational Patterns
-
-   The business of acquiring a :class:`.Session` has a good deal of variety based
-   on the variety of types of applications and frameworks out there.
-   Keep in mind the :class:`.Session` is just a workspace for your objects,
-   local to a particular database connection - if you think of
-   an application thread as a guest at a dinner party, the :class:`.Session`
-   is the guest's plate and the objects it holds are the food
-   (and the database...the kitchen?)!   Hints on
-   how :class:`.Session` is integrated into an application are at
-   :ref:`session_faq`.
 
 Adding New Objects
 ==================
 
 To persist our ``User`` object, we :meth:`~.Session.add` it to our :class:`~sqlalchemy.orm.session.Session`::
 
-    >>> ed_user = User('ed', 'Ed Jones', 'edspassword')
+    >>> ed_user = User(name='ed', fullname='Ed Jones', password='edspassword')
     >>> session.add(ed_user)
 
 At this point, we say that the instance is **pending**; no SQL has yet been issued
      LIMIT ? OFFSET ?
     ('ed', 1, 0)
     {stop}>>> our_user
-    <User('ed','Ed Jones', 'edspassword')>
+    <User(name='ed', fullname='Ed Jones', password='edspassword')>
 
 In fact, the :class:`~sqlalchemy.orm.session.Session` has identified that the
 row returned is the **same** row as one already represented within its
 .. sourcecode:: python+sql
 
     >>> session.add_all([
-    ...     User('wendy', 'Wendy Williams', 'foobar'),
-    ...     User('mary', 'Mary Contrary', 'xxg527'),
-    ...     User('fred', 'Fred Flinstone', 'blah')])
+    ...     User(name='wendy', fullname='Wendy Williams', password='foobar'),
+    ...     User(name='mary', fullname='Mary Contrary', password='xxg527'),
+    ...     User(name='fred', fullname='Fred Flinstone', password='blah')])
 
 Also, Ed has already decided his password isn't too secure, so lets change it:
 
 .. sourcecode:: python+sql
 
     >>> session.dirty
-    IdentitySet([<User('ed','Ed Jones', 'f8s7ccs')>])
+    IdentitySet([<User(name='ed', fullname='Ed Jones', password='f8s7ccs')>])
 
 and that three new ``User`` objects are pending:
 
 .. sourcecode:: python+sql
 
     >>> session.new  # doctest: +SKIP
-    IdentitySet([<User('wendy','Wendy Williams', 'foobar')>,
-    <User('mary','Mary Contrary', 'xxg527')>,
-    <User('fred','Fred Flinstone', 'blah')>])
+    IdentitySet([<User(name='wendy', fullname='Wendy Williams', password='foobar')>,
+    <User(name='mary', fullname='Mary Contrary', password='xxg527')>,
+    <User(name='fred', fullname='Fred Flinstone', password='blah')>])
 
 We tell the :class:`~sqlalchemy.orm.session.Session` that we'd like to issue
 all remaining changes to the database and commit the transaction, which has
 
 .. sourcecode:: python+sql
 
-    >>> fake_user = User('fakeuser', 'Invalid', '12345')
+    >>> fake_user = User(name='fakeuser', fullname='Invalid', password='12345')
     >>> session.add(fake_user)
 
 Querying the session, we can see that they're flushed into the current transaction:
     FROM users
     WHERE users.name IN (?, ?)
     ('Edwardo', 'fakeuser')
-    {stop}[<User('Edwardo','Ed Jones', 'f8s7ccs')>, <User('fakeuser','Invalid', '12345')>]
+    {stop}[<User(name='Edwardo', fullname='Ed Jones', password='f8s7ccs')>, <User(user='fakeuser', fullname='Invalid', password='12345')>]
 
 Rolling back, we can see that ``ed_user``'s name is back to ``ed``, and
 ``fake_user`` has been kicked out of the session:
     FROM users
     WHERE users.name IN (?, ?)
     ('ed', 'fakeuser')
-    {stop}[<User('ed','Ed Jones', 'f8s7ccs')>]
+    {stop}[<User(name='ed', fullname='Ed Jones', password='f8s7ccs')>]
 
 .. _ormtutorial_querying:
 
             users.password AS users_password
     FROM users
     ()
-    {stop}<User('ed','Ed Jones', 'f8s7ccs')> ed
-    <User('wendy','Wendy Williams', 'foobar')> wendy
-    <User('mary','Mary Contrary', 'xxg527')> mary
-    <User('fred','Fred Flinstone', 'blah')> fred
+    {stop}<User(name='ed', fullname='Ed Jones', password='f8s7ccs')> ed
+    <User(name='wendy', fullname='Wendy Williams', password='foobar')> wendy
+    <User(name='mary', fullname='Mary Contrary', password='xxg527')> mary
+    <User(name='fred', fullname='Fred Flinstone', password='blah')> fred
 
 You can control the names of individual column expressions using the
 :meth:`~.CompareMixin.label` construct, which is available from
             user_alias.password AS user_alias_password
     FROM users AS user_alias
     (){stop}
-    <User('ed','Ed Jones', 'f8s7ccs')>
-    <User('wendy','Wendy Williams', 'foobar')>
-    <User('mary','Mary Contrary', 'xxg527')>
-    <User('fred','Fred Flinstone', 'blah')>
+    <User(name='ed', fullname='Ed Jones', password='f8s7ccs')>
+    <User(name='wendy', fullname='Wendy Williams', password='foobar')>
+    <User(name='mary', fullname='Mary Contrary', password='xxg527')>
+    <User(name='fred', fullname='Fred Flinstone', password='blah')>
 
 Basic operations with :class:`~sqlalchemy.orm.query.Query` include issuing
 LIMIT and OFFSET, most conveniently using Python array slices and typically in
     FROM users ORDER BY users.id
     LIMIT ? OFFSET ?
     (2, 1){stop}
-    <User('wendy','Wendy Williams', 'foobar')>
-    <User('mary','Mary Contrary', 'xxg527')>
+    <User(name='wendy', fullname='Wendy Williams', password='foobar')>
+    <User(name='mary', fullname='Mary Contrary', password='xxg527')>
 
 and filtering results, which is accomplished either with
 :func:`~sqlalchemy.orm.query.Query.filter_by`, which uses keyword arguments:
     FROM users
     WHERE users.name = ? AND users.fullname = ?
     ('ed', 'Ed Jones')
-    {stop}<User('ed','Ed Jones', 'f8s7ccs')>
-
+    {stop}<User(name='ed', fullname='Ed Jones', password='f8s7ccs')>
 
 Common Filter Operators
 -----------------------
     FROM users
     WHERE users.name LIKE ? ORDER BY users.id
     ('%ed',)
-    {stop}[<User('ed','Ed Jones', 'f8s7ccs')>, <User('fred','Fred Flinstone', 'blah')>]
+    {stop}[<User(name='ed', fullname='Ed Jones', password='f8s7ccs')>, <User(name='fred', fullname='Fred Flinstone', password='blah')>]
 
 :meth:`~sqlalchemy.orm.query.Query.first()` applies a limit of one and returns
 the first result as a scalar:
     WHERE users.name LIKE ? ORDER BY users.id
      LIMIT ? OFFSET ?
     ('%ed', 1, 0)
-    {stop}<User('ed','Ed Jones', 'f8s7ccs')>
+    {stop}<User(name='ed', fullname='Ed Jones', password='f8s7ccs')>
 
 :meth:`~sqlalchemy.orm.query.Query.one()`, fully fetches all rows, and if not
 exactly one object identity or composite row is present in the result, raises
     FROM users
     WHERE id<? and name=? ORDER BY users.id
     (224, 'fred')
-    {stop}<User('fred','Fred Flinstone', 'blah')>
+    {stop}<User(name='fred', fullname='Fred Flinstone', password='blah')>
 
 To use an entirely string-based statement, using
 :meth:`~sqlalchemy.orm.query.Query.from_statement()`; just ensure that the
     ...                     params(name='ed').all()
     SELECT * FROM users where name=?
     ('ed',)
-    {stop}[<User('ed','Ed Jones', 'f8s7ccs')>]
+    {stop}[<User(name='ed', fullname='Ed Jones', password='f8s7ccs')>]
 
 You can use :meth:`~sqlalchemy.orm.query.Query.from_statement()` to go
 completely "raw", using string names to identify desired columns:
     ('%ed',)
     {stop}2
 
+.. sidebar:: Counting on ``count()``
+
+    :meth:`.Query.count` used to be a very complicated method
+    when it would try to guess whether or not a subquery was needed
+    around the
+    existing query, and in some exotic cases it wouldn't do the right thing.
+    Now that it uses a simple subquery every time, it's only two lines long
+    and always returns the right answer.  Use ``func.count()`` if a
+    particular statement absolutely cannot tolerate the subquery being present.
+
 The :meth:`~.Query.count()` method is used to determine
 how many rows the SQL statement would return.   Looking
 at the generated SQL above, SQLAlchemy always places whatever it is we are
     ...
     ...     user = relationship("User", backref=backref('addresses', order_by=id))
     ...
-    ...     def __init__(self, email_address):
-    ...         self.email_address = email_address
-    ...
     ...     def __repr__(self):
-    ...         return "<Address('%s')>" % self.email_address
+    ...         return "<Address(email_address='%s')>" % self.email_address
 
 The above class introduces the :class:`.ForeignKey` construct, which is a
 directive applied to :class:`.Column` that indicates that values in this
 
 .. sourcecode:: python+sql
 
-    >>> jack = User('jack', 'Jack Bean', 'gjffdd')
+    >>> jack = User(name='jack', fullname='Jack Bean', password='gjffdd')
     >>> jack.addresses
     []
 
 .. sourcecode:: python+sql
 
     >>> jack.addresses[1]
-    <Address('j25@yahoo.com')>
+    <Address(email_address='j25@yahoo.com')>
 
     >>> jack.addresses[1].user
-    <User('jack','Jack Bean', 'gjffdd')>
+    <User(name='jack', fullname='Jack Bean', password='gjffdd')>
 
 Let's add and commit ``Jack Bean`` to the database. ``jack`` as well as the
 two ``Address`` members in his ``addresses`` collection are both added to the
     ('jack',)
 
     {stop}>>> jack
-    <User('jack','Jack Bean', 'gjffdd')>
+    <User(name='jack', fullname='Jack Bean', password='gjffdd')>
 
 Let's look at the ``addresses`` collection.  Watch the SQL:
 
     FROM addresses
     WHERE ? = addresses.user_id ORDER BY addresses.id
     (5,)
-    {stop}[<Address('jack@google.com')>, <Address('j25@yahoo.com')>]
+    {stop}[<Address(email_address='jack@google.com')>, <Address(email_address='j25@yahoo.com')>]
 
 When we accessed the ``addresses`` collection, SQL was suddenly issued. This
 is an example of a **lazy loading relationship**.  The ``addresses`` collection
     ...                     filter(User.id==Address.user_id).\
     ...                     filter(Address.email_address=='jack@google.com').\
     ...                     all():   # doctest: +NORMALIZE_WHITESPACE
-    ...     print u, a
+    ...     print u
+    ...     print a
     SELECT users.id AS users_id,
             users.name AS users_name,
             users.fullname AS users_fullname,
     WHERE users.id = addresses.user_id
             AND addresses.email_address = ?
     ('jack@google.com',)
-    {stop}<User('jack','Jack Bean', 'gjffdd')> <Address('jack@google.com')>
+    {stop}<User(name='jack', fullname='Jack Bean', password='gjffdd')>
+    <Address(email_address='jack@google.com')>
 
-The actual SQL JOIN syntax, on the other hand, is most easily achieved using the :meth:`.Query.join`
-method:
+The actual SQL JOIN syntax, on the other hand, is most easily achieved
+using the :meth:`.Query.join` method:
 
 .. sourcecode:: python+sql
 
     FROM users JOIN addresses ON users.id = addresses.user_id
     WHERE addresses.email_address = ?
     ('jack@google.com',)
-    {stop}[<User('jack','Jack Bean', 'gjffdd')>]
+    {stop}[<User(name='jack', fullname='Jack Bean', email_address='gjffdd')>]
 
 :meth:`.Query.join` knows how to join between ``User``
 and ``Address`` because there's only one foreign key between them. If there
         ON users.id = anon_1.user_id
     ORDER BY users.id
     ('*',)
-    {stop}<User('ed','Ed Jones', 'f8s7ccs')> None
-    <User('wendy','Wendy Williams', 'foobar')> None
-    <User('mary','Mary Contrary', 'xxg527')> None
-    <User('fred','Fred Flinstone', 'blah')> None
-    <User('jack','Jack Bean', 'gjffdd')> 2
+    {stop}<User(name='ed', fullname='Ed Jones', password='f8s7ccs')> None
+    <User(name='wendy', fullname='Wendy Williams', password='foobar')> None
+    <User(name='mary', fullname='Mary Contrary', password='xxg527')> None
+    <User(name='fred', fullname='Fred Flinstone', password='blah')> None
+    <User(name='jack', fullname='Jack Bean', password='gjffdd')> 2
 
 Selecting Entities from Subqueries
 ----------------------------------
     >>> adalias = aliased(Address, stmt)
     >>> for user, address in session.query(User, adalias).\
     ...         join(adalias, User.addresses): # doctest: +NORMALIZE_WHITESPACE
-    ...     print user, address
+    ...     print user
+    ...     print address
     SELECT users.id AS users_id,
                 users.name AS users_name,
                 users.fullname AS users_fullname,
         WHERE addresses.email_address != ?) AS anon_1
         ON users.id = anon_1.user_id
     ('j25@yahoo.com',)
-    {stop}<User('jack','Jack Bean', 'gjffdd')> <Address('jack@google.com')>
+    {stop}<User(name='jack', fullname='Jack Bean', password='gjffdd')>
+    <Address(email_address='jack@google.com')>
 
 Using EXISTS
 ------------
     ORDER BY anon_1.users_id, addresses.id
     ('jack',)
     {stop}>>> jack
-    <User('jack','Jack Bean', 'gjffdd')>
+    <User(name='jack', fullname='Jack Bean', password='gjffdd')>
 
     >>> jack.addresses
-    [<Address('jack@google.com')>, <Address('j25@yahoo.com')>]
+    [<Address(email_address='jack@google.com')>, <Address(email_address='j25@yahoo.com')>]
 
 Joined Load
 -------------
     ('jack',)
 
     {stop}>>> jack
-    <User('jack','Jack Bean', 'gjffdd')>
+    <User(name='jack', fullname='Jack Bean', password='gjffdd')>
 
     >>> jack.addresses
-    [<Address('jack@google.com')>, <Address('j25@yahoo.com')>]
+    [<Address(email_address='jack@google.com')>, <Address(email_address='j25@yahoo.com')>]
 
 Note that even though the OUTER JOIN resulted in two rows, we still only got
 one instance of ``User`` back.  This is because :class:`.Query` applies a "uniquing"
     ('jack',)
 
     {stop}>>> jacks_addresses
-    [<Address('jack@google.com')>, <Address('j25@yahoo.com')>]
+    [<Address(email_address='jack@google.com')>, <Address(email_address='j25@yahoo.com')>]
 
     >>> jacks_addresses[0].user
-    <User('jack','Jack Bean', 'gjffdd')>
+    <User(name='jack', fullname='Jack Bean', password='gjffdd')>
 
 For more information on eager loading, including how to configure various forms
 of loading by default, see the section :doc:`/orm/loading`.
     ...     fullname = Column(String)
     ...     password = Column(String)
     ...
-    ...     addresses = relationship("Address", backref='user', cascade="all, delete, delete-orphan")
+    ...     addresses = relationship("Address", backref='user',
+    ...                     cascade="all, delete, delete-orphan")
     ...
     ...     def __repr__(self):
-    ...        return "<User('%s','%s', '%s')>" % (self.name, self.fullname, self.password)
+    ...        return "<User(name='%s', fullname='%s', password'%s')>" % (
+    ...                                self.name, self.fullname, self.password)
 
 Then we recreate ``Address``, noting that in this case we've created the ``Address.user`` relationship
 via the ``User`` class already::
     ...     user_id = Column(Integer, ForeignKey('users.id'))
     ...
     ...     def __repr__(self):
-    ...         return "<Address('%s')>" % self.email_address
+    ...         return "<Address(email_address='%s')>" % self.email_address
 
 Now when we load Jack (below using :meth:`~.Query.get`, which loads by primary key),
 removing an address from his ``addresses`` collection will result in that
     ...     def __init__(self, keyword):
     ...         self.keyword = keyword
 
+.. note::
+
+    The above class declarations illustrate explicit ``__init__()`` methods.
+    Remember, when using Declarative, it's optional!
+
 Above, the many-to-many relationship is ``BlogPost.keywords``. The defining
 feature of a many-to-many relationship is the ``secondary`` keyword argument
 which references a :class:`~sqlalchemy.schema.Table` object representing the
             AND keywords.id = post_keywords.keyword_id
             AND keywords.keyword = ?)
     ('firstpost',)
-    {stop}[BlogPost("Wendy's Blog Post", 'This is a test', <User('wendy','Wendy Williams', 'foobar')>)]
+    {stop}[BlogPost("Wendy's Blog Post", 'This is a test', <User(name='wendy', fullname='Wendy Williams', password='foobar')>)]
 
 If we want to look up just Wendy's posts, we can tell the query to narrow down
 to her as a parent:
             AND keywords.id = post_keywords.keyword_id
             AND keywords.keyword = ?))
     (2, 'firstpost')
-    {stop}[BlogPost("Wendy's Blog Post", 'This is a test', <User('wendy','Wendy Williams', 'foobar')>)]
+    {stop}[BlogPost("Wendy's Blog Post", 'This is a test', <User(name='wendy', fullname='Wendy Williams', password='foobar')>)]
 
 Or we can use Wendy's own ``posts`` relationship, which is a "dynamic"
 relationship, to query straight from there:
             AND keywords.id = post_keywords.keyword_id
             AND keywords.keyword = ?))
     (2, 'firstpost')
-    {stop}[BlogPost("Wendy's Blog Post", 'This is a test', <User('wendy','Wendy Williams', 'foobar')>)]
+    {stop}[BlogPost("Wendy's Blog Post", 'This is a test', <User(name='wendy', fullname='Wendy Williams', password='foobar')>)]
 
 Further Reference
 ==================

doc/build/static/docs.css

     text-align:right;
 }
 
+div.section {
+  clear:right;
+}
 div.note, div.warning, p.deprecated, div.topic, div.admonition  {
     background-color:#EEFFEF;
 }
 
+.footnote {
+  font-size: .95em;
+}
 
 div.faq {
   background-color: #EFEFEF;
     background-color: #FFFFEE;
     border: 1px solid #DDDDBB;
     float: right;
-    margin: 0 0 0.5em 1em;
+    margin: 10px 0 10px 1em;
     padding: 7px 7px 0;
     width: 40%;
     font-size:.9em;

doc/build/testdocs.py

         raise ValueError("Couldn't find suitable create_engine call to replace '%s' in it" % oldfile)
     return s
 
-#for filename in 'orm/tutorial','core/tutorial',:
-for filename in 'core/tutorial',:
+for filename in 'orm/tutorial','core/tutorial',:
     filename = '%s.rst' % filename
     s = open(filename).read()
     #s = replace_file(s, ':memory:')
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.