From: Mike Bayer Date: Sun, 9 Sep 2012 06:06:32 +0000 (-0400) Subject: almost obliterate the concept of "implicit execution" from the docs, move it only X-Git-Tag: rel_0_7_9~35 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8fe080fa3eaa2e182e3ac9df39e394794628487c;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git almost obliterate the concept of "implicit execution" from the docs, move it only to the "engines and connections" chapter nobody reads, put big green "note:" boxes with the word "discouraged" in them for "bound metadata", "implicit execution", "threadlocal strategy" --- diff --git a/doc/build/core/connections.rst b/doc/build/core/connections.rst index 1564e6aca4..92ea20f9cc 100644 --- a/doc/build/core/connections.rst +++ b/doc/build/core/connections.rst @@ -114,11 +114,12 @@ Using Transactions object internally. See :ref:`unitofwork_transaction` for further information. -The :class:`~sqlalchemy.engine.base.Connection` object provides a ``begin()`` -method which returns a :class:`~sqlalchemy.engine.base.Transaction` object. +The :class:`~sqlalchemy.engine.Connection` object provides a :meth:`~.Connection.begin` +method which returns a :class:`.Transaction` object. This object is usually used within a try/except clause so that it is -guaranteed to ``rollback()`` or ``commit()``:: +guaranteed to invoke :meth:`.Transaction.rollback` or :meth:`.Transaction.commit`:: + connection = engine.connect() trans = connection.begin() try: r1 = connection.execute(table1.select()) @@ -128,15 +129,30 @@ guaranteed to ``rollback()`` or ``commit()``:: trans.rollback() raise +The above block can be created more succinctly using context +managers, either given an :class:`.Engine`:: + + # runs a transaction + with engine.begin() as connection: + r1 = connection.execute(table1.select()) + connection.execute(table1.insert(), col1=7, col2='this is some data') + +Or from the :class:`.Connection`, in which case the :class:`.Transaction` object +is available as well:: + + with connection.begin() as trans: + r1 = connection.execute(table1.select()) + connection.execute(table1.insert(), col1=7, col2='this is some data') + .. _connections_nested_transactions: Nesting of Transaction Blocks ------------------------------ -The :class:`~sqlalchemy.engine.base.Transaction` object also handles "nested" +The :class:`.Transaction` object also handles "nested" behavior by keeping track of the outermost begin/commit pair. In this example, -two functions both issue a transaction on a Connection, but only the outermost -Transaction object actually takes effect when it is committed. +two functions both issue a transaction on a :class:`.Connection`, but only the outermost +:class:`.Transaction` object actually takes effect when it is committed. .. sourcecode:: python+sql @@ -230,7 +246,11 @@ Recall from the first section we mentioned executing with and without explicit usage of :class:`.Connection`. "Connectionless" execution refers to the usage of the ``execute()`` method on an object which is not a :class:`.Connection`. This was illustrated using the :meth:`~.Engine.execute` method -of :class:`.Engine`. +of :class:`.Engine`:: + + result = engine.execute("select username from users") + for row in result: + print "username:", row['username'] In addition to "connectionless" execution, it is also possible to use the :meth:`~.Executable.execute` method of @@ -241,6 +261,8 @@ in order to provide so-called "implicit" execution services. Given a table as below:: + from sqlalchemy import MetaData, Table, Column, Integer + meta = MetaData() users_table = Table('users', meta, Column('id', Integer, primary_key=True), @@ -248,7 +270,7 @@ Given a table as below:: ) Explicit execution delivers the SQL text or constructed SQL expression to the -``execute()`` method of :class:`~sqlalchemy.engine.base.Connection`: +:meth:`~.Connection.execute` method of :class:`~sqlalchemy.engine.Connection`: .. sourcecode:: python+sql @@ -260,7 +282,7 @@ Explicit execution delivers the SQL text or constructed SQL expression to the connection.close() Explicit, connectionless execution delivers the expression to the -``execute()`` method of :class:`~sqlalchemy.engine.base.Engine`: +:meth:`~.Engine.execute` method of :class:`~sqlalchemy.engine.Engine`: .. sourcecode:: python+sql @@ -270,14 +292,17 @@ Explicit, connectionless execution delivers the expression to the # .... result.close() -Implicit execution is also connectionless, and calls the ``execute()`` method -on the expression itself, utilizing the fact that either an -:class:`~sqlalchemy.engine.base.Engine` or -:class:`~sqlalchemy.engine.base.Connection` has been *bound* to the expression -object (binding is discussed further in -:ref:`metadata_toplevel`): - -.. sourcecode:: python+sql +Implicit execution is also connectionless, and makes usage of the :meth:`~.Executable.execute` method +on the expression itself. This method is provided as part of the +:class:`.Executable` class, which refers to a SQL statement that is sufficient +for being invoked against the database. The method makes usage of +the assumption that either an +:class:`~sqlalchemy.engine.Engine` or +:class:`~sqlalchemy.engine.Connection` has been **bound** to the expression +object. By "bound" we mean that the special attribute :attr:`.MetaData.bind` +has been used to associate a series of +:class:`.Table` objects and all SQL constructs derived from them with a specific +engine:: engine = create_engine('sqlite:///file.db') meta.bind = engine @@ -286,14 +311,45 @@ object (binding is discussed further in # .... result.close() +Above, we associate an :class:`.Engine` with a :class:`.MetaData` object using +the special attribute :attr:`.MetaData.bind`. The :func:`.select` construct produced +from the :class:`.Table` object has a method :meth:`~.Executable.execute`, which will +search for an :class:`.Engine` that's "bound" to the :class:`.Table`. + +.. note:: + + The concepts of "bound metadata" and "implicit execution" are not emphasized in modern SQLAlchemy. + In applications + where multiple :class:`.Engine` objects are present, each one logically associated + with a certain set of tables (i.e. *vertical sharding*), the "bound metadata" technique can be used + so that individual :class:`.Table` can refer to the appropriate :class:`.Engine` automatically; + in particular this is supported within the ORM via the :class:`.Session` object + as a means to associate :class:`.Table` objects with an appropriate :class:`.Engine`, + as an alternative to using the bind arguments accepted directly by the :class:`.Session`. + However, the "implicit execution" technique is not at all appropriate for use with the + ORM, as it bypasses the transactional context maintained by the :class:`.Session`. + + Overall, in the *vast majority* of cases, "bound metadata" and "implicit execution" + are **not useful**. While "bound metadata" has a marginal level of usefulness with regards to + ORM configuration, "implicit execution" is a very old usage pattern that in most + cases is more confusing than it is helpful, and its usage is discouraged. + + Modern SQLAlchemy usage, especially the ORM, places a heavy stress on working within the context + of a transaction at all times; the "implicit execution" concept makes the job of + associating statement execution with a particular transaction much more difficult. + The :meth:`.Executable.execute` method on a particular SQL statement + usually implies that the execution is not part of any particular transaction, which is + usually not the desired effect. + In both "connectionless" examples, the -:class:`~sqlalchemy.engine.base.Connection` is created behind the scenes; the -:class:`~sqlalchemy.engine.base.ResultProxy` returned by the ``execute()`` -call references the :class:`~sqlalchemy.engine.base.Connection` used to issue +:class:`~sqlalchemy.engine.Connection` is created behind the scenes; the +:class:`~sqlalchemy.engine.ResultProxy` returned by the ``execute()`` +call references the :class:`~sqlalchemy.engine.Connection` used to issue the SQL statement. When the :class:`.ResultProxy` is closed, the underlying :class:`.Connection` is closed for us, resulting in the DBAPI connection being returned to the pool with transactional resources removed. + .. _threadlocal_strategy: Using the Threadlocal Execution Strategy @@ -304,14 +360,18 @@ can be used by non-ORM applications to associate transactions with the current thread, such that all parts of the application can participate in that transaction implicitly without the need to explicitly reference a :class:`.Connection`. -"threadlocal" is designed for a very specific pattern of use, and is not -appropriate unless this very specfic pattern, described below, is what's -desired. It has **no impact** on the "thread safety" of SQLAlchemy components -or one's application. It also should not be used when using an ORM -:class:`~sqlalchemy.orm.session.Session` object, as the -:class:`~sqlalchemy.orm.session.Session` itself represents an ongoing -transaction and itself handles the job of maintaining connection and -transactional resources. + +.. note:: + + The "threadlocal" feature is generally discouraged. It's + designed for a particular pattern of usage which is generally + considered as a legacy pattern. It has **no impact** on the "thread safety" + of SQLAlchemy components + or one's application. It also should not be used when using an ORM + :class:`~sqlalchemy.orm.session.Session` object, as the + :class:`~sqlalchemy.orm.session.Session` itself represents an ongoing + transaction and itself handles the job of maintaining connection and + transactional resources. Enabling ``threadlocal`` is achieved as follows:: diff --git a/doc/build/core/schema.rst b/doc/build/core/schema.rst index dae9686a58..e382a73e2d 100644 --- a/doc/build/core/schema.rst +++ b/doc/build/core/schema.rst @@ -279,83 +279,12 @@ There are two major migration tools available for SQLAlchemy: Binding MetaData to an Engine or Connection -------------------------------------------- -Notice in the previous section the creator/dropper methods accept an argument -for the database engine in use. When a schema construct is combined with an -:class:`~sqlalchemy.engine.base.Engine` object, or an individual -:class:`~sqlalchemy.engine.base.Connection` object, we call this the *bind*. -In the above examples the bind is associated with the schema construct only -for the duration of the operation. However, the option exists to persistently -associate a bind with a set of schema constructs via the -:class:`~sqlalchemy.schema.MetaData` object's ``bind`` attribute:: - - engine = create_engine('sqlite://') - - # create MetaData - meta = MetaData() - - # bind to an engine - meta.bind = engine - -We can now call methods like :func:`~sqlalchemy.schema.MetaData.create_all` -without needing to pass the :class:`~sqlalchemy.engine.base.Engine`:: - - meta.create_all() - -The MetaData's bind is used for anything that requires an active connection, -such as loading the definition of a table from the database automatically -(called *reflection*):: - - # describe a table called 'users', query the database for its columns - users_table = Table('users', meta, autoload=True) - -As well as for executing SQL constructs that are derived from that MetaData's table objects:: - - # generate a SELECT statement and execute - result = users_table.select().execute() - -Binding the MetaData to the Engine is a **completely optional** feature. The -above operations can be achieved without the persistent bind using -parameters:: - - # describe a table called 'users', query the database for its columns - users_table = Table('users', meta, autoload=True, autoload_with=engine) - - # generate a SELECT statement and execute - result = engine.execute(users_table.select()) - -Should you use bind ? It's probably best to start without it, and wait for a -specific need to arise. Bind is useful if: - -* You aren't using the ORM, are usually using "connectionless" execution, and - find yourself constantly needing to specify the same - :class:`~sqlalchemy.engine.base.Engine` object throughout the entire - application. Bind can be used here to provide "implicit" execution. -* Your application has multiple schemas that correspond to different engines. - Using one :class:`~sqlalchemy.schema.MetaData` for each schema, bound to - each engine, provides a decent place to delineate between the schemas. The - ORM will also integrate with this approach, where the :class:`.Session` will - naturally use the engine that is bound to each table via its metadata - (provided the :class:`.Session` itself has no ``bind`` configured.). - -Alternatively, the ``bind`` attribute of :class:`~sqlalchemy.schema.MetaData` -is *confusing* if: - -* Your application talks to multiple database engines at different times, - which use the *same* set of :class:`.Table` objects. It's usually confusing - and unnecessary to begin to create "copies" of :class:`.Table` objects just - so that different engines can be used for different operations. An example - is an application that writes data to a "master" database while performing - read-only operations from a "read slave". A global - :class:`~sqlalchemy.schema.MetaData` object is *not* appropriate for - per-request switching like this, although a - :class:`~sqlalchemy.schema.ThreadLocalMetaData` object is. -* You are using the ORM :class:`.Session` to handle which class/table is bound - to which engine, or you are using the :class:`.Session` to manage switching - between engines. Its a good idea to keep the "binding of tables to engines" - in one place - either using :class:`~sqlalchemy.schema.MetaData` only (the - :class:`.Session` can of course be present, it just has no ``bind`` - configured), or using :class:`.Session` only (the ``bind`` attribute of - :class:`~sqlalchemy.schema.MetaData` is left empty). +The :class:`.MetaData` object can be associated directly with an :class:`.Engine` +or :class:`.Connection` so that SQL statement objects gain an :meth:`.Executable.execute` +method, and also provide an alternate means to the ORM :class:`.Session` object +in order to locate an :class:`.Engine`, given a particular mapping to execute a +query against. However, this pattern is **not recommended for general use**. +For background, see, :ref:`dbengine_implicit`. Specifying the Schema Name --------------------------- diff --git a/doc/build/core/tutorial.rst b/doc/build/core/tutorial.rst index 96fa65f4a2..120e0b45bf 100644 --- a/doc/build/core/tutorial.rst +++ b/doc/build/core/tutorial.rst @@ -340,52 +340,6 @@ statement is compiled against the **first** dictionary in the list, and it's assumed that all subsequent argument dictionaries are compatible with that statement. -Connectionless / Implicit Execution -==================================== - -We're executing our :class:`~sqlalchemy.sql.expression.Insert` using a -:class:`~sqlalchemy.engine.base.Connection`. There's two options that allow -you to not have to deal with the connection part. You can execute in the -**connectionless** style, using the engine, which checks out from the -connection pool a connection for you, performs the execute operation with that -connection, and then checks the connection back into the pool upon completion -of the operation: - -.. sourcecode:: pycon+sql - - {sql}>>> result = engine.execute(users.insert(), name='fred', fullname="Fred Flintstone") - INSERT INTO users (name, fullname) VALUES (?, ?) - ('fred', 'Fred Flintstone') - COMMIT - -and you can save even more steps than that, if you connect the -:class:`~sqlalchemy.engine.base.Engine` to the -:class:`~sqlalchemy.schema.MetaData` object we created earlier. When this is -done, all SQL expressions which involve tables within the -:class:`~sqlalchemy.schema.MetaData` object will be automatically **bound** to -the :class:`~sqlalchemy.engine.base.Engine`. In this case, we call it -**implicit execution**: - -.. sourcecode:: pycon+sql - - >>> metadata.bind = engine - {sql}>>> result = users.insert().execute(name="mary", fullname="Mary Contrary") - INSERT INTO users (name, fullname) VALUES (?, ?) - ('mary', 'Mary Contrary') - COMMIT - -When the :class:`~sqlalchemy.schema.MetaData` is bound, statements will also -compile against the engine's dialect. Since a lot of the examples here assume -the default dialect, we'll detach the engine from the metadata which we just -attached: - -.. sourcecode:: pycon+sql - - >>> metadata.bind = None - -Detailed examples of connectionless and implicit execution are available in -the "Engines" chapter: :ref:`dbengine_implicit`. - .. _coretutorial_selecting: Selecting diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py index 0817614ee0..26ad8772d9 100644 --- a/lib/sqlalchemy/schema.py +++ b/lib/sqlalchemy/schema.py @@ -2404,11 +2404,20 @@ class MetaData(SchemaItem): return self._bind is not None def bind(self): - """An Engine or Connection to which this MetaData is bound. + """An :class:`.Engine` or :class:`.Connection` to which this + :class:`.MetaData` is bound. - This property may be assigned an ``Engine`` or ``Connection``, or - assigned a string or URL to automatically create a basic ``Engine`` - for this bind with ``create_engine()``. + Typically, a :class:`.Engine` is assigned to this attribute + so that "implicit execution" may be used, or alternatively + as a means of providing engine binding information to an + ORM :class:`.Session` object:: + + engine = create_engine("someurl://") + metadata.bind = engine + + .. seealso:: + + :ref:`dbengine_implicit` - background on "bound metadata" """ return self._bind