Compare commits

...
1659 Commits
Author SHA1 Message Date
Mike Bayer fba9709593 - official name
- remove the switch readme
2010-02-03 16:59:10 +00:00
Mike Bayer bde3ceb1d9 the order of rollback()s wasn't correct. slightly disturbing as the test usually passed,
began failing on PG as of somewhat unrelated commit r6705, and only when the full test/engine series
of tests were run.  very heisenbuggy. may want to add tests to assert that TLEngine is enforcing
nesting even with subtransactions.
2010-02-02 23:26:34 +00:00
Mike Bayer c1e0978556 - Primary key values can now be changed on a joined-table inheritance
object, and ON UPDATE CASCADE will be taken into account when
the flush happens.  Set the new "passive_updates" flag to False
on mapper() when using SQLite or MySQL/MyISAM. [ticket:1362]

- flush() now detects when a primary key column was updated by
an ON UPDATE CASCADE operation from another primary key, and
can then locate the row for a subsequent UPDATE on the new PK
value.  This occurs when a relation() is there to establish
the relationship as well as passive_updates=True.  [ticket:1671]
2010-02-02 22:56:19 +00:00
Mike Bayer 9a84fa585b formatting tweak 2010-02-02 01:48:01 +00:00
Mike Bayer af94c93387 add 0.5.9 note for [ticket:1661] 2010-02-02 01:17:44 +00:00
Mike Bayer 53d7530ee8 - added a failing-so-far test for #1671 2010-02-01 23:38:51 +00:00
Mike Bayer 0421668763 yikes entirely wrong option name here 2010-01-31 15:49:51 +00:00
Mike Bayer 6b82c6e89b - use_ansi=False won't leak into the FROM/WHERE clause of
a statement that's selecting from a subquery that also
uses JOIN/OUTERJOIN.
2010-01-30 19:00:40 +00:00
Mike Bayer 489d5010fd - the "save-update" cascade will now cascade the pending *removed*
values from a scalar or collection attribute into the new session
during an add() operation.  This so that the flush() operation
will also delete or modify rows of those disconnected items.
2010-01-30 18:28:37 +00:00
Mike Bayer 5d265624e7 - the "connection" argument from engine.transaction() and
engine.run_callable() is removed - Connection itself
now has those methods.   All four methods accept
*args and **kwargs which are passed to the given callable,
as well as the operating connection.
2010-01-29 22:20:55 +00:00
Mike Bayer bb0e6d5edd oursql doesn't need the warnings propagate flag 2010-01-29 20:21:47 +00:00
Mike Bayer 5935b9e367 for string deferred evals, don't return the underlying Column, rely upon the original propcomparator to do what it wants.
reduces duplication of the "columns[0]" rule and removes potentially surprise behavior from the eval
2010-01-29 18:55:03 +00:00
Mike Bayer dfcc375e17 fix the kwargs scoping. mysteriously was affecting pool gcing 2010-01-29 02:16:48 +00:00
Mike Bayer a04c4dc42c - inline some code and turn some instance-level defaults into class level 2010-01-29 02:01:11 +00:00
Mike Bayer e78cee6618 - the "autocommit" flag on select() and text() as well
as select().autocommit() are deprecated - now call
      .execution_options(autocommit=True) on either of those
      constructs, also available directly on Connection and orm.Query.
2010-01-28 23:49:22 +00:00
Mike Bayer 0b185fc84f - make frozendict serializable
- serialize tests use HIGHEST_PROTOCOL
2010-01-28 22:47:25 +00:00
Mike Bayer bf163f0237 against is optional 2010-01-28 22:14:34 +00:00
Mike Bayer d1916eb6c9 - allow exists(s.as_scalar()) to work 2010-01-28 21:13:38 +00:00
Mike Bayer 0ca4107c3e add an informative error msg for non-collection passed to select() 2010-01-28 20:30:42 +00:00
Lele Gaifax a8220e2a8a Fix #1663: the whitespace after DEFAULT may start with a newline 2010-01-28 10:52:08 +00:00
Philip Jenvey ff09d5709d missing import, forcefully compile the expression to str 2010-01-26 23:23:13 +00:00
Philip Jenvey 8d22a984be oracle compat 2010-01-26 06:17:02 +00:00
Philip Jenvey cdfe09794a Binary -> LargeBinary 2010-01-26 06:15:20 +00:00
Mike Bayer 73bfc87669 - Added a tuple_() construct, allows sets of expressions
to be compared to another set, typically with IN against
composite primary keys or similar.  Also accepts an
IN with multiple columns.   The "scalar select can
have only one column" error message is removed - will
rely upon the database to report problems with
col mismatch.
2010-01-25 21:04:50 +00:00
Mike Bayer ba53c6e844 added a test to ensure the concrete example in the docs works 2010-01-25 16:22:07 +00:00
Philip Jenvey 649b34155e handle the new CursorFairy __setattr__ 2010-01-25 04:56:47 +00:00
Mike Bayer 934d3bc164 fricking typo 2010-01-25 00:47:02 +00:00
Mike Bayer 6a0fa04c58 remove my comment. still wish this could be done in a cleaner way tho 2010-01-25 00:41:55 +00:00
Mike Bayer 67e7f45c59 - union(), intersect(), except() and other "compound" types
of statements have more consistent behavior w.r.t.
parenthesizing.   Each compound element embedded within
another will now be grouped with parenthesis - previously,
the first compound element in the list would not be grouped,
as SQLite doesn't like a statement to start with
parenthesis.   However, Postgresql in particular has
precedence rules regarding INTERSECT, and it is
more consistent for parenthesis to be applied equally
to all sub-elements.   So now, the workaround for SQLite
is also what the workaround for PG was previously -
when nesting compound elements, the first one usually needs
".alias().select()" called on it to wrap it inside
of a subquery.  [ticket:1665]
2010-01-25 00:35:28 +00:00
Philip Jenvey c0835ffdc2 revert r6686 and adjust the stacklevel of test_notsane_warning's SAWarning so
it can force it to be emitted
2010-01-25 00:32:47 +00:00
Mike Bayer 770e1ddc13 - Connection has execution_options(), generative method
which accepts keywords that affect how the statement
is executed w.r.t. the DBAPI.   Currently supports
"stream_results", causes psycopg2 to use a server
side cursor for that statement.   Can also be set
upon select() and text() constructs directly as well
as ORM Query().
2010-01-24 22:50:58 +00:00
Mike Bayer d3e49722d1 query 2010-01-24 21:42:21 +00:00
Philip Jenvey 8bdf8b3a94 test_notsane_working needs to run first for dialects that don't
supports_sane_rowcount so the other VersioningTests don't ignore the warning it
expects (that ignore lasts forever)
2010-01-24 21:32:09 +00:00
Mike Bayer 20a9cd8013 not ready to put execution_options in the text()/select() constructors yet 2010-01-24 19:18:55 +00:00
Mike Bayer f20102829e - move "should_autocommit" to a deferred method. connection wont call it if a transaction is in progress. 2010-01-24 19:01:11 +00:00
Mike Bayer dd01f817b7 - oracle + firebird: "case sensitivity" feature will detect an all-lowercase
case-sensitive column name during reflect and add
"quote=True" to the generated Column, so that proper
quoting is maintained.
2010-01-24 18:41:30 +00:00
Mike Bayer 9806d81675 - the "threadlocal" engine has been rewritten and simplified
and now supports SAVEPOINT operations.
2010-01-24 18:13:21 +00:00
Mike Bayer 72d1cbadde clarify intent 2010-01-24 15:37:50 +00:00
Philip Jenvey 77b6e981a4 disable SAWarning exceptions when supports_sane_rowcount isn't supported so
VersioningTest can complete
2010-01-23 21:35:40 +00:00
Mike Bayer fc92d14bbe - types.Binary is renamed to types.LargeBinary, it only
produces BLOB, BYTEA, or a similar "long binary" type.
New base BINARY and VARBINARY
types have been added to access these MySQL/MS-SQL specific
types in an agnostic way [ticket:1664].
2010-01-23 19:44:06 +00:00
Mike Bayer 2d15d9b0d0 - association_proxy now has basic comparator methods .any(),
.has(), .contains(), ==, !=, thanks to Scott Torborg.
[ticket:1372]
2010-01-22 20:24:27 +00:00
Philip Jenvey 4a18506d4d not applicable to zxjdbc 2010-01-22 02:15:22 +00:00
Philip Jenvey 6a09ccd0d3 arrange imports, cleanup 2010-01-22 02:11:56 +00:00
Mike Bayer 8301a266b5 send along the sqlsoup engine for encoding purposes. 2010-01-21 20:52:37 +00:00
Mike Bayer 196284c083 - ensure correct session usage + tests 2010-01-21 20:41:52 +00:00
Mike Bayer bf6c88fe23 use issubclass here, allows lazy loads from a subclass to hit a loader that was configured on base 2010-01-21 17:40:41 +00:00
Mike Bayer d6aa10d7e9 add autoflush to the list of attributes exported on scoped_session 2010-01-21 16:23:22 +00:00
Mike Bayer c5e29f0eed fixed the illegal_initial_chars collection + unit test, [ticket:1659] 2010-01-21 15:56:23 +00:00
Mike Bayer cd70a42007 base supports_native_enum on 8.3 or greater 2010-01-21 03:41:35 +00:00
Mike Bayer da21efabb4 - agnosticize checking for the two phase events 2010-01-21 01:46:06 +00:00
Gaëtan de Menten 463f73f80d fix ResultProxy for SQLite truncated names 2010-01-20 20:47:20 +00:00
Mike Bayer 6973635136 - query.select_from() also accepts mapped classes, aliased()
constructs, and mappers as arguments.  In particular this
    helps when querying from multiple joined-table classes to ensure
    the full join gets rendered.
2010-01-20 19:26:12 +00:00
Mike Bayer c3702fa516 moved the metadata step of ResultProxy into a ResultMetaData object. this also replaces PickledResultProxy.
Allows RowProxy objects to reference just the metadata they need and provides the "core" of ResultProxy
detached from the object itself, allowing ResultProxy implementations to vary more easily.  will also
enable [ticket:1635]
2010-01-20 17:48:43 +00:00
Mike Bayer 81372486d9 lessons learned unpickling from an 0.5 cache 2010-01-19 23:25:43 +00:00
Mike Bayer 40f8aadd58 - mega example cleanup
- added READMEs to all examples in each __init__.py and added to sphinx documentation
- added versioning example
- removed vertical/vertical.py, the dictlikes are more straightforward
2010-01-19 00:53:12 +00:00
Mike Bayer 56fe538cc7 some cleanup 2010-01-18 21:47:50 +00:00
Mike Bayer f15eb50a75 sorry, this example is just ridiculous 2010-01-18 21:35:18 +00:00
Mike Bayer 7f91210e94 modernized "adjacencytree" example 2010-01-18 21:29:55 +00:00
Mike Bayer 8a9e2a6c37 updated the large_collection example to modern SQLA. 2010-01-18 20:58:34 +00:00
Mike Bayer 9680e6483f - added native INTERVAL type to the dialect. This supports
only the DAY TO SECOND interval type so far due to lack
  of support in cx_oracle for YEAR TO MONTH. [ticket:1467]
- The Interval type includes a "native" flag which controls
  if native INTERVAL types (postgresql + oracle) are selected
  if available, or not.  "day_precision" and "second_precision"
  arguments are also added which propagate as appropriately
  to these native types. Related to [ticket:1467].
- DefaultDialect.type_descriptor moves back to being per-dialect.
  TypeEngine/TypeDecorator key type impls to the dialect class
  + server_version_info so that the colspecs dict can be modified
  per-dialect based on server version.
- Fixed TypeDecorator's incorrect usage of _impl_dict
2010-01-18 03:00:05 +00:00
Mike Bayer e9076d04b0 - raise error when unpickling non-mapped state, [ticket:1610]
- remove pickle language from regular unmapped class error
2010-01-17 22:23:54 +00:00
Mike Bayer 8fa55917ac - add a unit test for r6089 / [ticket:1438] 2010-01-17 21:49:31 +00:00
Mike Bayer 3188ad6043 - implement dynamic type_affinity for Oracle.NUMBER
- standardize type tests on type affinity matches
2010-01-17 21:29:03 +00:00
Mike Bayer 8f4871eaf2 - remove the exclusion of cx_oracle.STRING from setinputsizes by
configuring cx_oracle.UNICODE on OracleNVarChar.  Attempts
were made to pass unicode data to/from a plain VARCHAR2 with
cx_oracle, both with and without setinputsizes in use, but
it doesn't appear to be possible - therefore users will need to use
Unicode/UnicodeText with oracle if data contains non-ASCII info.
[ticket:1517]
- updated the Unicode/UnicodeText docs to reflect this, that
convert_unicode might not be enough.
- allowed convert_unicode='force' to be significant for bind parameters
as well.
2010-01-17 21:12:47 +00:00
Mike Bayer 151fa4e75c statement_options -> execution_options 2010-01-17 20:43:35 +00:00
Mike Bayer 2b1937a31e - reorganized and re-documented Oracle schema tests to assume
test user has DBA privs, and all objects can be created /dropped.
- added ORDER BY to oracle column listing
- Oracle all_tables always limits to current user if schema not given.
- views reflect - added documentation + a unit test for this.
- Table(autoload) with no bind produces an error message specific to
the fact that autoload_with should be the first option to try.
2010-01-17 20:32:45 +00:00
Mike Bayer 15bc27bfb7 update test_schema/test_schema_2 docs, per [ticket:1644] 2010-01-17 18:10:37 +00:00
Mike Bayer 943259264f doc updates partially from [ticket:1651] 2010-01-17 18:00:01 +00:00
Mike Bayer abccc06242 - added "statement_options()" to Query, to so options can be
passed to the resulting statement. Currently only
Select-statements have these options, and the only option
used is "stream_results", and the only dialect which knows
"stream_results" is psycopg2.
- Query.yield_per() will set the "stream_results" statement
option automatically.
- Added "statement_options()" to Selects, which set statement
specific options. These enable e.g. dialect specific options
such as whether to enable using server side cursors, etc.
- The psycopg2 now respects the statement option
"stream_results". This option overrides the connection setting
"server_side_cursors". If true, server side cursors will be
used for the statement. If false, they will not be used, even
if "server_side_cursors" is true on the
connection. [ticket:1619]
- added a "frozendict" from http://code.activestate.com/recipes/414283/,
adding more default collections as immutable class vars on
Query, Insert, Select
2010-01-16 22:44:04 +00:00
Mike Bayer 00df05061e add a doc for query.delete() 2010-01-16 21:31:07 +00:00
Mike Bayer 16ca025657 - ConnectionProxy now has wrapper methods for all transaction
lifecycle events, including begin(), rollback(), commit()
begin_nested(), begin_prepared(), prepare(), release_savepoint(),
etc.
2010-01-16 19:04:39 +00:00
Mike Bayer 8c660d4611 - sqlsoup objects no longer have the 'query' attribute - it's
not needed for sqlsoup's usage paradigm and it gets in the
way of a column that is actually named 'query'.
2010-01-16 18:04:11 +00:00
Mike Bayer abdf3a22cc reflect MSSQL cols that have descending markers [ticket:1629] 2010-01-16 01:40:51 +00:00
Mike Bayer f72378137a restore common_parent logic in correspoinds_to, fixes [ticket:1657] 2010-01-15 19:04:50 +00:00
Mike Bayer 6e6b13b945 local session caching example 2010-01-13 20:42:09 +00:00
Mike Bayer 2d65e9772d add more examples, start basic 2010-01-13 20:00:25 +00:00
Mike Bayer db4af57e20 - replace the tip of the path info with the subclass mapper being used.
that way accurate "load_path" info is available for options
invoked during deferred loads.
we lose AliasedClass path elements this way, but currently,
those are not needed at this stage.
2010-01-13 18:31:19 +00:00
Mike Bayer 3c1a8adc78 NamedTuple is pickleable ! no really with all the protocols too ! 2010-01-13 17:11:27 +00:00
Mike Bayer fccc377417 OK, you can't merge NamedTuples and such. Fine. New query method. 2010-01-12 20:27:32 +00:00
Mike Bayer d309827c75 yes you can even set_value(). I'm using it to prepopulate individual "by_id" elements
from a multiple-row SELECT.
2010-01-12 17:51:28 +00:00
Mike Bayer 7fedc9298a have paths represented as their actual mapper, not the base mapper, allowing
more information for custom mapper opts to see what's going on.  add a new _reduce_path()
function to apply to the path as stored in dictionaries, adds a slight cost overhead.
2010-01-11 20:26:34 +00:00
Mike Bayer 513b350ccc add option to hardcode a cache key 2010-01-11 17:16:59 +00:00
Mike Bayer 7b1631fea1 compare class in the given path with our own class using issubclass, since paths
are always against the base class
2010-01-11 17:12:04 +00:00
Mike Bayer 79e5c5087a memcached wants this 2010-01-11 16:59:16 +00:00
Mike Bayer 01e9a49613 ensure criterion is not None 2010-01-11 16:10:11 +00:00
Mike Bayer f4b143685c - cut down on a few hundred method calls 2010-01-11 03:16:10 +00:00
Mike Bayer b729503a3e - merge() will not issue a needless merge of attributes if the
given instance is the same instance which is already present.

- merge() now also merges the "options" associated with a given
state, i.e. those passed through query.options() which follow
along with an instance, such as options to eagerly- or
lazyily- load various attributes.   This is essential for
the construction of highly integrated caching schemes.  This
is a subtle behavioral change vs. 0.5.

- A bug was fixed regarding the serialization of the "loader
path" present on an instance's state, which is also necessary
when combining the usage of merge() with serialized state
and associated options that should be preserved.

- The "query_cache" examples have been removed, and are replaced
with a fully comprehensive approach that combines the usage of
Beaker with SQLAlchemy.  New query options are used to indicate
the caching characteristics of a particular Query, which
can also be invoked deep within an object graph when lazily
loading related objects.  See /examples/beaker_caching/README.
2010-01-10 21:21:45 +00:00
Mike Bayer 517ed6b7ce happy new year 2010-01-07 23:56:00 +00:00
Mike Bayer ed6cbe607c - Session.merge() is performance optimized, using half the
call counts for "load=False" mode compared to 0.5 and
significantly fewer SQL queries in the case of collections
for "load=True" mode.
2010-01-07 22:09:17 +00:00
Mike Bayer 9ee458a619 - py3k binary type returned natively for sqlite3, pg8000, fixes [ticket:1639] and [ticket:1581] for now
- use type(None), py3k compat, [ticket:1584]
2010-01-07 18:47:39 +00:00
Mike Bayer 6ee80994a3 remove needless check_modified() 2010-01-07 17:51:45 +00:00
Mike Bayer 7ead46cbf2 merge r6616 of 0.5 branch, allow DefaultGenerators as "default" and "onupdate" 2010-01-07 00:24:30 +00:00
Mike Bayer f07297e82d - fix mysqlconnector import
- mysqlconnector returns get_server_version() as a tuple-ready structure
2010-01-04 18:41:16 +00:00
Mike Bayer e6ba4d9426 - accept 'expire' with a deprecation warning for query.update() [ticket:1648] 2010-01-04 16:36:11 +00:00
Mike Bayer 0156f1a9a7 - rename "myconnpy" to "mysqlconnector"
- remove all bug workarounds in mysqlconnector dialect
- add mysqlconnector as one of two "official" DBAPIs for MySQL
2010-01-04 16:26:01 +00:00
Mike Bayer d24133e5c5 - Fixed a column arithmetic bug that affected column
correspondence for cloned selectables which contain
free-standing column expressions.   This bug is
generally only noticeable when exercising newer
ORM behavior only availble in 0.6 via [ticket:1568],
but is more correct at the SQL expression level
as well. [ticket:1617]
2010-01-03 20:57:37 +00:00
Mike Bayer a2c727788e add documentation for Numeric/Float types, [ticket:1624] 2010-01-03 19:11:37 +00:00
Mike Bayer d66f470326 fixed DDL quoting with literal strings that have ' [ticket:1640] 2010-01-03 18:53:41 +00:00
Mike Bayer cae83dd7ea - added a refresh logger step to the nose plugin so that SQLA class loggers get correct state from nose cmdline
- fix mapper logging [ticket:1620]
2010-01-03 18:42:23 +00:00
Mike Bayer 5d711348da - have inspector properly return default_schema_name [ticket:1626] 2010-01-03 18:27:38 +00:00
Mike Bayer 87ff3c679c add a brief doc for custom DDL 2010-01-03 18:22:50 +00:00
Mike Bayer 52d99aaa97 - clarify ForeignKey docs, copy operation
- link all classes/functions in expressions
2010-01-02 18:20:08 +00:00
Mike Bayer d05a35daf8 - The copy() method on Column now supports uninitialized,
unnamed Column objects. This allows easy creation of
declarative helpers which place common columns on multiple
subclasses (merged from 0.5 with changes).
2010-01-02 03:50:50 +00:00
Mike Bayer 9cdbf8e8fe begin modernize of informix dialect for [ticket:1499] 2009-12-31 17:45:19 +00:00
Ants Aasma 9825126a61 Fix invalid behavior of Query.update and Query.delete with evaluate strategy and no criterion. 2009-12-31 12:21:30 +00:00
Ants Aasma 700b3a5295 fix error reporting in evaluator for unknown clauselist operators. 2009-12-30 15:51:03 +00:00
Mike Bayer d024426049 some compile docs 2009-12-30 04:49:03 +00:00
Mike Bayer 2698c8facb - postgresql dialect can properly detect pg "devel" version
strings, i.e. "8.5devel" [ticket:1636]
2009-12-29 23:41:04 +00:00
Mike Bayer d732e7bf26 - calling expr.in_([]), i.e. with an empty list, emits a warning
before issuing the usual "expr != expr" clause.  The
"expr != expr" can be very expensive, and it's preferred
that the user not issue in_() if the list is empty,
instead simply not querying, or modifying the criterion
as appropriate for more complex situations.
[ticket:1628]
2009-12-29 23:20:48 +00:00
Mike Bayer 8c3c2ea508 add note re #1646 2009-12-29 23:06:23 +00:00
Mike Bayer ebb54d1bc2 merge r6591, r6592 from 0.5 branch for PGInterval etc. /extract 2009-12-29 16:19:09 +00:00
Mike Bayer 67481f534f only on oracle 2009-12-29 02:45:32 +00:00
Mike Bayer cf7c80b3f4 - merge r6586 from 0.5 branch, for [ticket:1647] 2009-12-29 02:41:16 +00:00
Mike Bayer a572e39871 rest tweaks 2009-12-28 03:22:24 +00:00
Mike Bayer 25ea5bb32b clean up mapperextension docs 2009-12-28 03:18:13 +00:00
Lele Gaifax 535a4a77bf Recognize more Firebird disconnection cases, fixing #1646 on trunk 2009-12-27 18:56:27 +00:00
Mike Bayer a829db8dde include .jpg in recursive doc 2009-12-26 23:27:08 +00:00
Mike Bayer bc5ad4e8af merged r6570 from 0.5 branch, dont reflect IOT tables [ticket:1637] 2009-12-26 22:41:44 +00:00
Mike Bayer aa6f068a92 - merge sqlite 2.5 memory exception from r6567 of 0.5 branch 2009-12-25 16:50:49 +00:00
Mike Bayer 0446d17a3e add the uselist=False / single row assertion from [ticket:1643] for lazy loads too. 2009-12-19 05:41:37 +00:00
Mike Bayer 68da6c5e1d and the docs... 2009-12-18 21:12:18 +00:00
Mike Bayer 62b2fb8756 the arg is on Table, ahhh 2009-12-18 21:11:26 +00:00
Mike Bayer 33f2e2bfbb - Column() supports a keyword argument "sqlite_autoincrement", which
applies the SQLite keyword "AUTOINCREMENT" to columns within DDL -
will prevent generation of a separate PRIMARY KEY constraint.
[ticket:1016]
- added docs
- fixed underlines in mysql.rst
2009-12-18 21:08:35 +00:00
Mike Bayer 404be6e761 - added _with_options() to Connection. not publicizing this yet.
- updated oursql driver with latest fixes using options. [ticket:1613]
- all the MySQL drivers get a shoutout in the docs
- marked tests that OurSQL has problems with (only three), passes 100% now
2009-12-18 20:41:34 +00:00
Mike Bayer bc9e742b64 - mysql: a column of type TIMESTAMP now defaults to NULL if
"nullable=False" is not passed to Column(), and no default
is present. This is now consistent with all other types,
and in the case of TIMESTAMP explictly renders "NULL"
due to MySQL's "switching" of default nullability
for TIMESTAMP columns. [ticket:1539]
2009-12-18 20:09:14 +00:00
Mike Bayer fc175f478b - The version_id_col feature on mapper() will raise a warning when
used with dialects that don't support "rowcount" adequately.
[ticket:1569]
2009-12-18 19:11:19 +00:00
Mike Bayer 7f7a908d20 - All pyodbc-dialects now support extra pyodbc-specific
kw arguments 'ansi', 'unicode_results', 'autocommit'.
[ticket:1621]
2009-12-18 18:55:14 +00:00
Mike Bayer b9657c763a - Fixed bug preventing alias() of an alias() from being
cloned or adapted (occurs frequently in ORM operations).
[ticket:1641]
2009-12-18 18:46:40 +00:00
Mike Bayer 0683cc486d - relation() with uselist=False will emit a warning when
an eager load locates more than one valid value for the row,
typically due to primaryjoin/secondaryjoin conditions which
aren't appropriate for LEFT OUTER JOIN.  [ticket:1643]
2009-12-18 17:24:20 +00:00
Mike Bayer e8e446e92a - Session.merge() now properly overwrites a many-to-one or
uselist=False attribute to None if the attribute
is also None in the given object to be merged.
2009-12-14 01:29:51 +00:00
Mike Bayer 6bc016a762 documentation patch for [ticket:1354] 2009-12-09 00:00:48 +00:00
Mike Bayer 1805699923 - merge r6549 of 0.5 branch
- Fixed bug in query.update() when passing Cls.attribute
as keys in the value dict and using synchronize_session='expire'
('fetch' in 0.6). [ticket:1436]
2009-12-08 23:47:09 +00:00
Mike Bayer 54e6dedc5f don't advocate for text() inside of select(), plain strings are interpreted more intelligently, [ticket:1374] 2009-12-08 23:17:14 +00:00
Mike Bayer dc1fc3a897 - The signature of the proxy_factory callable passed to
association_proxy is now (lazy_collection, creator,
value_attr, association_proxy), adding a fourth argument
that is the parent AssociationProxy argument.  Allows
serializability and subclassing of the built in collections.
[ticket:1259]
2009-12-08 23:09:48 +00:00
Mike Bayer d39a157489 - Session.execute() now locates table- and
mapper-specific binds based on a passed
in expression which is an insert()/update()/delete()
construct. [ticket:1054]
2009-12-08 03:09:18 +00:00
Mike Bayer 358bc9db1c - removed needless "counter" behavior with select()
labelnames that match a column name in the table,
i.e. generates "tablename_id" for "id", instead of
"tablename_id_1" in an attempt to avoid naming
conflicts, when the table has a column actually
named "tablename_id" - this is because
the labeling logic is always applied to all columns
      so a naming conflict will never occur.
2009-12-08 02:31:59 +00:00
Mike Bayer 1b1acad676 - multi-part schema names, i.e. with dots such as
"dbo.master", are now rendered in select() labels
with underscores for dots, i.e. "dbo_master_table_column".
This is a "friendly" label that behaves better
in result sets. [ticket:1428]
2009-12-08 02:27:35 +00:00
Mike Bayer 71c0be0921 - The "use get" behavior of many-to-one relations, i.e. that a
lazy load will fallback to the possibly cached query.get()
value, now works across join conditions where the two compared
types are not exactly the same class, but share the same
"affinity" - i.e. Integer and SmallInteger.  Also allows
combinations of reflected and non-reflected types to work
with 0.5 style type reflection, such as PGText/Text (note 0.6
reflects types as their generic versions).   [ticket:1556]
- types now support an "affinity comparison" operation, i.e.
that an Integer/SmallInteger are "compatible", or
a Text/String, PickleType/Binary, etc.  Part of
[ticket:1556].
2009-12-08 01:53:21 +00:00
Mike Bayer 048f70ce85 - sqlite dialect properly generates CREATE INDEX for a table
that is in an alternate schema.  [ticket:1439]
2009-12-07 23:08:42 +00:00
Mike Bayer 90efddbb1d fixed CHANGES message location 2009-12-07 22:53:32 +00:00
Mike Bayer 2fbdb67cbe - Fixed a needless select which would occur when merging
transient objects that contained a null primary key
identifier.  [ticket:1618]
2009-12-07 22:39:33 +00:00
Mike Bayer 3b0f5d0cfd remove unfinished dialects 2009-12-07 19:29:36 +00:00
Mike Bayer f1464da1e5 seriously, this is not 0.6 code ! 2009-12-06 23:47:13 +00:00
Mike Bayer 089dd19ca8 add a warning for unported dialects. considered a full blown NotImplementedError but will see if this gets the message across 2009-12-06 23:45:19 +00:00
Mike Bayer 7dc4df8a68 - The Boolean type, when used on a backend that doesn't
have native boolean support, will generate a CHECK
constraint "col IN (0, 1)" along with the int/smallint-
based column type.  This can be switched off if
desired with create_constraint=False.
Note that MySQL has no native boolean *or* CHECK constraint
support so this feature isn't available on that platform.
[ticket:1589]
2009-12-06 22:58:05 +00:00
Mike Bayer f9cb6f5834 - reworked the DDL generation of ENUM and similar to be more platform agnostic.
Uses a straight CheckConstraint with a generic expression.  Preparing for boolean
constraint in [ticket:1589]
- CheckConstraint now accepts SQL expressions, though support for quoting of values
will be very limited.  we don't want to get into formatting dates and such.
2009-12-06 19:51:10 +00:00
Mike Bayer 4ca12d76bd remove unneeded _OracleDateTime/_OracleTimestamp cx_oracle types, streamline _OracleDate, [ticket:1600] 2009-12-06 01:59:14 +00:00
Mike Bayer 7fe0916aec - merged r6526 from 0.5 branch + some additional formatting fixes, [ticket:1597] 2009-12-06 01:41:13 +00:00
Mike Bayer ffdbcbc89a - fixed the import for entrypoint-driven dialects to
not rely upon silly tb_info trick to determine import
error status.  [ticket:1630]
2009-12-05 00:36:11 +00:00
Mike Bayer a52ffc3647 - an explicit check occurs when a synonym() is used with
map_column=True, when a ColumnProperty (deferred or otherwise)
    exists separately in the properties dictionary sent to mapper
    with the same keyname.   Instead of silently replacing
    the existing property (and possible options on that property),
    an error is raised.  [ticket:1633]
2009-12-04 01:51:23 +00:00
Mike Bayer 648e0eb70c - The cursor associated with connection pool connections
(i.e. _CursorFairy) now proxies `__iter__()` to the
underlying cursor correctly. [ticket:1632]
2009-12-03 02:34:47 +00:00
Mike Bayer 72859dd13b primaryjoin/secondaryjoin accept ColumnElement, docs, [ticket:1622] 2009-11-30 16:31:16 +00:00
Gaëtan de Menten 7ea4fca4ed - changed a few isinstance(value, Decimal) to "is not None", where appropriate
- fixed result processor for Numeric(asdecimal=False) on MSSQL.
2009-11-28 18:07:59 +00:00
Mike Bayer 7fedc5e070 - relation primaryjoin and secondaryjoin now check that they
are column-expressions, not just clause elements.  this prohibits
things like FROM expressions being placed there directly.
[ticket:1622]
2009-11-25 17:34:25 +00:00
Mike Bayer a358868d64 typo 2009-11-24 23:26:47 +00:00
Mike Bayer 976b9223ae test fails on zxjdbc 2009-11-24 22:56:59 +00:00
Gaëtan de Menten 95e349a5dc Prelookup codec in the String result processor for dialects which do not
return Unicode natively, as suggested in #1323. Provides a nice speed boost
(~21% total query time).
2009-11-23 08:35:34 +00:00
Mike Bayer 6249922a26 add NATIONAL CHAR test 2009-11-22 22:21:08 +00:00
Mike Bayer d9388a5194 - VARCHAR/NVARCHAR will not render without a length, raises
an error before passing to MySQL.   Doesn't impact
CAST since VARCHAR is not allowed in MySQL CAST anyway,
the dialect renders CHAR/NCHAR in those cases.
[ticket:1252]
2009-11-22 22:11:41 +00:00
Mike Bayer 66b7d008ee add an interesting from_statement() format 2009-11-21 20:51:16 +00:00
Gaëtan de Menten 26edef4f24 revert part of the change of r6510 because "select datetime('now')" in SQLite
does not contain microseconds
2009-11-17 19:38:09 +00:00
Gaëtan de Menten 1bca0c42a3 - sqlite
- DATE, TIME and DATETIME types can now take optional storage_format and
      regexp argument. storage_format can be used to store those types using
      a custom string format. regexp allows to use a custom regular expression
      to match string values from the database.
    - Time and DateTime types now use by a default a stricter regular
      expression to match strings from the database. Use the regexp argument
      if you are using data stored in a legacy format.
    - __legacy_microseconds__ on SQLite Time and DateTime types is not
      supported anymore. You should use the storage_format argument instead.
    - Date, Time and DateTime types are now stricter in what they accept as
      bind parameters: Date type only accepts date objects (and datetime ones,
      because they inherit from date), Time only accepts time objects, and
      DateTime only accepts date and datetime objects.
2009-11-17 18:35:06 +00:00
Gaëtan de Menten f96130acef minor speed optimization in String result_processor (if decoding is required) 2009-11-17 18:28:13 +00:00
Gaëtan de Menten 6f96478e76 minor speed optimization for PGArray bind & result processors 2009-11-16 15:50:25 +00:00
Mike Bayer 734dce8b60 - Fixed bug whereby composite columns wouldn't load properly
when configured on a joined-table subclass, introduced in
version 0.5.6 as a result of the fix for [ticket:1480].
[ticket:1616] thx to Scott Torborg.
2009-11-15 20:39:39 +00:00
Mike Bayer 943ce6bf16 merge r6504 from 0.5 plus an enhancement to the unit test, [ticket:1611] 2009-11-15 20:22:57 +00:00
Mike Bayer 92a00ee663 - Removed unused load() method from ShardedQuery.
[ticket:1606]
2009-11-15 19:50:55 +00:00
Mike Bayer 447dc44e1f start relying on new unicode detection fully - remove isinstance() from the unicode result processing. 2009-11-15 19:46:54 +00:00
Mike Bayer 5f6ed1a3f8 - pg8000 + postgresql dialects now check for float/numeric return
types to more intelligently determine float() vs. Decimal(),
[ticket:1567]
- since result processing is a hot issue of late, the DBAPI type
returned from cursor.description is certainly useful in cases like
these to determine an efficient result processor.   There's likely
other result processors that can make use of it.  But, backwards
incompat change to result_processor().  Happy major version number..
2009-11-15 19:20:22 +00:00
Mike Bayer b14d53aba1 fix StaticPool [ticket:1615] 2009-11-12 21:46:14 +00:00
Mike Bayer 4f74e231ba merge r6497 of 0.5 branch 2009-11-11 03:46:45 +00:00
Mike Bayer b0833b66a0 reduce some call overhead 2009-11-10 23:15:39 +00:00
Mike Bayer 169c4fb3d3 scan for autocommit based on text() specific flag, saves isinstance() call on each execution. 2009-11-10 22:59:59 +00:00
Mike Bayer 9911443b9d - new oursql dialect added. [ticket:1613] 2009-11-10 22:39:42 +00:00
Mike Bayer e972bb569a dont run sqlsoup test on jython 2009-11-10 00:48:06 +00:00
Mike Bayer 55a3e5e30d - subclassed Function off of new FunctionElement generic base
- removed "key" accessor of Function, Grouping - this doesn't seem to be used for anything
- various formatting
- documented the four "Element" classes in the compiler extension as per [ticket:1590]
2009-11-10 00:43:53 +00:00
Mike Bayer 12a323eb0c test fix, [ticket:1595] 2009-11-10 00:10:11 +00:00
Mike Bayer f8e098544b - ForeignKey(constraint=some_parent) is now private _constraint
- ForeignKey and ForeignKeyConstraint objects now correctly
copy() all their public keyword arguments.  [ticket:1605]
2009-11-09 23:40:57 +00:00
Mike Bayer c6724a3ff0 - query.get() can be used with a mapping to an outer join
where one or more of the primary key values are None.
[ticket:1135]
2009-11-09 23:20:31 +00:00
Mike Bayer 89fcf7c3c9 add test for map explicit Table 2009-11-09 22:32:33 +00:00
Mike Bayer ba00071e74 - added a real unit test for sqlsoup
- removed doctest stuff
- redid session docs for sqlsoup
- sqlsoup stays within the transaction of a Session now, is explcitly autocommit=False by default and includes commit()/rollback() methods
- sqlsoup db.<sometable>.update() and delete() now call
query(cls).update() and delete(), respectively.
- sqlsoup now has execute() and connection(), which call upon
the Session methods of those names, ensuring that the bind is
in terms of the SqlSoup object's bind.
2009-11-09 19:41:45 +00:00
Mike Bayer 8a282a9b60 moved modified_event() calls below the attribute extension fires. this basically has no difference in any case except that where an extension is calling commit() on the attribute - in that case it usually, but not always, maintains the same history. [ticket:1601] 2009-11-08 21:54:56 +00:00
Mike Bayer 516dd178bf allow setattr() access to _CursorFairy directly, thereby removing the need for dialects to guess whether they have a wrapped cursor or not, fixes #1609, regression from r6471 2009-11-06 15:51:06 +00:00
Mike Bayer 8f4373104f supports unicode binds in PG too. even without the UNICODE extension it seems to work now... 2009-11-06 02:39:16 +00:00
Gaëtan de Menten e4571c59a6 Within NamedTuple, izip is faster on most cases, and equally fast on others 2009-11-05 14:30:33 +00:00
Gaëtan de Menten d00ee6bf56 use list comprehension instead of generator as it is much faster for small
lists, as will usually be the case here. provides a ~11% speedup for large
column-only queries.
2009-11-04 20:22:00 +00:00
Mike Bayer a69a094db5 - Fixed bug in two-phase transaction whereby commit() method
didn't set the full state which allows subsequent close()
call to succeed. [ticket:1603]
2009-11-04 17:15:36 +00:00
Mike Bayer 43348d6163 - Fixed bug where Query would crash if a join() with no clear
"left" side were called when a non-mapped column entity
appeared in the columns list. [ticket:1602]
2009-11-04 13:27:59 +00:00
Gaëtan de Menten d79d48ca55 Using generators for small lists is highly inefficient. This change shoves
6% of total time for large ORM queries. Not bad for a 2 characters change :)
2009-11-04 13:19:47 +00:00
Mike Bayer 6acbb4fb93 - simplify default schema name test
- MySQL + zxjdbc *is* unicode by default.  it was the broken initialize()
2009-11-03 19:48:07 +00:00
Mike Bayer cac9f6b3bb fix MySQL initialize to use defaultdialect first 2009-11-03 19:05:58 +00:00
Mike Bayer e35dcee6ca - The "start" and "increment" attributes on Sequence now
generate "START WITH" and "INCREMENT BY" by default,
on Oracle and Postgresql.  Firebird doesn't support
these keywords right now.  [ticket:1545]
2009-11-03 18:33:57 +00:00
Mike Bayer 56f64add81 - Connection pool logging now uses both INFO and DEBUG
log levels for logging.  INFO is for major events such
as invalidated connections, DEBUG for all the acquire/return
logging.  `echo_pool` can be False, None, True or "debug"
the same way as `echo` works.
2009-11-03 17:52:02 +00:00
Mike Bayer 4b532e2084 - dialect.get_default_schema_name(connection) is now
public via dialect.default_schema_name.
[ticket:1571]
2009-11-03 17:35:13 +00:00
Gaëtan de Menten b5af1759df * tweaked PickleType result_processor and bind_processor so that they are more
correct and more easily maintainable.
 * implemented specific result_processor and bind_processor for Interval type
   to avoid TypeDecorator call overhead (closes #1598)
2009-11-03 16:30:07 +00:00
Gaëtan de Menten adaecccda1 rewrote PickleType bind_processor and result_processors to bypass TypeDecorator
call overhead and avoid pickler function lookup for each row (see #1598).
Provides a speedup of ~7 % on total query time for a 1000 record query on a
table with 1 PickeType field and 25% None values.
2009-11-03 15:52:57 +00:00
Mike Bayer db3521823d - Fixed the call to get_committed_value() on CompositeProperty.
[ticket:1504]
2009-11-03 04:58:18 +00:00
Mike Bayer db2ff89644 revert r6466 2009-11-03 04:57:09 +00:00
Mike Bayer cc9c615c5a - Fixed the call to get_committed_value() on CompositeProperty.
[ticket:1504]
2009-11-03 04:54:56 +00:00
Mike Bayer 659ca0c508 added a test for #1349 2009-11-03 04:30:18 +00:00
Mike Bayer e2ca4ebe58 omit this test for non-oracle 2009-11-02 03:07:13 +00:00
Mike Bayer 9b8292ec6a fix adapt() so that DB-specified typedecorator replacements work 2009-11-01 23:28:44 +00:00
Mike Bayer e8854fe945 - INTERVAL supports an optional "precision" argument
corresponding to the argument that PG accepts.
- Added support for reflecting the INTERVAL YEAR TO MONTH
and INTERVAL DAY TO SECOND syntaxes of the INTERVAL
type.  [ticket:460]
2009-11-01 22:47:14 +00:00
Mike Bayer fb6be4d359 - filter out SYS_NC\d+$ columns [ticket:1513]
- remove explicit INNER JOIN from index query to support oracle 8
2009-11-01 21:53:00 +00:00
Mike Bayer 7115454c70 added test for [ticket:1450] 2009-11-01 21:08:12 +00:00
Mike Bayer 2cefbf11c3 add "dialect" to the __all__ of each root dialect package 2009-11-01 20:59:40 +00:00
Mike Bayer 98f80d6ce9 - added py3k and "OS Independent" classifiers 2009-11-01 20:48:03 +00:00
Mike Bayer 0d2ae16aee - the __contains__() method of MetaData now accepts
strings or `Table` objects as arguments.  If given
a `Table`, the argument is converted to `table.key` first,
i.e. "[schemaname.]<tablename>" [ticket:1541]
2009-11-01 20:39:43 +00:00
Gaëtan de Menten 68451b990a get more speed out of the Time type on Mysql 2009-10-30 11:37:26 +00:00
Gaëtan de Menten 1adf8e7fcd get a bit more speed out of datetime and LOB-based types on cx_oracle 2009-10-30 11:09:45 +00:00
Gaëtan de Menten 876b3fdd3f added comment so that other people don't spend their time trying to optimize
optimal code
2009-10-30 10:28:43 +00:00
Gaëtan de Menten b1a7258750 minor speed improvement on date, datetime and time types on SQLite 2009-10-30 10:20:19 +00:00
Gaëtan de Menten 8195ec35ad large speed improvement of the Interval type on non-native dialects 2009-10-29 21:24:33 +00:00
Gaëtan de Menten 7c6e5dfffd slightly sped-up Binary type, PickleType and all TypeDecorators 2009-10-29 20:41:16 +00:00
Mike Bayer b01d634161 added docs to case() illusrtating usage of literal_column(), can't implement #809 directly 2009-10-28 15:46:51 +00:00
Gaëtan de Menten 953a653179 partially PEP8-ified informix dialect 2009-10-27 09:25:13 +00:00
Mike Bayer 5e0a1d1ea1 no native unicode for mysql + zxjdbc 2009-10-26 18:52:12 +00:00
Mike Bayer 62d2069210 whats up with the native_unicode test on jython 2009-10-26 18:23:20 +00:00
Mike Bayer bc714d614d test fixes 2009-10-26 01:29:56 +00:00
Mike Bayer 4a7f889d8a oracle test fixes 2009-10-26 01:20:38 +00:00
Mike Bayer 5119ce78b5 - The psycopg2 dialect now uses psycopg2's "unicode extension"
on all new connections, which allows all String/Text/etc.
types to skip the need to post-process bytestrings into
unicode (an expensive step due to its volume).  Other
dialects which return unicode natively (pg8000, zxjdbc)
also skip unicode post-processing.

- String/Text/Unicode types now skip the unicode() check
on each result column value if the dialect has
detected the DBAPI as returning Python unicode objects
natively.  This check is issued on first connect
using "SELECT CAST 'some text' AS VARCHAR(10)" or
equivalent, then checking if the returned object
is a Python unicode.   This allows vast performance
increases for native-unicode DBAPIs, including
pysqlite/sqlite3, psycopg2, and pg8000.
2009-10-26 00:32:39 +00:00
Mike Bayer eb9763febe - generalized Enum to issue a CHECK constraint + VARCHAR on default platform
- added native_enum=False flag to do the same on MySQL, PG, if desired
2009-10-25 21:27:08 +00:00
Mike Bayer a5f827b12d well great nobody even supports PG enum. 2009-10-25 16:50:09 +00:00
Mike Bayer 85d49bde73 - Using a "dynamic" loader with a "secondary" table now produces
a query where the "secondary" table is *not* aliased.  This
allows the secondary Table object to be used in the "order_by"
attribute of the relation(), and also allows it to be used
in filter criterion against the dynamic relation.
[ticket:1531]

- a "dynamic" loader sets up its query criterion at construction
time so that the actual query is returned from non-cloning
accessors like "statement".
2009-10-25 16:31:54 +00:00
Mike Bayer a77a0fd3eb fix errant 2.6ism 2009-10-25 14:58:21 +00:00
Mike Bayer ed83a844bb - Fixed a slight inaccuracy in the sharding example.
Comparing equivalence of columns in the ORM is best
accomplished using col1.shares_lineage(col2).
[ticket:1491]
2009-10-25 01:40:23 +00:00
Mike Bayer aa557982fa - Added new ENUM type to the Postgresql dialect, which exists as a schema-level
construct and extends the generic Enum type.  Automatically
associates itself with tables and their parent metadata
to issue the appropriate CREATE TYPE/DROP TYPE
commands as needed, supports unicode labels, supports
reflection.  [ticket:1511]

- MySQL ENUM now subclasses the new generic Enum type, and also handles
unicode values implicitly, if the given labelnames are unicode
objects.

- Added a new Enum generic type, currently supported on
Postgresql and MySQL.  Enum is a schema-aware object
to support databases which require specific DDL in
order to use enum or equivalent; in the case of PG
it handles the details of `CREATE TYPE`, and on
other databases without native enum support can
support generation of CHECK constraints.
[ticket:1109] [ticket:1511]

- types documentation updates

- some cleanup on schema/expression docs
2009-10-25 00:40:34 +00:00
Mike Bayer 82ea898ab0 - apply ged's suggested optimization of not needlessly wrapping mapper._instance_processor
- start playing with semi-automated 78-col wrapping
2009-10-24 20:58:21 +00:00
Mike Bayer fafbe57b30 fix some 2.4 callcounts 2009-10-24 20:36:44 +00:00
Mike Bayer 52b1ace676 - ResultProxy internals have been overhauled to greatly reduce
method call counts when fetching columns that have no
type-level processing applied.   Provides a 100% speed
improvement when fetching large result sets with no unicode
conversion.  Many thanks to Elixir's Gaëtan de Menten
for this dramatic improvement !  [ticket:1586]
2009-10-24 16:38:07 +00:00
Mike Bayer 2e8b1639f8 update counts for 2.4 2009-10-23 21:30:02 +00:00
Mike Bayer 52fab3ed34 - Mutable collection passed to the "extension" attribute
of relation(), column_property() etc. will not be mutated
or shared among multiple instrumentation calls, preventing
duplicate extensions, such as backref populators,
from being inserted into the list.
[ticket:1585]
2009-10-23 19:46:58 +00:00
Mike Bayer a43a0e8b68 - insert() and update() constructs can now embed bindparam()
objects using names that match the keys of columns.  These
      bind parameters will circumvent the usual route to those
      keys showing up in the VALUES or SET clause of the generated
      SQL. [ticket:1579]
2009-10-23 01:08:02 +00:00
Michael Trier 9ae821ee66 Removed references to sequence in MSSQL
Implicit identities in mssql work the same as implicit sequences on any
other dialects. Explicit sequences are enabled through the use of
"default=Sequence()". See the MSSQL dialect documentation for more
information.
2009-10-22 03:29:52 +00:00
Mike Bayer e552ce339e - RETURNING is supported by 8.2+
- add docs for PG delete..returning
2009-10-21 16:33:04 +00:00
Mike Bayer 5a140299b3 - Corrected the "has_sequence" query to take current schema,
or explicit sequence-stated schema, into account.
[ticket:1576]
2009-10-21 04:47:02 +00:00
Mike Bayer 7dddcd1403 some cleanup 2009-10-21 04:45:29 +00:00
Mike Bayer aceb90525a merged scopefunc patch from r6420 of 0.5 branch 2009-10-20 17:33:33 +00:00
Mike Bayer 404f43894a merge r6418 from 0.5, dedupe expressions on clause ident, not string value
[ticket:1574]
2009-10-20 17:12:58 +00:00
Mike Bayer 7b457b9731 merged r6416 of 0.5 branch, fix the "numeric" paramstyle and add tests 2009-10-20 16:19:54 +00:00
Lele Gaifax f44f59e05e Fix reST markup 2009-10-19 08:39:36 +00:00
Lele Gaifax 75a3baf94d Modernise doc about returning() support 2009-10-19 08:00:55 +00:00
Mike Bayer b252fc249d attempt to fix some jython ordering annoyingness 2009-10-18 22:50:14 +00:00
Mike Bayer 4bd8451046 - the behavior of eagerloading such that the main query is
wrapped in a subquery when LIMIT/OFFSET are present now
makes an exception for the case when all eager loads are
many-to-one joins. In those cases, the eager joins are
against the parent table directly along with the
limit/offset without the extra overhead of a subquery,
since a many-to-one join does not add rows to the result.
2009-10-18 21:59:54 +00:00
Mike Bayer cad7e3ceca - added a flag to relation(), eagerload(), and eagerload_all()
called 'innerjoin'.  Specify `True` or `False` to control
whether an eager join is constructed as an INNER or OUTER
join.   Default is `False` as always.   The mapper options
will override whichever setting is specified on relation().
Should generally be set for many-to-one, not nullable
foreign key relations to allow improved join performance.
[ticket:1544]
2009-10-18 20:28:19 +00:00
Mike Bayer facb6516e9 - initial MySQL Connector/Python driver
- support exceptions raised in dialect initialize phase
- provide default dialect create_connect_args() method
2009-10-18 16:48:46 +00:00
Mike Bayer 1f9ee311cd gratuitous try/except/else usage 2009-10-18 04:05:47 +00:00
Mike Bayer e1d304ce6f fix MySQL tests 2009-10-18 02:52:56 +00:00
Mike Bayer eb6f1f87f6 deprecations per [ticket:1498]:
- deprecated PassiveDefault - use DefaultClause.
- the BINARY and MSBinary types now generate "BINARY" in all
cases.  Omitting the "length" parameter will generate
"BINARY" with no length.  Use BLOB to generate an unlengthed
binary column.
- the "quoting='quoted'" argument to MSEnum/ENUM is deprecated.
It's best to rely upon the automatic quoting.
- "shortname" attribute on bindparam() is removed.
- fold_equivalents flag on join is deprecated (will remain
until [ticket:1131] is implemented)
- "scalar" flag on select() is removed, use
select.as_scalar().
- 'transactional' flag on sessionmaker() and others is
removed. Use 'autocommit=True' to indicate 'transactional=False'.
- 'polymorphic_fetch' argument on mapper() is removed.
Loading can be controlled using the 'with_polymorphic'
option.
- 'select_table' argument on mapper() is removed.  Use
'with_polymorphic=("*", <some selectable>)' for this
functionality.
- 'proxy' argument on synonym() is removed.  This flag
did nothing throughout 0.5, as the "proxy generation"
behavior is now automatic.
- Passing a single list of elements to eagerload(),
eagerload_all(), contains_eager(), lazyload(),
defer(), and undefer() instead of multiple positional
-args is deprecated.
- Passing a single list of elements to query.order_by(),
query.group_by(), query.join(), or query.outerjoin()
instead of multiple positional *args is deprecated.
- query.iterate_instances() is removed.  Use query.instances().
- Query.query_from_parent() is removed.  Use the
sqlalchemy.orm.with_parent() function to produce a
"parent" clause, or alternatively query.with_parent().
- query._from_self() is removed, use query.from_self()
instead.
- the "comparator" argument to composite() is removed.
Use "comparator_factory".
- RelationProperty._get_join() is removed.
- the 'echo_uow' flag on Session is removed.  Use
logging on the "sqlalchemy.orm.unitofwork" name.
- session.clear() is removed.  use session.expunge_all().
- session.save(), session.update(), session.save_or_update()
are removed.  Use session.add() and session.add_all().
- the "objects" flag on session.flush() remains deprecated.
- the "dont_load=True" flag on session.merge() is deprecated
in favor of "load=False".
- passing an InstanceState (internal SQLAlchemy state object) to
attributes.init_collection() or attributes.get_history() is
deprecated.  These functions are public API and normally
expect a regular mapped object instance.
- the 'engine' parameter to declarative_base() is removed.
Use the 'bind' keyword argument.
2009-10-15 23:00:06 +00:00
Mike Bayer 53f1c775db - Added BigInteger to global imports
- Oracle compiles BigInteger into NUMBER(19), finishes [ticket:1125]
2009-10-15 21:23:45 +00:00
Mike Bayer 066cdf13c2 - setting echo=False on create_engine() now sets the loglevel
to WARN instead of NOTSET.  This so that logging can be
disabled for a particular engine even if logging
for "sqlalchemy.engine" is enabled overall.  Note that the
default setting of "echo" is `None`. [ticket:1554]
2009-10-15 21:06:35 +00:00
Mike Bayer 6535456ea6 - A column can be added to a joined-table declarative
superclass after the class has been constructed
(i.e. via class-level attribute assignment), and
the column will be propagated down to
subclasses. [ticket:1570]  This is the reverse
situation as that of [ticket:1523], fixed in 0.5.6.
2009-10-15 20:52:06 +00:00
Mike Bayer 3984cad722 - query.from_self(), query.union(), others which do a
"SELECT * from (SELECT...)" type of nesting will do
a better job translating column expressions within the subquery
to the columns clause of the outer query.  This is
potentially backwards incompatible with 0.5, in that this
may break queries with literal expressions that do not have labels
applied (i.e. literal('foo'), etc.)
[ticket:1568]
2009-10-15 20:15:19 +00:00
Mike Bayer cf6c66e70e - mapping to a select() construct now requires that you
make an alias() out of it distinctly.   This to eliminate
confusion over such issues as [ticket:1542]
2009-10-15 19:08:35 +00:00
Mike Bayer c5571ab19a - an executemany() now requires that all bound parameter
sets require that all keys are present which are
present in the first bound parameter set.  The structure
and behavior of an insert/update statement is very much
determined by the first parameter set, including which
defaults are going to fire off, and a minimum of
guesswork is performed with all the rest so that performance
is not impacted.  For this reason defaults would otherwise
silently "fail" for missing parameters, so this is now guarded
against. [ticket:1566]
2009-10-15 18:41:02 +00:00
Mike Bayer bc351a2dc4 - DefaultRunner and subclasses have been removed. The job
of this object has been simplified and moved into
ExecutionContext.  Dialects which support sequences should
add a `fire_sequence()` method to their execution context
implementation.  [ticket:1566]
2009-10-15 16:09:59 +00:00
Mike Bayer ad89932715 remove instanceof() in favor of memoized flags, part of [ticket:1566] 2009-10-14 02:19:37 +00:00
Mike Bayer 87824331c6 - expression.null() is fully understood the same way
None is when comparing an object/collection-referencing
attribute within query.filter(), filter_by(), etc.
[ticket:1415]
2009-10-12 22:29:08 +00:00
Mike Bayer b16f1ca427 fix SQL output 2009-10-12 16:32:29 +00:00
Mike Bayer d6239f2262 - added "ddl" argument to the "on" callable of DDLElement [ticket:1538]
- fixed the imports in the "postgres" cleanup dialect
- renamed "schema_item" attribute/argument of DDLElement
  to "target".
2009-10-12 00:11:00 +00:00
Mike Bayer 114ad36894 - RowProxy objects are now pickleable, i.e. the object returned
by result.fetchone(), result.fetchall() etc.
- the "named tuple" objects returned when iterating a
Query() are now pickleable.
2009-10-11 17:16:53 +00:00
Mike Bayer b47f8237d8 export UPPERCASE types as "from sqlalchemy.dialects.<dbname> import VARCHAR, TEXT, INET, ..." 2009-10-10 19:28:18 +00:00
Mike Bayer 8495152b30 - the setuptools entrypoint for external dialects is now
called "sqlalchemy.dialects".  external dialects need to be changed to work with 0.6 in any case.
2009-10-10 16:51:05 +00:00
Mike Bayer 9bab004a9b - unit test fixes
- py3k readme
- removed column.sequence accessor
2009-10-10 16:14:13 +00:00
Mike Bayer bf207ae1b6 - the mechanics of "backref" have been fully merged into the
finer grained "back_populates" system, and take place entirely
within the _generate_backref() method of RelationProperty.  This
makes the initialization procedure of RelationProperty
simpler and allows easier propagation of settings (such as from
subclasses of RelationProperty) into the reverse reference.
The internal BackRef() is gone and backref() returns a plain
tuple that is understood by RelationProperty.
2009-10-05 22:11:06 +00:00
Mike Bayer 9f2e94fc2c changelog re [ticket:1561] 2009-10-05 20:17:43 +00:00
Michael Trier 14390939bd Corrected problem with a Trusted Connection under MSSQL 2008 native driver. 2009-10-05 02:38:39 +00:00
Mike Bayer 680f87c9ce - usage of the CHAR type results in cx_oracle's
FIXED_CHAR dbapi type being bound to statements.
2009-10-03 19:58:19 +00:00
Lele Gaifax 3c1e054251 Fix #1560 revisiting Firebird dialect docs 2009-10-03 13:44:14 +00:00
Mike Bayer 6c12838f9a - query.options() now only propagate to loaded objects
for potential further sub-loads only for options where
such behavior is relevant, keeping
various unserializable options like those generated
by contains_eager() out of individual instance states.
[ticket:1553]
2009-10-02 22:23:30 +00:00
Mike Bayer 8e8da289d5 - boolean, int, and float arguments count as "cache key" values for inspector info_cache()
- added awareness of sqlite implicit auto indexes [ticket:1551]
2009-10-01 23:00:02 +00:00
Philip Jenvey ddbcf97f33 avoid __nonzero__ on ClauseElements 2009-10-01 21:27:12 +00:00
Mike Bayer 0d8ba83046 added a test for #1085 2009-09-30 20:55:00 +00:00
Gaëtan de Menten e9acc2418f removed obsolete code (closes #1559) 2009-09-30 12:11:14 +00:00
Philip Jenvey 5a9c1b8824 merge from branches/clauseelement-nonzero
adds a __nonzero__ to _BinaryExpression to avoid faulty comparisons during hash
collisions (which only occur on Jython)
fixes #1547
2009-09-24 02:11:56 +00:00
Philip Jenvey 79ce8e89bd small change 2009-09-21 19:43:51 +00:00
Mike Bayer 7760e70d8d place the constructor level configuration within the COMPILE_MUTEX,
to prevent half-constructed mappers from getting sucked into the compile()
phase
2009-09-21 00:47:35 +00:00
Mike Bayer 2acd40f0dc tweaks 2009-09-20 16:48:15 +00:00
Mike Bayer 6d4186999f a picture 2009-09-20 16:40:02 +00:00
Mike Bayer 7aa0b15353 ensure default dialect for these 2009-09-20 15:45:24 +00:00
Mike Bayer 2dfc500ac3 - query.join() has been reworked to provide more consistent
behavior and more flexibility (includes [ticket:1537])

- query.select_from() accepts multiple clauses to produce
multiple comma separated entries within the FROM clause.
Useful when selecting from multiple-homed join() clauses.
2009-09-18 20:04:45 +00:00
Mike Bayer 8b328f6942 merged r6357 of rel_0_5 branch 2009-09-16 20:38:29 +00:00
Mike Bayer 998183be6b - contains_eager() now works with the automatically
generated subquery that results when you say
"query(Parent).join(Parent.somejoinedsubclass)", i.e.
when Parent joins to a joined-table-inheritance subclass.
Previously contains_eager() would erroneously add the
subclass table to the query separately producing a
cartesian product.  An example is in the ticket
description.  [ticket:1543]
2009-09-16 19:48:22 +00:00
Lele Gaifax 89a8e000ef Fix #1185: better way of checking already initialized kinterbasdb backend 2009-09-15 09:33:05 +00:00
Lele Gaifax 0515dc2480 Revisited Firebird's keywords set
Several keywords were missing, and various words were wrongly included
in the set. I took the current list of keywords out of "keywords.cpp", filtering
out effective reserverd words with a simple script that created a dummy table
with a field named after each word.
While this fixes a few tests (for example, those creating a table with a "start"
field, a Firebird reserverd words not previously registered as such), it may
introduce a backward incompatibility with previous SA releases: should this be
not wanted, I will add previous non-reserved-words to the set.
2009-09-15 08:52:25 +00:00
Mike Bayer 75848ce2c8 - Table objects declared in the MetaData can now be used
in string expressions sent to primaryjoin/secondaryjoin/
secondary - the name is pulled from the MetaData of the
declarative base.  [ticket:1527]
2009-09-12 20:28:10 +00:00
Mike Bayer f83c9a3959 - Added an assertion that prevents a @validates function
or other AttributeExtension from loading an unloaded
collection such that internal state may be corrupted.
[ticket:1526]
2009-09-12 19:59:39 +00:00
Philip Jenvey 6a8b52c406 o don't need to str() Jython arrays twice
o map the Binary types w/ new str handling in mssql's BinaryTest.test_binary
2009-09-12 00:18:06 +00:00
Philip Jenvey d82894bc40 don't assume dict ordering 2009-09-11 23:48:56 +00:00
Philip Jenvey a5217abf5f typo 2009-09-11 23:18:27 +00:00
Philip Jenvey fd22efe297 oracle/mssql+zxjdbc blurb 2009-09-11 23:16:20 +00:00
Philip Jenvey ed486e9523 close cursors: mostly fetchone -> first 2009-09-11 22:53:50 +00:00
Philip Jenvey 4f87ca83ec update per new Binary str handling 2009-09-11 22:51:25 +00:00
Mike Bayer 5ca7fa3abd all about DDL events 2009-09-11 22:37:13 +00:00
Philip Jenvey f385260987 mssql+zxjdbc support
original patch from Victor Ng
fixes #1505
2009-09-11 08:10:32 +00:00
Mike Bayer 0c26713326 docs 2009-09-10 21:23:04 +00:00
Mike Bayer d987afc8d9 move foreign keys stuff into the constraints section 2009-09-09 21:00:14 +00:00
Mike Bayer a3499358f9 edits plus redid "sequences". 2009-09-09 20:18:48 +00:00
Mike Bayer 590d5d99f0 fix update examples for [ticket:1533] 2009-09-09 16:26:26 +00:00
Philip Jenvey 2c4311c2a9 fix the new Binary str handling under Jython 2009-09-09 06:48:06 +00:00
Mike Bayer 0b292e8842 - the Binary type now returns data as a Python string
(or a "bytes" type in Python 3), instead of the built-
in "buffer" type.  This allows symmetric round trips
of binary data. [ticket:1524]
2009-09-05 03:29:20 +00:00
Mike Bayer ebcdb289d9 - distill contextual parameters for the purpose of default functions into context.current_parameters
- metadata docs continued
2009-09-03 22:26:16 +00:00
Mike Bayer ef8c8d21cd mysql doesn't like the 0's. not sure why but its not the point of the test in any case 2009-09-02 16:22:47 +00:00
Philip Jenvey 601e60e250 add a length for mysql 2009-09-02 00:30:44 +00:00
Mike Bayer 0eab503a87 - Fixed bug which prevented two entities from mutually
replacing each other's primary key values within a single
flush() for some orderings of operations.  [ticket:1519]
2009-09-01 22:55:59 +00:00
Mike Bayer 31f26e561c - Fixed bug which disallowed one side of a many-to-many
bidirectional reference to declare itself as "viewonly"
[ticket:1507]
2009-09-01 22:42:35 +00:00
Mike Bayer 4888c89ce5 - A column can be added to a joined-table subclass after
the class has been constructed (i.e. via class-level
attribute assignment).  The column is added to the underlying
Table as always, but now the mapper will rebuild its
"join" to include the new column, instead of raising
an error about "no such column, use column_property()
instead".  [ticket:1523]
- added an additional test in test_mappers for "added nonexistent column",
even though this test is already in test_query its more appropriate within
"mapper configuration" tests.
2009-09-01 22:26:23 +00:00
Mike Bayer aa73243fde - Fixed the error message for "could not find a FROM clause"
in query.join() which would fail to issue correctly
if the query was against a pure SQL construct.
[ticket:1522]
2009-09-01 22:14:22 +00:00
Mike Bayer fed57bc9dd doh 2009-08-31 21:59:45 +00:00
Mike Bayer 2cb92cbbd0 - Fixed incorrect exception raise in
Weak/StrongIdentityMap.add()
[ticket:1506]
2009-08-31 21:53:49 +00:00
Mike Bayer 358ab55c31 - Fixed recursion issue which occured if a mapped object's
`__len__()` or `__nonzero__()` method resulted in state
changes.  [ticket:1501]
2009-08-31 21:24:52 +00:00
Mike Bayer c3b0df488f - the allow_null_pks flag on mapper() is deprecated, and
the feature is turned "on" by default.  This means that
a row which has a non-null value for any of its primary key
columns will be considered an identity.  The need for this
scenario typically only occurs when mapping to an outer join.
[ticket:1339]
- streamlined the NULL check to use set operations
2009-08-31 20:54:19 +00:00
Mike Bayer 3d38969fd4 - Inserting NULL into a primary key + foreign key column
will allow the "not null constraint" error to raise,
not an attempt to execute a nonexistent "col_id_seq"
sequence.  [ticket:1516]

- autoincrement SELECT statements, i.e. those which
select from a procedure that modifies rows, now work
with server-side cursor mode (the named cursor isn't
used for such statements.)
2009-08-31 20:38:14 +00:00
Gaëtan de Menten f3480a7ff4 Remove NCLOB from types.__all__, since it's not defined there. Not sure if this
is the proper fix but the former situation made it impossible to do "from
sqlalchemy.types import *", which Elixir does.
2009-08-31 15:14:28 +00:00
Philip Jenvey db9921f14e rename jdbc suffix to zxjdbc, add ReturningParam.__ne__ 2009-08-30 20:18:34 +00:00
Philip Jenvey 799f6c37d3 assert -> eq_ 2009-08-29 19:12:14 +00:00
Philip Jenvey ff911f6211 assert -> eq_ 2009-08-29 07:12:15 +00:00
Mike Bayer a6b56237fe fix, comes back funky on PG reflection 2009-08-28 20:36:28 +00:00
Mike Bayer 72b9be719f - Fixed an obscure issue whereby a joined-table subclass
with a self-referential eager load on the base class
would populate the related object's "subclass" table with
data from the "subclass" table of the parent.
[ticket:1485]
2009-08-28 20:29:08 +00:00
Mike Bayer f892d1a3fe - Fixed column.copy() to copy defaults and onupdates.
[ticket:1373]
2009-08-28 19:00:44 +00:00
Philip Jenvey 7e0614fc7a fix port specification 2009-08-28 03:27:29 +00:00
Mike Bayer 8b74ddd791 use *args with log.debug()/log.info(), [ticket:1520] 2009-08-26 19:46:04 +00:00
Philip Jenvey c90d7ef74d correct the create_engine url
fixes #1515
thanks Randall
2009-08-26 01:27:04 +00:00
Mike Bayer 030ca77f68 doc fixes 2009-08-25 01:31:27 +00:00
Philip Jenvey 7d07ee0b39 instance_dict may be modified before the GC triggers _cleanup on Jython, so eat
state mismatch AssertionErrors
2009-08-21 02:19:07 +00:00
Mike Bayer c43073b659 get mysql+pyodbc connections to work again 2009-08-20 22:55:59 +00:00
Philip Jenvey 8e551942c0 this workaround isn't necessary 2009-08-18 05:51:32 +00:00
Philip Jenvey 19b8802892 fix oracle+zxjdbc asdecimal conversions 2009-08-18 05:30:50 +00:00
Philip Jenvey fc59a5e0c4 oracle+zxjdbc returning support 2009-08-18 05:28:05 +00:00
Philip Jenvey f465044d26 always visit returning clauses in the right order for positional paramstyle
sanity
2009-08-18 02:43:37 +00:00
Mike Bayer da9c1405d6 dont see the need to re-call post_configure inside a reentrant loop. since we're
in 0.6 let's just simplify.
2009-08-14 17:02:56 +00:00
Mike Bayer bc8217cd76 add a really contrived test that tests the _already_compiling flag. but this seems silly
so far.
2009-08-14 16:54:49 +00:00
Philip Jenvey e94210cdf5 lower call counts for 2.4, hurray 2009-08-14 04:01:54 +00:00
Philip Jenvey 4ec9926e96 don't use the deprecated driver name 2009-08-14 03:04:28 +00:00
Lele Gaifax f50bac1b74 Replace HTML entities with reST markup 2009-08-11 08:19:37 +00:00
Philip Jenvey 1b43a06aac move postgresql's % escape handling out of base 2009-08-11 05:12:50 +00:00
Philip Jenvey cd33d09ce0 o default Connector/J's characterEncoding=UTF-8 for generally better JDBC
unicode handling
o pass url query params down as jdbc connect opts
2009-08-11 04:16:48 +00:00
Mike Bayer 57fe160fe7 some doc work 2009-08-10 23:06:07 +00:00
Mike Bayer 347150090e 0.6 2009-08-10 21:44:14 +00:00
Mike Bayer c3bdad10a3 - simplify MySQLIdentifierPreparer into standard pattern,
thus allowing easy subclassing
- move % sign logic for MySQLIdentifierPreparer into MySQLdb dialect
- paramterize the escape/unescape quote char in IdentifierPreparer
- cut out MySQLTableDefinitionParser cruft
2009-08-10 04:48:00 +00:00
Mike Bayer d564baf593 - the Oracle dialect now features NUMBER which intends
to act justlike Oracle's NUMBER type.  It is the primary
numeric type returned by table reflection and attempts
to return Decimal()/float/int based on the precision/scale
parameters.  [ticket:885]
2009-08-09 23:46:06 +00:00
Mike Bayer f0d2e599b6 - PG: somewhat better support for % signs in table/column names;
psycopg2 can't handle a bind parameter name of
%(foobar)s however and SQLA doesn't want to add overhead
just to treat that one non-existent use case.
[ticket:1279]
- MySQL: somewhat better support for % signs in table/column names;
MySQLdb can't handle % signs in SQL when executemany() is used,
and SQLA doesn't want to add overhead just to treat that one
non-existent use case. [ticket:1279]
2009-08-09 22:11:40 +00:00
Mike Bayer 00d247edcc close out py3k + pg8000 bugs that are fixable for now without pg8000 decimal fix 2009-08-09 21:41:56 +00:00
Mike Bayer e7241263aa python3k fixes 2009-08-09 20:50:46 +00:00
Philip Jenvey 7974625e8b pull from identity_map atomically to avoid a race with weakref cleanup 2009-08-09 20:35:24 +00:00
Philip Jenvey a53d4e2ab4 o oracle+zxjdbc type handling additions
o avoid returning tests on oracle+zxjdbc for now
2009-08-09 00:56:52 +00:00
Mike Bayer f0b848c86f merge 06CHANGES into CHANGES and add more stuff 2009-08-08 23:10:00 +00:00
Mike Bayer a04da2a417 - added **kw to ClauseElement.compare(), so that we can smarten up the "use_get" operation
- many-to-one relation to a joined-table subclass now uses get()
  for a simple load (known as the "use_get" condition),
  i.e. Related->Sub(Base), without the need
  to redefine the primaryjoin condition in terms of the base
  table. [ticket:1186]
- specifying a foreign key with a declarative column,
  i.e. ForeignKey(MyRelatedClass.id) doesn't break the "use_get"
  condition from taking place [ticket:1492]
2009-08-08 22:21:02 +00:00
Mike Bayer cbdccb7fd2 clean up the way we detect MSSQL's form of RETURNING 2009-08-08 17:38:45 +00:00
Mike Bayer 3dc8678529 unwrapped _get_colparams a bit, dropped out an isinstance() call 2009-08-08 17:24:02 +00:00
Mike Bayer a7499ddfc0 fix up oracle tests, returning is on by default 2009-08-08 16:49:28 +00:00
Mike Bayer 491f6796f8 - turned on auto-returning for oracle, some errors
- added make_transient() [ticket:1052]
- ongoing refactor of compiler _get_colparams()  (more to come)
2009-08-08 15:26:43 +00:00
Philip Jenvey b365cc8396 ensure order of larger comparisons 2009-08-08 02:01:46 +00:00
Mike Bayer 57b36ebcb8 documentation updates 2009-08-07 22:17:24 +00:00
Mike Bayer 7b019f3d49 - renamed PASSIVE_NORESULT to PASSIVE_NO_RESULT
- renamed PASSIVE_NO_CALLABLES to PASSIVE_NO_FETCH
- passive now propagates all the way through lazy callables,
all the way into query._get(), so that many-to-one lazy load
can load the instance via the local session but not trigger
any SQL if not available, fixes [ticket:1298] without
messing up consistency of tests added in r6201
- many-to-one also handles returning PASSIVE_NO_RESULT
for the "old" value thus eliminating the need for the
previous value even if the new value is None
- query._get() uses identity_map.get(), which has been
changed to no longer raise KeyError, thus providing
mythical time savings that didn't seem to make any
difference in how fast the unit tests ran.
2009-08-07 21:14:32 +00:00
Lele Gaifax 54dd8b851b Fix #1451: take into account the actual coding system when determining the field length 2009-08-07 12:34:10 +00:00
Lele Gaifax 8aa2886c1a Fix #1429: take into account possible spurious spaces around the DEFAULT keyword 2009-08-07 12:16:15 +00:00
Lele Gaifax 1a9ce60778 M-x whitespace-cleanup 2009-08-07 12:10:33 +00:00
Mike Bayer 328e2647be fix some profiles for 2.4 2009-08-06 22:37:38 +00:00
Mike Bayer cfa1a42328 fix non2.4 gremlin 2009-08-06 22:16:53 +00:00
Mike Bayer 2a23578b55 dont need this anymore 2009-08-06 21:19:20 +00:00
Mike Bayer 8fc5005dfe merge 0.6 series to trunk. 2009-08-06 21:11:27 +00:00
Mike Bayer 7638aa7f24 disabled examples test pending necesary repairs 2009-08-06 20:16:31 +00:00
Mike Bayer 5cac19a9c0 - UPDATE and DELETE do not support ORDER BY, LIMIT, OFFSET,
etc. in standard SQL.  Query.update() and Query.delete()
now raise an exception if any of limit(), offset(),
order_by(), group_by(), or distinct() have been
called. [ticket:1487]
2009-08-02 18:13:07 +00:00
Mike Bayer 68c8b13ed6 - Simplified the sweep of instrumentation in strategies._register_attribute
- Improved support for MapperProperty objects overriding
that of an inherited mapper for non-concrete
inheritance setups - attribute extensions won't randomly
collide with each other.  [ticket:1488]

- Added AttributeExtension to sqlalchemy.orm.__all__
2009-08-02 17:51:33 +00:00
Mike Bayer e58b66838d backported 0.6 r6084 fix for oracle alias names, [ticket:1309] 2009-07-31 23:10:46 +00:00
Philip Jenvey 458c7afa79 fix broken orm debug logging 2009-07-29 00:41:26 +00:00
Mike Bayer b303eb9342 merged [ticket:1486] fix from 0.6 2009-07-28 17:47:54 +00:00
Michael Trier 9ac6f28996 Reverted my screw up of setup.cfg 2009-07-28 01:23:17 +00:00
Michael Trier 6a67cb17c3 Corrected examples tests. I was running from ./test instead of root. 2009-07-28 01:20:52 +00:00
Michael Trier 34aaabf7ea Added in Examples into the test suite so they get exercised regularly. Cleaned up some deprecation warnings in the examples. 2009-07-27 02:12:15 +00:00
Michael Trier 73554aa4fa Corrected annoying deprecation warning on 2.6+ related to mssql and the __new__ calls. 2009-07-26 03:07:42 +00:00
Michael Trier 7f812abb73 Corrected problem with binary test on mssql. Still having issues with prepared statements. 2009-07-26 03:07:32 +00:00
Mike Bayer 306c901946 - Squeezed a few more unnecessary "lazy loads" out of
relation().  When a collection is mutated, many-to-one
backrefs on the other side will not fire off to load
the "old" value, unless "single_parent=True" is set.
A direct assignment of a many-to-one still loads
the "old" value in order to update backref collections
on that value, which may be present in the session
already, thus maintaining the 0.5 behavioral contract.
[ticket:1483]
2009-07-26 01:46:41 +00:00
Mike Bayer 05a82671c3 - Fixed bug in Table and Column whereby passing empty
dict for "info" argument would raise an exception.
[ticket:1482]
2009-07-25 21:40:27 +00:00
Mike Bayer c1a36dfe41 - Fixed bug whereby a load/refresh of joined table
inheritance attributes which were based on
column_property() or similar would fail to evaluate.
[ticket:1480]
2009-07-25 21:26:28 +00:00
Mike Bayer a510e9f23a - Declarative will raise an informative exception if
__table_args__ is passed as a tuple with no dict argument.
Improved documentation.  [ticket:1468]
2009-07-25 20:43:11 +00:00
Mike Bayer c30cd4a6ab - fixed the test for FalseDiscriminator to use Boolean for picky postgresql
- added Query.enable_assertions(False) as a mediocre solution for [ticket:1424].
updated the recipe at http://www.sqlalchemy.org/trac/wiki/UsageRecipes/PreFilteredQuery to
reflect.
- moved most default Query state to be class level variables to start.  the dicts could
go as well but being overly careful to not place mutables there for the moment.
- a visit by the "dunder-private method names aren't cool" police
- continued undisciplined pep-8ness
2009-07-25 20:27:33 +00:00
Mike Bayer 818c9a617e - Using False or 0 as a polymorphic discriminator now
works on the base class as well as a subclass.
[ticket:1440]
2009-07-25 19:42:15 +00:00
Mike Bayer 4d036d6dd4 - Unary expressions such as DISTINCT propagate their
type handling to result sets, allowing conversions like
unicode and such to take place.  [ticket:1420]
2009-07-25 19:34:02 +00:00
Mike Bayer 1e6df0eeb7 - Improved error message when query() is called with
a non-SQL /entity expression. [ticket:1476]
2009-07-25 18:59:56 +00:00
Mike Bayer 066bdaec75 beefed up documentation for count(), [ticket:1465] 2009-07-25 18:54:20 +00:00
Mike Bayer ed8742e685 - The collection proxies produced by associationproxy are now
pickleable.  A user-defined proxy_factory however
is still not pickleable unless it defines __getstate__
and __setstate__. [ticket:1446]
2009-07-25 17:08:38 +00:00
Lele Gaifax 87b50f78f0 Fix small typos in docstring 2009-07-24 15:31:09 +00:00
Mike Bayer 0eb14e7ec5 ensure "rowswitch" for isdelete is supported 2009-07-22 20:41:33 +00:00
Mike Bayer b9b62b2369 - relations() now have greater ability to be "overridden",
meaning a subclass that explicitly specifies a relation()
overriding that of the parent class will be honored
during a flush.  This is currently to support
many-to-many relations from concrete inheritance setups.
Outside of that use case, YMMV.  [ticket:1477]
2009-07-21 21:47:03 +00:00
Mike Bayer f300bb43de - Fixed bug whereby inheritance discriminator part of a
composite primary key would fail on updates.
Continuation of [ticket:1300].
2009-07-21 20:25:36 +00:00
Mike Bayer 8804e1963f - Fixed a bug in extract() introduced in 0.5.4 whereby
the string "field" argument was getting treated as a
ClauseElement, causing various errors within more
complex SQL transformations.
2009-07-17 15:10:54 +00:00
Jason Kirtland f843442d97 Guard against a gc hitting during the sweep for dirty objects. 2009-07-16 23:24:30 +00:00
Mike Bayer f5b055fabb beefed up the description of dialects 2009-07-13 22:53:20 +00:00
Mike Bayer 5503028d8c changed reference to PostgreSQL in docs. 2009-07-13 02:04:54 +00:00
Mike Bayer 40772955a5 - remove docs about partial flush, add docs about disabling autoflush 2009-07-12 22:34:06 +00:00
Mike Bayer d99c0a9ecc - sqlalchemy.orm.join and sqlalchemy.orm.outerjoin are now
added to __all__ in sqlalchemy.orm.*. [ticket:1463]

- Fixed bug where Query exception raise would fail when
a too-short composite primary key value were passed to
get().  [ticket:1458]

- rearranged CHANGES for 0.5.5 to be somewhat severity based.

- commented on [ticket:1445]
2009-07-12 14:33:06 +00:00
Mike Bayer eeecbb8fc6 updates 2009-07-11 21:07:52 +00:00
Jason Kirtland c8fbedfcb3 Formatting 2009-07-10 21:51:40 +00:00
Jason Kirtland ca4744c1cb Implemented recreate() for StaticPool 2009-07-10 21:48:45 +00:00
Mike Bayer 910961fccd - Fixed potential memory leak whereby previously pickled objects
placed back in a session would not be fully garbage collected
unless the Session were explicitly closed out.
2009-07-10 20:01:56 +00:00
Mike Bayer 4e4102f64d - Fixed bug whereby session.is_modified() would raise an exception
if any synonyms were in use.
2009-07-09 01:45:44 +00:00
Mike Bayer 5045bf4f4b - Fixed a bug involving contains_eager(), which would apply itself
to a secondary (i.e. lazy) load in a particular rare case,
producing cartesian products.   improved the targeting
of query.options() on secondary loads overall [ticket:1461].
2009-07-07 17:17:22 +00:00
Mike Bayer c2108dafbd Session.mapper is now *deprecated*.
Call session.add() if you'd like a free-standing object to be
part of your session.  Otherwise, a DIY version of
Session.mapper is now documented at
http://www.sqlalchemy.org/trac/wiki/UsageRecipes/SessionAwareMapper
The method will remain deprecated throughout 0.6.


M    test/ext/test_declarative.py
M    test/orm/test_scoping.py
M    lib/sqlalchemy/orm/scoping.py
M    CHANGES
2009-07-03 15:31:29 +00:00
Lele Gaifax 41bc4d877f Fix deprecated usage of on numeric type 2009-06-23 09:09:13 +00:00
Mike Bayer d8aa899bfb added docs for post_update 2009-06-21 16:21:52 +00:00
Mike Bayer cea9cc5e8e - repaired non-working attributes.set_committed_value function. 2009-06-18 19:37:16 +00:00
Mike Bayer 8a72a5b0bc - Trimmed the pickle format for InstanceState which should further
reduce the memory footprint of pickled instances.  The format
should be backwards compatible with that of 0.5.4 and previous.
2009-06-16 19:23:43 +00:00
Mike Bayer adde312c2a assoc proxy object appends to list automatically [ticket:1351] 2009-06-15 22:39:45 +00:00
Mike Bayer a7d0ed81bd added test to verify #1423 2009-06-15 22:33:12 +00:00
Mike Bayer 964fac813c - Fixed bug whereby list-based attributes, like pickletype
and PGArray, failed to be merged() properly.
2009-06-15 22:23:08 +00:00
Mike Bayer 44b6977cf4 split CHANGES into CHANGES and CHANGES_PRE_05, since I would like CHANGES to be viewable in trac 2009-06-15 22:11:08 +00:00
Mike Bayer 99d3c5f335 - The "foreign_keys" argument of relation() will now propagate
automatically to the backref in the same way that
primaryjoin and secondaryjoin do.   For the extremely
rare use case where the backref of a relation() has
intentionally different "foreign_keys" configured, both sides
now need to be configured explicity (if they do in fact require
this setting, see the next note...).

- ...the only known (and really, really rare) use case where a
different foreign_keys setting was used on the forwards/backwards
side, a composite foreign key that partially points to its own
columns, has been enhanced such that the fk->itself aspect of the
relation won't be used to determine relation direction.
2009-06-13 03:31:30 +00:00
Mike Bayer 45cec095b4 - unit tests have been migrated from unittest to nose.
See README.unittests for information on how to run
the tests.  [ticket:970]
2009-06-10 21:18:24 +00:00
Mike Bayer 698a3c1ac6 - Fixed Query being able to join() from individual columns of
a joined-table subclass entity, i.e.
query(SubClass.foo, SubcClass.bar).join(<anything>).
In most cases, an error "Could not find a FROM clause to join
from" would be raised. In a few others, the result would be
returned in terms of the base class rather than the subclass -
so applications which relied on this erroneous result need to be
adjusted. [ticket:1431]
2009-06-05 21:23:11 +00:00
Mike Bayer 31b95e6cdc - removed test.testing.ORMTest, test.fixtures, and all
dependencies on those.
2009-06-02 21:42:14 +00:00
Mike Bayer e83a1a20f9 move from pXXX to 0.5.5 [ticket:1427] 2009-06-02 15:50:04 +00:00
Mike Bayer 4c786944e8 - Fixed another 0.5.4 bug whereby mutable attributes (i.e. PickleType)
wouldn't be deserialized correctly when the whole object
was serialized.  [ticket:1426]
2009-06-01 22:42:14 +00:00
Mike Bayer c84dd331df slight cleanup i want in 0.5/0.6 2009-05-31 21:27:56 +00:00
Mike Bayer 13d4004774 removed needless "thread" imports from util 2009-05-30 01:09:16 +00:00
Mike Bayer 5ea1d67315 - sql
- Removed an obscure feature of execute() (including connection,
      engine, Session) whereby a bindparam() construct can be sent as
      a key to the params dictionary.  This usage is undocumented
      and is at the core of an issue whereby the bindparam() object
      created implicitly by a text() construct may have the same
      hash value as a string placed in the params dictionary and
      may result in an inappropriate match when computing the final
      bind parameters.   Internal checks for this condition would
      add significant latency to the critical task of parameter
      rendering, so the behavior is removed.  This is a backwards
      incompatible change for any application that may have been
      using this feature, however the feature has never been
      documented.
2009-05-29 18:56:50 +00:00
Mike Bayer fe3902771f - Fixed bug introduced in 0.5.4 whereby Composite types
fail when default-holding columns are flushed.
2009-05-26 22:45:56 +00:00
Mike Bayer 853992649c more fixes to bound parameter exception reporting 2009-05-26 17:03:03 +00:00
Mike Bayer d6b9757778 - added unit test for exception formatting
- Deprecated the hardcoded TIMESTAMP function, which when
used as func.TIMESTAMP(value) would render "TIMESTAMP value".
This breaks on some platforms as Postgres doesn't allow
bind parameters to be used in this context.  The hard-coded
uppercase is also inappropriate and there's lots of other
PG casts that we'd need to support.  So instead, use
text constructs i.e. select(["timestamp '12/05/09'"]).
2009-05-26 01:00:46 +00:00
Mike Bayer 3837a29bfc - Repaired the printing of SQL exceptions which are not
based on parameters.
2009-05-25 15:26:16 +00:00
Mike Bayer 7ff23a5218 - Fixed an attribute error introduced in 0.5.4 which would
occur when merge() was used with an incomplete object.
2009-05-18 16:21:42 +00:00
Mike Bayer d7e531ce9f - Back-ported the "compiler" extension from SQLA 0.6. This
is a standardized interface which allows the creation of custom
ClauseElement subclasses and compilers.  In particular it's
handy as an alternative to text() when you'd like to
build a construct that has database-specific compilations.
See the extension docs for details.
2009-05-17 22:58:21 +00:00
Mike Bayer d1d3c1ad93 unusual ... 2009-05-17 22:44:35 +00:00
Mike Bayer eb30cb1feb - The "polymorphic discriminator" column may be part of a
primary key, and it will be populated with the correct
discriminator value.  [ticket:1300]
2009-05-17 22:20:28 +00:00
Mike Bayer ab0434d648 - Reflecting a FOREIGN KEY construct will take into account
a dotted schema.tablename combination, if the foreign key
references a table in a remote schema. [ticket:1405]
2009-05-17 22:00:33 +00:00
Mike Bayer 9cafc85479 - Exception messages are truncated when the list of bound
parameters is larger than 10, preventing enormous
multi-page exceptions from filling up screens and logfiles
for large executemany() statements. [ticket:1413]
2009-05-17 21:54:17 +00:00
Mike Bayer 155466aad1 - Removed all* O(N) scanning behavior from the flush() process,
i.e. operations that were scanning the full session,
including an extremely expensive one that was erroneously
assuming primary key values were changing when this
was not the case.

* one edge case remains which may invoke a full scan,
  if an existing primary key attribute is modified
  to a new value.
2009-05-17 21:51:40 +00:00
Mike Bayer 2be867ffac - Significant performance enhancements regarding Sessions/flush()
in conjunction with large mapper graphs, large numbers of
      objects:

      - The Session's "weak referencing" behavior is now *full* -
        no strong references whatsoever are made to a mapped object
        or related items/collections in its __dict__.  Backrefs and
        other cycles in objects no longer affect the Session's ability
        to lose all references to unmodified objects.  Objects with
        pending changes still are maintained strongly until flush.
        [ticket:1398]

        The implementation also improves performance by moving
        the "resurrection" process of garbage collected items
        to only be relevant for mappings that map "mutable"
        attributes (i.e. PickleType, composite attrs).  This removes
        overhead from the gc process and simplifies internal
        behavior.

        If a "mutable" attribute change is the sole change on an object
        which is then dereferenced, the mapper will not have access to
        other attribute state when the UPDATE is issued.  This may present
        itself differently to some MapperExtensions.

        The change also affects the internal attribute API, but not
        the AttributeExtension interface nor any of the publically
        documented attribute functions.

      - The unit of work no longer genererates a graph of "dependency"
        processors for the full graph of mappers during flush(), instead
        creating such processors only for those mappers which represent
        objects with pending changes.  This saves a tremendous number
        of method calls in the context of a large interconnected
        graph of mappers.

      - Cached a wasteful "table sort" operation that previously
        occured multiple times per flush, also removing significant
        method call count from flush().

      - Other redundant behaviors have been simplified in
        mapper._save_obj().
2009-05-17 18:17:46 +00:00
Mike Bayer 6515e84d4c fix mysql tests 2009-05-14 01:16:18 +00:00
Mike Bayer 7af9d1c04b - Fixed obscure mapper compilation issue when inheriting
mappers are used which would result in un-initialized
attributes.
2009-05-13 19:34:21 +00:00
Mike Bayer f19d4dc815 - It is now an error to specify both columns of a binary primaryjoin
condition in the foreign_keys or remote_side collection.  Whereas
previously it was just nonsensical, but would succeed in a
non-deterministic way.
2009-05-08 01:41:51 +00:00
Mike Bayer d8c9dcc0ad - Fixed bug which prevented "mutable primary key" dependency
logic from functioning properly on a one-to-one
relation().  [ticket:1406]
- moved MySQL to use innodb for naturalpks tests
2009-05-08 01:07:36 +00:00
Michael Trier 01cdbd0734 Corrected the SQLite SLBoolean type so that it properly treats 1 only as True. Fixes #1402 2009-05-05 00:36:37 +00:00
Mike Bayer f8daff6da1 - MapperOptions and other state associated with query.options()
is no longer bundled within callables associated with each
lazy/deferred-loading attribute during a load.
The options are now associated with the instance's
state object just once when it's populated.  This removes
the need in most cases for per-instance/attribute loader
objects, improving load speed and memory overhead for
individual instances. [ticket:1391]
2009-05-02 17:41:04 +00:00
Mike Bayer f554d0f02b this falls back to "expire" in any case since concat_op is not supported by the evaluator 2009-05-02 15:35:37 +00:00
Michael Trier 78da9e361b Corrected missing stop in the ORM Tutorial. Fixes #1395. 2009-04-29 00:14:09 +00:00
Michael Trier 541a0c8491 Modified savepoint logic in mssql to ensure that it does not step on non-savepoint oriented routines. Savepoint support is still very experimental. 2009-04-28 03:35:35 +00:00
Mike Bayer 85f7547514 - Allowed pickling of PropertyOption objects constructed with
instrumented descriptors; previously, pickle errors would occur
when pickling an object which was loaded with a descriptor-based
option, such as query.options(eagerload(MyClass.foo)).
2009-04-26 21:57:18 +00:00
Michael Trier 1ec5704d14 Modified query_cls on DynamicAttribteImpl to accept a full mixin version of the AppenderQuery. 2009-04-25 15:35:52 +00:00
Mike Bayer 3eeb240fd1 we work with sphinx 0.6.1 now 2009-04-25 15:13:30 +00:00
Ants Aasma 7d0c5f72f9 Query.update() and Query.delete() should turn off eagerloads. Fixes #1378. 2009-04-20 15:00:41 +00:00
Michael Trier 7bb91d034f Fixed adding of deferred or othe column properties to a declarative class. 2009-04-18 15:35:07 +00:00
Michael Trier 33b3360e68 Removed allow_column_override documentation. Fixes #1381. 2009-04-18 01:21:38 +00:00
Michael Trier a7e0fdd5fc Added copy and __copy__ methods to the OrderedDict. Fixes #1377. 2009-04-13 04:25:41 +00:00
Michael Trier b5ad47271e Cleaned up the deprecation problems with the examples. 2009-04-13 03:23:19 +00:00
Michael Trier eba7328c46 Corrected the sqlite float type so that it properly gets reflected as a SLFloat type. Fixes #1273. 2009-04-13 03:05:03 +00:00
Michael Trier e14734c8dd Added in MSSQL reserved words list. Fixes #1310 2009-04-12 02:12:41 +00:00
Michael Trier 2a962802de Added multi part schema name support. Closes #594 and #1341. 2009-04-11 21:36:45 +00:00
Mike Bayer 99d3e251cf - Fixed a unit of work issue whereby the foreign
key attribute on an item contained within a collection
owned by an object being deleted would not be set to
None if the relation() was self-referential. [ticket:1376]
2009-04-11 20:20:38 +00:00
Michael Trier 0143770384 Corrected duplication of serializer docs. Fixes #1375. 2009-04-09 22:57:16 +00:00
Mike Bayer 0790f9c6ac - Fixed documentation for session weak_identity_map -
the default value is True, indicating a weak
referencing map in use.
2009-04-09 21:47:49 +00:00
Mike Bayer 3d9389fcaf test multi-level eager load without the limiting subquery 2009-04-08 04:14:16 +00:00
Ants Aasma 9ffd3ddb00 - Fixed the evaluator not being able to evaluate IS NULL clauses.
- Added evaluator tests to orm/alltests.py
2009-04-06 07:03:13 +00:00
Mike Bayer f77c9f950f disabling triggers for Mysql since it requires SUPER privs 2009-04-05 02:29:45 +00:00
Michael Trier d8a04804f9 Added indexed to the list of reserved keywords (added in 3.6.4). Fixes #1358. 2009-04-04 02:54:09 +00:00
Michael Trier a7574d3a26 Added Oracle examples showing how to use named parameters with a TNS. Fixes #1361. 2009-04-04 02:42:42 +00:00
Michael Trier 7f1e6621f8 Corrected examples in ORM tutorial to specify actual exception being thrown. Fixes 1365. 2009-04-04 02:39:23 +00:00
Michael Trier 1954b87039 Corrected doc notations that suppressed some non-SQL output. Fixes #1366. 2009-04-04 00:48:26 +00:00
Jason Kirtland 6890495d23 Explicit String length 2009-04-03 19:43:10 +00:00
Mike Bayer 4e2ac31712 add defaults to alltests 2009-04-03 19:37:56 +00:00
Mike Bayer 13cc1279f8 - Fixed bug in relation(), introduced in 0.5.3,
whereby a self referential relation
from a base class to a joined-table subclass would
not configure correctly.
2009-04-02 14:53:28 +00:00
Michael Trier 6010afb28f Lots of fixes to the code examples to specify imports explicitly.
Explicit imports make it easier for users to understand the examples.
Additionally a lot of the examples were fixed to work with the changes in the
0.5.x code base. One small correction to the Case expression.  Thanks a bunch
to Adam Lowry! Fixes #717.
2009-03-31 22:31:08 +00:00
Mike Bayer 832ea82fef - Fixed another location where autoflush was interfering
with session.merge().  autoflush is disabled completely
for the duration of merge() now. [ticket:1360]
2009-03-31 14:57:19 +00:00
Jason Kirtland aca84bebb0 extract() is now dialect-sensitive and supports SQLite and others. 2009-03-30 20:41:48 +00:00
Mike Bayer 1ad157a0a1 remove needless print stuff 2009-03-30 15:38:00 +00:00
Mike Bayer d0f67e2c4d - Lazy loader will not use get() if the "lazy load"
SQL clause matches the clause used by get(), but
contains some parameters hardcoded.  Previously
the lazy strategy would fail with the get().  Ideally
get() would be used with the hardcoded parameters
but this would require further development.
[ticket:1357]
2009-03-29 21:21:10 +00:00
Mike Bayer 290ff9930a - coverage dumps out separate reports for individual packages
- other coverage tips
2009-03-29 20:23:05 +00:00
Mike Bayer 12b5ab7e4f - added a section on using aliased() with a subquery
- doctests needed huge number of +NORMALIZE_WHITESPACE not needed before for some reason
2009-03-29 18:39:54 +00:00
Michael Trier 8e8546c919 Corrected docstring for class_mapper. It does not accept an object. Fixes #1316. 2009-03-29 04:29:50 +00:00
Michael Trier af012dd588 Modified information_schema change to keep it backwards compatible. 2009-03-29 02:08:22 +00:00
Mike Bayer ccdd7f603e fix crappity 2009-03-27 21:42:41 +00:00
Mike Bayer d65c25bcc9 - Fixed __repr__() and other _get_colspec() methods on
ForeignKey constructed from __clause_element__() style
construct (i.e. declarative columns).  [ticket:1353]
2009-03-27 21:41:36 +00:00
Michael Trier e26b9e5f98 Corrected problem with information schema not working with binary collation on mssql. Fixes #1343. 2009-03-27 21:27:34 +00:00
Mike Bayer 18106b5e5b - Fixed the "set collection" function on "dynamic" relations
to initiate events correctly.  Previously a collection
could only be assigned to a pending parent instance,
otherwise modified events would not be fired correctly.
Set collection is now compatible with merge(),
fixes [ticket:1352].
2009-03-27 19:54:10 +00:00
Mike Bayer 3223bafafe more tests 2009-03-24 01:22:38 +00:00
Mike Bayer 533a0ab955 - Fixed bug in dynamic_loader() where append/remove events
after construction time were not being propagated to the
      UOW to pick up on flush(). [ticket:1347]
2009-03-24 01:19:45 +00:00
Mike Bayer 0bb1e1b8e3 add collections module to API ref for completeness. links to the mapper documentation
which is less wordy.
2009-03-21 18:03:47 +00:00
Mike Bayer 0983b610b4 - An alias() of a select() will convert to a "scalar subquery"
when used in an unambiguously scalar context, i.e. it's used
in a comparison operation.  This applies to
the ORM when using query.subquery() as well.
2009-03-21 16:12:37 +00:00
Mike Bayer 3ecf84f5ad - Fixed SQLite reflection methods so that non-present
cursor.description, which triggers an auto-cursor
      close, will be detected so that no results doesn't
      fail on recent versions of pysqlite which raise
      an error when fetchone() called with no rows present.
2009-03-17 15:09:49 +00:00
Mike Bayer 53deb98918 - Query.join() can now construct multiple FROM clauses, if
needed.  Such as, query(A, B).join(A.x).join(B.y)
might say SELECT A.*, B.* FROM A JOIN X, B JOIN Y.
Eager loading can also tack its joins onto those
multiple FROM clauses.  [ticket:1337]
2009-03-15 03:02:42 +00:00
Mike Bayer 3f0252abc7 - Fixed bug where column_prefix wasn't being checked before
not mapping an attribute that already had class-level
      name present.
2009-03-11 21:45:57 +00:00
Mike Bayer bef0bb95e7 fix formatting to match unit tests 2009-03-11 05:48:02 +00:00
Jonathan Ellis a4ee98fe66 add schema to entity method 2009-03-09 21:24:43 +00:00
Mike Bayer 68ee348d36 - a forward and complementing backwards reference which are both
of the same direction, i.e. ONETOMANY or MANYTOONE,
is now detected, and an error message is raised.
Saves crazy CircularDependencyErrors later on.
2009-03-09 01:20:29 +00:00
Mike Bayer 1b5d224df5 take 2 2009-03-08 19:40:12 +00:00
Mike Bayer dc7974deae attempt to exlude sqlite 3.5.9 and below, for buildbot failure. not clear which version of sqlite fixes this particular issue 2009-03-08 19:29:56 +00:00
Mike Bayer de06f512db - Query.group_by() properly takes into account aliasing applied
to the FROM clause, such as with select_from(), using
with_polymorphic(), or using from_self().
2009-03-08 19:06:12 +00:00
Mike Bayer 3953fe0ad4 some buildbot fixes 2009-03-08 02:35:46 +00:00
Mike Bayer 4928ea0e46 - updated builders for latest sphinx tip
- applied patch from [ticket:1321]
2009-03-02 00:01:09 +00:00
Mike Bayer b84c3b3469 - The "objects" argument to session.flush() is deprecated.
State which represents the linkage between a parent and
child object does not support "flushed" status on
one side of the link and not the other, so supporting
this operation leads to misleading results.
[ticket:1315]
2009-03-01 23:53:58 +00:00
Mike Bayer 77d6d31542 - Added PGUuid and PGBit types to
sqlalchemy.databases.postgres. [ticket:1327]

- Refection of unknown PG types won't crash when those
types are specified within a domain.  [ticket:1327]

- executemany() in conjunction with INSERT..RETURNING is documented as undefined by psycopg2.
2009-03-01 20:24:02 +00:00
Mike Bayer 8d295ec118 - Fixed adaptation of EXISTS clauses via any(), has(), etc.
in conjunction with an aliased object on the left and
of_type() on the right.  [ticket:1325]
2009-02-26 15:16:06 +00:00
Lele Gaifax b2204616c7 Fix markup glitch 2009-02-25 13:20:38 +00:00
Mike Bayer a8021432b8 - pared down private and semi-private functions in the attributes package.
- simplified the process of establishment and unestablishment of
class management from a mapper perspective; class manager setup/teardown
is now symmetric (ClassManager would never be fully de-associated previously).
- class manager now unconditionally decorates __init__.  this has a slight
behavior change for an unmapped subclass of a mapped superclass, in that
InstanceState creation corresponds to that of the superclass.  This
still doesn't allow unmapped subclasses to be usable in mapper
situations, though.
2009-02-23 00:08:37 +00:00
Mike Bayer b60185a078 - Declarative will accept a table-bound column as a property
when used in conjunction with __table__, if the column is already
present in __table__.  The column will be remapped to the given
key the same way as when added to the mapper() properties dict.
2009-02-22 19:35:36 +00:00
Mike Bayer e8b57a47cd - Query won't fail with weakref error when a non-mapper/class
instrumented descriptor is passed, raises
"Invalid column expession".
2009-02-20 15:45:25 +00:00
Mike Bayer 734e02c4f1 - Declarative locates the "inherits" class using a search
through __bases__, to skip over mixins that are local
to subclasses.
2009-02-19 15:48:37 +00:00
Gaëtan de Menten 40b20c680a fix docstring indent in orm.util.with_parent 2009-02-19 07:40:25 +00:00
Michael Trier 07e28e74ee Corrected issue on mssql where max_identifier_length was not being respected. 2009-02-18 03:34:58 +00:00
Mike Bayer 911c7b9b36 - Session.scalar() now converts raw SQL strings to text()
the same way Session.execute() does and accepts same
alternative **kw args.
2009-02-17 23:10:52 +00:00
Mike Bayer 38c9a5be50 - Declarative will properly interpret the "foreign_keys" argument
on a backref() if it's a string.
2009-02-17 22:09:30 +00:00
Mike Bayer 689a144a71 - Fixed a recursive pickling issue in serializer, triggered
by an EXISTS or other embedded FROM construct.
2009-02-17 12:56:48 +00:00
Mike Bayer e329fb3178 - Declarative figures out joined-table inheritance primary join
condition even if "inherits" mapper argument is given
explicitly.  Allows mixins to be used with joined table
inheritance.
2009-02-16 23:49:53 +00:00
Mike Bayer 2cee9cb243 - Added an attribute helper method `set_committed_value` in
sqlalchemy.orm.attributes.  Given an object, attribute name,
and value, will set the value on the object as part of its
"committed" state, i.e. state that is understood to have
been loaded from the database.   Helps with the creation of
homegrown collection loaders and such.
- documented public attributes helper functions.
2009-02-15 20:43:14 +00:00
Mike Bayer 1de9012573 - annotations store 'parententity' as well as 'parentmapper'
- ORMAdapter filters all replacements against a non-compatible 'parentmapper' annotation
- Other filterings, like
query(A).join(A.bs).filter(B.foo=='bar'), were erroneously
adapting "B.foo" as though it were an "A".
2009-02-13 18:08:40 +00:00
Rick Morrison 7954673ea7 Preliminary support for pymssql 1.0.1 [Ticket:1318] 2009-02-13 17:18:52 +00:00
Mike Bayer f180cc0c9f - Fixed bugs in Query regarding simultaneous selection of
multiple joined-table inheritance entities with common base
classes, previously the adaption applied to "e2" on
"e1 JOIN e2" would be partially applied to "e1".  Additionally,
comparisons on relations (i.e. Entity2.related==e2)
were not getting adapted correctly.
2009-02-13 17:14:05 +00:00
Mike Bayer 2d6b3f09eb move test by itself so that no fixture data is inserted 2009-02-12 16:02:23 +00:00
Mike Bayer 1d3185139a - a session.expire() on a particular collection attribute
will clear any pending backref additions as well, so that
the next access correctly returns only what was present
in the database.  Presents some degree of a workaround for
[ticket:1315], although we are considering removing the
flush([objects]) feature altogether.
2009-02-11 20:38:30 +00:00
Mike Bayer 60dd7842f0 - Added "post_configure_attribute" method to InstrumentationManager,
so that the "listen_for_events.py" example works again.
[ticket:1314]
2009-02-11 18:23:35 +00:00
Gaëtan de Menten a9817ae244 fix Query.update docstring 2009-02-10 11:25:25 +00:00
Mike Bayer 03b5b34114 - anonymous alias names now truncate down to the max length
allowed by the dialect.  More significant on DBs like
Oracle with very small character limits. [ticket:1309]
2009-02-10 01:20:45 +00:00
Mike Bayer 6336e55069 need sizes for mysql 2009-02-07 22:18:40 +00:00
Mike Bayer 2dba55cb27 - When flushing partial sets of objects using session.flush([somelist]),
pending objects which remain pending after the operation won't
inadvertently be added as persistent. [ticket:1306]
2009-02-07 21:57:30 +00:00
Mike Bayer 1c751e3ddb - PG Index reflection won't fail when an index with
multiple expressions is encountered.
2009-02-03 00:22:01 +00:00
Gaëtan de Menten 76e001d39e fix docstring typo 2009-02-02 09:32:56 +00:00
Mike Bayer e39b98ca7b - Fixed missing _label attribute on Function object, others
when used in a select() with use_labels (such as when used
in an ORM column_property()).  [ticket:1302]
2009-02-01 18:20:20 +00:00
Michael Trier 4b252f659e Added a few IDENTITY tests for mssql. 2009-01-31 21:20:04 +00:00
Mike Bayer 85e8509399 detect backref string as basestring, not str. [ticket:1301] 2009-01-30 22:47:28 +00:00
Mike Bayer 0749bc3850 appease older sqlite version 2009-01-29 17:03:49 +00:00
Mike Bayer f6718dc6d6 fix serialize 2009-01-29 17:03:04 +00:00
Mike Bayer 10dbde43db - The per-dialect cache used by TypeEngine to cache
dialect-specific types is now a WeakKeyDictionary.
This to prevent dialect objects from
being referenced forever for an application that
creates an arbitrarily large number of engines
or dialects.   There is a small performance penalty
which will be resolved in 0.6.  [ticket:1299]
2009-01-29 16:09:14 +00:00
Mike Bayer 966119f4d3 - improvements to the "determine direction" logic of
relation() such that the direction of tricky situations
like mapper(A.join(B)) -> relation-> mapper(B) can be
determined.
2009-01-29 06:40:29 +00:00
Mike Bayer b2ee806b6f some docstring stuff 2009-01-28 16:44:57 +00:00
Mike Bayer 22b32da23a unit test fixes 2009-01-28 16:00:16 +00:00
Mike Bayer 397ba5d73d - _CalculatedClause is gone
- Function rolls the various standalone execution functionality of CC into itself,
accesses its internal state more directly
- collate just uses _BinaryExpression, don't know why it didn't do this already
- added new _Case construct, compiles directly
- the world is a happier place
2009-01-28 01:28:20 +00:00
Mike Bayer 7e7aa8f7c2 - Query now implements __clause_element__() which produces
its selectable, which means a Query instance can be accepted
in many SQL expressions, including col.in_(query),
union(query1, query2), select([foo]).select_from(query),
etc.

- the __selectable__() interface has been replaced entirely
by __clause_element__().
2009-01-27 01:05:20 +00:00
Mike Bayer 332be8c396 further fixes to sphinx.sty per progress on the sphinx trunk 2009-01-24 22:42:30 +00:00
Mike Bayer cb69dd6aea create correct hyperlink for the pdf file 2009-01-24 19:51:32 +00:00
Mike Bayer 20132a9caa - 0.5.3
- add new directives to sphinx.sty.  pdf output is still currently busted for other reasons, however.
2009-01-24 19:37:41 +00:00
Mike Bayer c10104f0a8 - refined and clarified query.__join() for readability
- _ORMJoin() gets a new flag join_to_left to specify if
we really want to alias from the existing left side or not.  eager loading
wants this flag off in almost all cases, query.join() usually wants it on.
- query.join()/outerjoin() will now properly join an aliased()
construct to the existing left side, even if query.from_self()
or query.select_from(someselectable) has been called.
[ticket:1293]
2009-01-24 17:29:56 +00:00
Mike Bayer febf00ea5d moved the non-expire of unloaded deferred attributes into the attributes package 2009-01-24 15:43:05 +00:00
Mike Bayer a03aed2dca - session.expire() and related methods will not expire() unloaded
deferred attributes.  This prevents them from being needlessly
loaded when the instance is refreshed.
2009-01-24 00:22:49 +00:00
Michael Trier 84a38c81e4 Correction to reflection fix r5718 to handle Binary / other numeric types. 2009-01-23 01:45:20 +00:00
Rick Morrison 0caf971263 mssql: modified table reflection code to use only kwargs when constructing coldefs. 2009-01-23 00:53:32 +00:00
Mike Bayer fc7de2aafd - Fixed an eager loading bug whereby self-referential eager
loading would prevent other eager loads, self referential or not,
from joining to the parent JOIN properly.  Thanks to Alex K
for creating a great test case.
2009-01-22 18:28:27 +00:00
Mike Bayer 3954df86cb - Adjusted the attribute instrumentation change from 0.5.1 to
fully establish instrumentation for subclasses where the mapper
was created after the superclass had already been fully
instrumented. [ticket:1292]
2009-01-22 03:55:48 +00:00
Michael Trier a7459fe1ab Trying one more time to get the decimal handling on mssql right. Closes #1282. 2009-01-22 01:55:06 +00:00
Michael Trier 52e2c2d916 Restored convert_unicode handling on mssql. Fixes #1291. 2009-01-22 01:46:04 +00:00
Mike Bayer 7c56371f81 - Further refined 0.5.1's warning about delete-orphan cascade
placed on a many-to-many relation.   First, the bad news:
the warning will apply to both many-to-many as well as
many-to-one relations.  This is necessary since in both
cases, SQLA does not scan the full set of potential parents
when determining "orphan" status - for a persistent object
it only detects an in-python de-association event to establish
the object as an "orphan".  Next, the good news: to support
one-to-one via a foreign key or assocation table, or to
support one-to-many via an association table, a new flag
single_parent=True may be set which indicates objects
linked to the relation are only meant to have a single parent.
The relation will raise an error if multiple parent-association
events occur within Python.

- Fixed bug in delete-orphan cascade whereby two one-to-one
relations from two different parent classes to the same target
class would prematurely expunge the instance.  This is
an extension of the non-ticketed fix in r4247.

- the order of "sethasparent" flagging in relation to
AttributeExtensions has been refined such that false setparents
are issued before the event, true setparents issued afterwards.
event handlers "know" that a remove event originates
from a non-orphan but need to know if its become an orphan,
and that append events will become non-orphans but need to know
if the event originates from a non-orphan.
2009-01-20 21:35:57 +00:00
Mike Bayer 9fc05aae02 added some missing internal types for reflection, [ticket:1287] 2009-01-20 04:12:00 +00:00
Mike Bayer c1384f4c83 remove comparison/group by on TEXT columns 2009-01-19 22:28:48 +00:00
Mike Bayer 072039945b - Further fixes to the "percent signs and spaces in column/table
names" functionality. [ticket:1284]
- Still doesn't work for PG/MySQL, which unfortunately would require
post_process_text() calls all over the place.  Perhaps % escaping
can be assembled into IdentifierPreparer.quote() since that's where
identifier names are received.
2009-01-18 17:08:28 +00:00
Mike Bayer 86fcffc854 dont need pre-0.5 note 2009-01-17 21:43:19 +00:00
Michael Trier 27c4e7aade Corrected handling of large decimal values on mssql. Added more robust tests.
- Removed string manipulation on floats. Float types are now passed through
  to mssql as is.
- Fixes #1280
2009-01-17 20:57:18 +00:00
Mike Bayer f5eca3933e more session updates 2009-01-17 20:06:54 +00:00
Mike Bayer 2a3135744a docstring fixup 2009-01-17 19:30:32 +00:00
Mike Bayer 2ab5b4f30c fix errant foreign key 2009-01-17 19:28:46 +00:00
Mike Bayer e1cdfc9400 fixed more save/clear calls 2009-01-17 19:07:19 +00:00
Mike Bayer b996bcff52 - The "clear()", "save()", "update()", "save_or_update()"
Session methods have been deprecated, replaced by
"expunge_all()" and "add()".  "expunge_all()" has also
been added to ScopedSession.
2009-01-17 18:19:29 +00:00
Mike Bayer 2ac2770155 explicit rollback to get the connection back to the pool 2009-01-17 17:04:51 +00:00
Mike Bayer 345eaeed74 WeakCompositeKey was coded incorrectly and was not weakly referencing anything. However when repaired, the usage within RelationLoader._create_joins() still creates cycles between key elements and the value placed in the dict. In the interests of risk reduction, WCK is now removed and the two caches it was used for are now non-cached. Speed comparisons with one join/eager-heavy web application show no noticeable effect in response time. 2009-01-17 06:27:02 +00:00
Mike Bayer b58d6fe9d9 - misc savepoint test
- don't need dialect_impl() for Text
2009-01-16 20:16:31 +00:00
Mike Bayer 79a7f1723a - Using delete-orphan on a many-to-many relation is deprecated.
This produces misleading or erroneous results since SQLA does
not retrieve the full list of "parents" for m2m.  To get delete-orphan
behavior with an m2m table, use an explcit association class
so that the individual association row is treated as a parent.
[ticket:1281]

- delete-orphan cascade always requires delete cascade.  Specifying
delete-orphan without delete now raises a deprecation warning.
[ticket:1281]
2009-01-15 18:08:48 +00:00
Mike Bayer 454f1d7f58 - Query.from_self() as well as query.subquery() both disable
the rendering of eager joins inside the subquery produced.
The "disable all eager joins" feature is available publically
via a new query.enable_eagerloads() generative. [ticket:1276]
- Added a rudimental series of set operations to Query that
receive Query objects as arguments, including union(),
union_all(), intersect(), except_(), insertsect_all(),
except_all().  See the API documentation for
Query.union() for examples.
- Fixed bug that prevented Query.join() and eagerloads from
attaching to a query that selected from a union or aliased union.
2009-01-15 17:08:56 +00:00
Mike Bayer 37b7e458c2 - use ForeignKey.column as _colspec source in Column._make_proxy(), preventing needless
redundant string arithmetic in memoized ForeignKey.column method
- _pre_existing_column attribute becomes optional, only needed for original Table-bound column, not proxies
- compare two ForeignKeys based on target_fullname, don't assume self._colspec is a string
- Fixed bug when overriding a Column with a ForeignKey
on a reflected table, where derived columns (i.e. the
"virtual" columns of a select, etc.) would inadvertently
call upon schema-level cleanup logic intended only
for the original column. [ticket:1278]
2009-01-14 20:48:01 +00:00
Mike Bayer 76a7818013 - Improved the methodology to handling percent signs in column
names from [ticket:1256].  Added more tests.  MySQL and
Postgres dialects still do not issue correct CREATE TABLE
statements for identifiers with percent signs in them.
2009-01-14 19:55:20 +00:00
Lele Gaifax 4fad095858 Fix a hyperref 2009-01-14 17:02:17 +00:00
Mike Bayer 49f6342e37 prefer this methods 2009-01-13 15:56:51 +00:00
Mike Bayer 3e3f309cf9 - It's an error to add new Column objects to a declarative class
that specified an existing table using __table__.
2009-01-13 15:45:59 +00:00
Mike Bayer b99bdc7cee - Column with no name (as in declarative) won't raise a
NoneType error when it's string output is requsted
(such as in a stack trace).
2009-01-13 15:38:38 +00:00
Mike Bayer b23f8c0f2e - Fixed a bug with the unitofwork's "row switch" mechanism,
i.e. the conversion of INSERT/DELETE into an UPDATE, when
combined with joined-table inheritance and an object
which contained no defined values for the child table where
an UPDATE with no SET clause would be rendered.
2009-01-13 06:11:17 +00:00
Jason Kirtland 313762e86f - Tightened up **kw on ColumnProperty and its front-end functions. 2009-01-13 02:43:52 +00:00
Mike Bayer 4ae6690bb9 happy new year 2009-01-12 21:19:11 +00:00
Mike Bayer 32add82d01 - Can now specify Column objects on subclasses which have no
table of their own (i.e. use single table inheritance).
The columns will be appended to the base table, but only
mapped by the subclass.

- For both joined and single inheriting subclasses, the subclass
will only map those columns which are already mapped on the
superclass and those explicit on the subclass.  Other
columns that are present on the `Table` will be excluded
from the mapping by default, which can be disabled
by passing a blank `exclude_properties` collection to the
`__mapper_args__`.  This is so that single-inheriting
classes which define their own columns are the only classes
to map those columns.   The effect is actually a more organized
mapping than you'd normally get with explicit `mapper()`
calls unless you set up the `exclude_properties` arguments
explicitly.

- docs/tests
2009-01-12 20:36:06 +00:00
Mike Bayer dc0bbdd92a oh, its UNION ordering that's changing 2009-01-12 16:50:19 +00:00
Mike Bayer a052a44f76 more comparator tweaks 2009-01-12 16:28:01 +00:00
Mike Bayer 68c247358b Ensure RowTuple names are correct by adding "key" to QueryableAttribute. 2009-01-12 15:58:09 +00:00
Mike Bayer fd3b037e52 suspect the InstrumentedSet/set comparison is failing for some reason 2009-01-12 04:04:33 +00:00
Mike Bayer ab9bf61445 *more* sqlite appeasement 2009-01-12 00:12:10 +00:00
Mike Bayer 91001b84c6 mysql/pg sensitive fixes 2009-01-12 00:06:47 +00:00
Mike Bayer 16c710a227 don't INSERT a blank row if no rows passed. (breaks all the tests for SQLite on the buildbot....) 2009-01-11 23:55:35 +00:00
Mike Bayer fc46b9eb5b NotSupportedError is a DBAPI wrapper which takes four args and is expected to originate from the DBAPI layer.
Moved those error throws to CompileError/InvalidRequestError.
2009-01-11 23:37:19 +00:00
Mike Bayer f2c302d03a added an order by 2009-01-11 23:34:36 +00:00
Mike Bayer 209e888e1b - Concrete inheriting mappers now instrument attributes which are inherited from the superclass, but are not defined for the concrete mapper itself, with an InstrumentedAttribute that issues a descriptive error when accessed. [ticket:1237]
- Added a new `relation()` keyword `back_populates`.  This allows configuation of backreferences using explicit relations. [ticket:781]  This is required when creating bidirectional relations between a hierarchy of concrete mappers and another class. [ticket:1237]
- Test coverage added for `relation()` objects specified on concrete mappers. [ticket:1237]
- A short documentation example added for bidirectional relations specified on concrete mappers. [ticket:1237]
- Mappers now instrument class attributes upon construction with the final InstrumentedAttribute object which remains persistent.  The `_CompileOnAttr`/`__getattribute__()` methodology has been removed.  The net effect is that Column-based mapped class attributes can now be used fully at the class level without invoking a mapper compilation operation, greatly simplifying typical usage patterns within declarative. [ticket:1269]
- Index now accepts column-oriented InstrumentedAttributes (i.e. column-based mapped class attributes) as column arguments.  [ticket:1214]
- Broke up attributes.register_attribute into two separate functions register_descriptor and register_attribute_impl.    The first assembles an InstrumentedAttribute or Proxy descriptor, the second assembles the AttributeImpl inside the InstrumentedAttribute.  register_attribute remains for outside compatibility.  The argument lists have been simplified.
- Removed class_manager argument from all but MutableScalarAttributeImpl (the branch had removed class_ as well but this has been reverted locally to support the serializer extension).
- Mapper's previous construction of _CompileOnAttr now moves to a new MapperProperty.instrument_class() method which is called on all MapperProperty objects at the moment the mapper receives them. All MapperProperty objects now call attributes.register_descriptor within that method to assemble an InstrumentedAttribute object directly.
- InstrumentedAttribute now receives the "property" attribute from the given PropComparator.  The guesswork within the constructor is removed, and allows "property" to serve as a mapper compilation trigger.
- RelationProperty.Comparator now triggers compilation of its parent mapper within a util.memoized_property accessor for the "property" attribute, which is used instead of "prop" (we can probably remove "prop").
- ColumnProperty and similar handle most of their initialization in their __init__ method since they must function fully at the class level before mappers are compiled.
- SynonymProperty and ComparableProperty move their class instrumentation logic to the new instrument_class() method.
- LoaderStrategy objects now add their state to existing InstrumentedAttributes using attributes.register_attribute_impl.  Both column and relation-based loaders instrument in the same way now, with a unique InstrumentedAttribute *and* a unique AttributeImpl for each class in the hierarchy.  attribute.parententity should now be correct in all cases.
- Removed unitofwork.register_attribute, and simpified the _register_attribute methods into a single function in strategies.py.  unitofwork exports the UOWEventHandler extension directly.
- To accomodate the multiple AttributeImpls across a class hierarchy, the sethasparent() method now uses an optional "parent_token" attribute to identify the "parent".  AbstractRelationLoader sends the MapperProperty along to serve as this token.  If the token isn't present (which is only the case in the attributes unit tests), the AttributeImpl is used instead, which is essentially the same as the old behavior.
- Added new ConcreteInheritedProperty MapperProperty.  This is invoked for concrete mappers within _adapt_inherited_property() to accomodate concrete mappers which inherit unhandled attributes from the base class, and basically raises an exception upon access.  [ticket:1237]
- attributes.register_attribute and register_descriptor will now re-instrument an attribute unconditionally without checking for a previous attribute.  Not sure if this is controversial. It's needed so that ConcreteInheritedProperty instrumentation can be overridden by an incoming legit MapperProperty without any complexity.
- Added new UninstrumentedColumnLoader LoaderStrategy.  This is used by the polymorphic_on argument when the given column is not represented within the mapped selectable, as is typical with a concrete scenario which maps to a polymorphic union.  It does not configure class instrumentation, keeping polymorphic_on from getting caught up in the new concrete attribute-checking logic.
- RelationProperty now records its "backref" attributes using a set assigned to `_reverse_property` instead of a scalar.  The `back_populates` keyword allows any number of properties to be involved in a single bidirectional relation.  Changes were needed to RelationProperty.merge(), DependencyProcessor to accomodate for the new multiple nature of this attribute.
- Generalized the methodology used by ManyToManyDP to check for "did the other dependency already handle this direction", building on the `_reverse_property` collection.
- post_update logic within dependency.py moves to use the same methodology as ManyToManyDP so that "did the other dependency do this already" checks are made to be specific to the two dependent instances.
- Caught that RelationProperty.merge() was writing to instance.__dict__ directly (!) - repaired to talk to instance_state.dict.
- Removed needless eager loading example from concrete mapper docs.
- Added test for [ticket:965].
- Added the usual Node class/nodes table to orm/_fixtures.py, but haven't used it for anything yet.   We can potentially update test/orm/query.py to use this fixture.
- Other test/documentation cleanup.
2009-01-11 22:41:20 +00:00
Michael Trier db33ad9dec Corrected SAVEPOINT support on the adodbapi dialect by changing the handling
of savepoint_release, which is unsupported on mssql.

The way it was being discarded previously resulted in an empty execute being
called on the dialect; adodbapi didn't like that much.
2009-01-11 19:15:37 +00:00
Michael Trier 9a3b662ec1 Modified the do_begin handling in mssql to use the Cursor not the Connection.
This corrects a problem where we were trying to call execute on the Connection
object instead of against the cursor. This is supported on pyodbc but not in
the DBAPI. Overrode the behavior in pymssql to not do special do_begin
processing on that dialect.
2009-01-11 19:15:30 +00:00
Mike Bayer 7b3b9f559a - 0.5.1 bump
- modernized mapper()/no table exception
- added __tablename__ exception to declarative since ppl keep complaining
2009-01-11 16:45:45 +00:00
Mike Bayer 4316ecb358 clarified docs on foreign key cascades, mapper extension methods during delete() and update() methods 2009-01-10 01:30:56 +00:00
Mike Bayer e9f787f9fe query.delete(False) is not so bad 2009-01-08 15:33:34 +00:00
Ants Aasma c695c3e061 Added the missing keywords from MySQL 4.1 so they get escaped properly. 2009-01-08 15:16:32 +00:00
Mike Bayer cacc64d809 typo 2009-01-07 01:13:29 +00:00
Jason Kirtland 4220fc0943 Formatting fixups 2009-01-06 19:11:06 +00:00
Mike Bayer 786a96fdc5 doh its 0.5.0 2009-01-06 19:04:08 +00:00
Mike Bayer 0e79d6e28e move memusage to the isolation chamber 2009-01-06 18:30:38 +00:00
Mike Bayer a3ab10c3f3 - removed 2.3 compat stuff
- updated MANIFEST for the newer build
2009-01-06 18:19:59 +00:00
Mike Bayer bd3d262640 next release is 0.5.0 2009-01-06 17:15:27 +00:00
Mike Bayer d14317fd7a - query.join() raises an error when the target of the join
doesn't match the property-based attribute - while it's
unlikely anyone is doing this, the SQLAlchemy author was
guilty of this particular loosey-goosey behavior.
2009-01-06 04:30:11 +00:00
Michael Trier e1401bd28e Forgot to sqash a commit. Follow up on mssql dates refactoring. 2009-01-05 22:33:09 +00:00
Michael Trier bc2c1b2f94 mssql date / time refactor.
- Added new MSSmallDateTime, MSDateTime2, MSDateTimeOffset, MSTime types
- Refactored the Date/Time types. The smalldatetime data type no longer
  truncates to a date only, and will now be mapped to the MSSmallDateTime
  type. Closes #1254.
2009-01-05 22:05:51 +00:00
Mike Bayer c55d3f8a7c made the "you passed a non-aliased selectable" warning scarier. scarier ! 2009-01-05 20:02:23 +00:00
Mike Bayer 182badb266 - property.of_type() is now recognized on a single-table
inheriting target, when used in the context of
prop.of_type(..).any()/has(), as well as
query.join(prop.of_type(...)).
2009-01-05 19:23:56 +00:00
Mike Bayer b5e0d613fa if at first you don't succeed, fail, fail again 2009-01-05 17:43:40 +00:00
Mike Bayer 29df16432d assume table.schema, not None, when constraint reflection has no explicit schema. unit test TBD. 2009-01-05 16:08:12 +00:00
Mike Bayer ec344ebf72 - Generalized the IdentityManagedState._instance_dict() callable
to the IdentityMap class so that Weak/StrongInstanceDict both
have the same behavior wrt the state referencing the map
- Fixed bug when using weak_instance_map=False where modified
events would not be intercepted for a flush(). [ticket:1272]
2009-01-05 15:34:09 +00:00
Michael Trier d90cf48c46 Corrected a few docs and didn't realize we put pyodbc first in the search list. 2009-01-04 03:31:05 +00:00
Mike Bayer 54c00bb127 docstrings for the hated fold_equivalents argument/function 2009-01-03 22:40:58 +00:00
Mike Bayer 653191d913 added teardown_instance() to complement setup_instance().
Based on the instance/class agnostic behavior of ClassManager, this might be the best we can
do regarding [ticket:860]
2009-01-03 22:15:38 +00:00
Mike Bayer 88a799379f - query.order_by() accepts None which will remove any pending
order_by state from the query, as well as cancel out any
mapper/relation configured ordering. This is primarily useful
for overriding the ordering specified on a dynamic_loader().
[ticket:1079]
2009-01-03 20:52:34 +00:00
Mike Bayer db0d59191b added the significant test for #1247 2009-01-03 20:31:43 +00:00
Michael Trier 770f603afb Corrected an issue on mssql where Numerics would not accept an int. 2009-01-03 20:07:17 +00:00
Mike Bayer 9b89103394 added order_by test coverage as per [ticket:1218] 2009-01-03 20:06:53 +00:00
Mike Bayer be0c5e4a0b one more typo 2009-01-03 19:37:17 +00:00
Mike Bayer 7e57e282c5 fixed critical errors in assocationproxy docs while we wait for the all new and improved version 2009-01-03 19:35:59 +00:00
Mike Bayer 67909d9645 - Fixed bug which was preventing out params of certain types
from being received; thanks a ton to huddlej at wwu.edu !
[ticket:1265]
2009-01-03 18:58:52 +00:00
Mike Bayer 4fcb1e97d9 identified the SQLite changes which affect default reflection 2009-01-03 18:06:59 +00:00
Michael Trier de5a4c544d Added a note about mssql compatibility levels. 2009-01-03 17:42:31 +00:00
Mike Bayer ba2bd53ec9 send a NASA probe to the buildbot 2009-01-03 17:28:43 +00:00
Michael Trier 4dec075ee5 Flagged two versioning tests as failing on MSSQL. The flush occurs even though
there should be a concurrency issue.

I cheated and marked these as FIXME. With this commit all MSSQL tests pass
now. The work of correcting the ``fails_on`` tests begins.
2009-01-03 05:32:11 +00:00
Michael Trier 6b6848f124 sqlite tests run fine locally but the buildbot seems to have an issue. Perhaps this will work. 2009-01-03 04:57:31 +00:00
Michael Trier c6558eecb5 Some of the ordering fixes messed up MySQL. This should work better. Better testing next time. 2009-01-03 04:57:29 +00:00
Michael Trier 3d487b64eb Modified DefaultTest in order to get passage on mssql and still test the right stuff. 2009-01-03 03:59:54 +00:00
Michael Trier dd73bf16fd Excluded another failing test from the mssql dialect.
MSSQL doesn't allow ON UPDATE for self-referential keys. The tree of cascading
referential actions must only have one path to a particular table on the
cascading referential actions tree.
2009-01-03 03:59:49 +00:00
Mike Bayer 9fe69cb503 - Fixed some deep "column correspondence" issues which could
impact a Query made against a selectable containing
multiple versions of the same table, as well as
unions and similar which contained the same table columns
in different column positions at different levels.
[ticket:1268]
2009-01-03 02:42:34 +00:00
Michael Trier 0bd31484ea A couple of ordering fixes for the tests. 2009-01-03 02:32:10 +00:00
Michael Trier 6ea3521b45 sqlite reflection now stores the actual DefaultClause value for the column. 2009-01-02 22:40:45 +00:00
Mike Bayer 5bc1f17cb5 - mysql, postgres: "%" signs in text() constructs are automatically escaped to "%%".
Because of the backwards incompatible nature of this change,
a warning is emitted if '%%' is detected in the string.  [ticket:1267]
2009-01-02 21:24:17 +00:00
Michael Trier 2f2d84fbb1 Swap out text_as_varchar on the mssql dialect for the Types tests. 2009-01-02 20:33:14 +00:00
Michael Trier 54598789ee Marked a couple of unicode schema tests as failing on mssql. 2009-01-02 20:33:11 +00:00
Mike Bayer e878a96e6e found some more _Function->Function 2009-01-02 20:00:31 +00:00
Mike Bayer 023cc62bfc - sqlalchemy.sql.expression.Function is now a public
class.  It can be subclassed to provide user-defined
SQL functions in an imperative style, including
with pre-established behaviors.  The postgis.py
example illustrates one usage of this.
2009-01-02 19:45:05 +00:00
Michael Trier eb1a7c1bdf Marked mssql test as failing since it cannot update identity columns. 2009-01-02 18:25:08 +00:00
Michael Trier de97a18bb7 Mapped char_length to the LEN() function for mssql. 2009-01-02 18:25:04 +00:00
Michael Trier a69db2ae63 Corrected a UOW DefaultTest for mssql because it requires the identity column setup. 2009-01-02 18:25:00 +00:00
Michael Trier f793a88403 Added ability to use subselects within INSERTS on mssql. 2009-01-02 18:24:57 +00:00
Michael Trier 9c83fafc27 Specialized trigger tests to accomodate mssql syntax. 2009-01-02 18:24:52 +00:00
Michael Trier a7fe20bfda Added note for mssql about using snapshot isolation in order to get multiple
connection session tests to pass.
2009-01-02 18:24:49 +00:00
Michael Trier 0f842d28a1 Turned off the implicit transaction behavior of MSSQL.
This corrects the savepoint tests.
2009-01-02 18:24:47 +00:00
Mike Bayer 50dfbc7e79 - Custom comparator classes used in conjunction with
column_property(), relation() etc. can define
new comparison methods on the Comparator, which will
become available via __getattr__() on the
InstrumentedAttribute.   In the case of synonym()
or comparable_property(), attributes are resolved first
on the user-defined descriptor, then on the user-defined
comparator.
2009-01-02 18:22:50 +00:00
Michael Trier a52a0c43c3 Modified UOW so that a Row Switch scenario will not attempt to update the Primary Key. 2009-01-02 04:54:45 +00:00
Michael Trier 50e077ee52 Cleanup of r5556. Makes the description_encoding less public since this is a
workaround for the pyodbc dbapi.
2009-01-02 03:29:33 +00:00
Jonathan Ellis 3373994cfd emacs 2008-12-31 14:25:53 +00:00
Mike Bayer 7391c7bd4d yes ive been watching the IRC channel. restored setup_instance() to ClassManager and added coverage for mapper's usage of it. 2008-12-31 05:28:53 +00:00
Mike Bayer 96c76ec79e - added an extremely basic illustration of a PostGIS
integration to the examples folder.
2008-12-30 20:38:32 +00:00
Michael Trier f62a78242d Modifications to the mssql dialect in order to to pass through unicode in the pyodbc dialect. 2008-12-30 06:39:37 +00:00
Michael Trier dfd80ba089 Added a new description_encoding attribute on the dialect.
This is used for encoding the column name when processing the metadata. This
usually defaults to utf-8.
2008-12-30 06:39:33 +00:00
Michael Trier 4e8a817ac2 A few 2.3 cleanup items. 2008-12-30 06:24:59 +00:00
Michael Trier df267fcc77 Added in MSGenericBinary to the mssql dialect tests. 2008-12-29 21:38:04 +00:00
Mike Bayer f9adad3acc - added another usage recipe for contains_eager()
- some typos
2008-12-29 20:25:11 +00:00
Mike Bayer 8598780e8d - Added OracleNVarchar type, produces NVARCHAR2, and also
subclasses Unicode so that convert_unicode=True by default.
      NVARCHAR2 reflects into this type automatically so
      these columns pass unicode on a reflected table with no explicit
      convert_unicode=True flags.  [ticket:1233]
2008-12-28 22:32:04 +00:00
Mike Bayer bd23baf4ac - Can pass mapped attributes and column objects as keys
to query.update({}).  [ticket:1262]

- Mapped attributes passed to the values() of an
expression level insert() or update() will use the
keys of the mapped columns, not that of the mapped
attribute.
2008-12-28 21:48:12 +00:00
Michael Trier 8669eda82d Added in a new MSGenericBinary type.
This maps to the Binary type so it can implement the specialized behavior of
treating length specified types as fixed-width Binary types and non-length
types as an unbound variable length Binary type.
2008-12-28 21:07:57 +00:00
Mike Bayer 7009653aa1 - RowProxy objects can be used in place of dictionary arguments
sent to connection.execute() and friends.  [ticket:935]
2008-12-28 20:58:38 +00:00
Mike Bayer d245397bd2 - Fixed shard_id argument on ShardedSession.execute().
[ticket:1072]
2008-12-28 19:54:58 +00:00
Michael Trier a848e2ac47 Corrected reflection issue in mssql where include_columns doesn't include the PK. 2008-12-28 17:38:26 +00:00
Michael Trier cae83f6d4f On MSSQL if a field is part of the primary_key then it should not allow NULLS. 2008-12-28 07:40:56 +00:00
Michael Trier defba2fc02 MSSQL refactoring of BINARY type and addition of MSVarBinary and MSImage.
- Added in new types: MSVarBinary and MSImage
- Modified MSBinary to now return BINARY instead of IMAGE. This is a
  backwards incompatible change. Closes #1249.
2008-12-28 01:46:44 +00:00
Mike Bayer 0465d0b880 - added a full exercising test for all of #946, #947, #948, #949 2008-12-27 19:35:12 +00:00
Mike Bayer 8220d9cdbe - Added a mutex for the initial pool creation when
using pool.manage(dbapi).  This prevents a minor
case of "dogpile" behavior which would otherwise
occur upon a heavy load startup.  [ticket:799]
2008-12-27 18:45:41 +00:00
Mike Bayer f80471e6f3 - Added ScopedSession.is_active accessor. [ticket:976] 2008-12-27 18:24:00 +00:00
Mike Bayer fbcaa34b9e - NullPool supports reconnect on failure behavior.
[ticket:1094]
2008-12-27 18:07:35 +00:00
Mike Bayer 83a756c541 - Reflected foreign keys will properly locate
their referenced column, even if the column
was given a "key" attribute different from
the reflected name.  This is achieved via a
new flag on ForeignKey/ForeignKeyConstraint
called "link_to_name", if True means the given
name is the referred-to column's name, not its
assigned key.
[ticket:650]
- removed column types from sqlite doc, we
aren't going to list out "implementation" types
since they aren't significant and are less present
in 0.6
- mysql will report on missing reflected foreign
key targets in the same way as other dialects
(we can improve that to be immediate within
reflecttable(), but it should be within
ForeignKeyConstraint()).
- postgres dialect can reflect table with
an include_columns list that doesn't include
one or more primary key columns
2008-12-26 05:28:38 +00:00
Mike Bayer 3ea2888b7f fix imports for index reflection unit test 2008-12-26 00:51:44 +00:00
Michael Trier 8a8a7ffc52 Fixed bugs in sqlalchemy documentation. Closes #1263. 2008-12-24 14:43:24 +00:00
Mike Bayer 2876c7e46f - Exceptions raised during compile_mappers() are now
preserved to provide "sticky behavior" - if a hasattr()
call on a pre-compiled mapped attribute triggers a failing
compile and suppresses the exception, subsequent compilation
is blocked and the exception will be reiterated on the
next compile() call.  This issue occurs frequently
when using declarative.
2008-12-24 04:47:06 +00:00
Mike Bayer a75af96bcc use new anonymize style for the public _anonymize as well 2008-12-23 19:16:01 +00:00
Michael Trier 88aad2a374 Added MSSQL support for introspecting the default schema name for the logged in user. Thanks Randall Smith. Fixes #1258. 2008-12-23 06:01:09 +00:00
Mike Bayer 10c62a94b0 silly negative ID numbers on linux... 2008-12-23 05:44:49 +00:00
Mike Bayer 864644bee4 - Added Index reflection support to Postgres, using a
great patch we long neglected, submitted by
Ken Kuhlman. [ticket:714]
2008-12-23 04:47:52 +00:00
Michael Trier 5b09d8179e Merge branch 'collation' 2008-12-23 04:08:13 +00:00
Mike Bayer 6cf4db7df3 - Columns can again contain percent signs within their
names. [ticket:1256]
2008-12-23 01:22:54 +00:00
Michael Trier 886ddcd12d Major refactoring of the MSSQL dialect. Thanks zzzeek.
Includes simplifying the IDENTITY handling and the exception handling. Also
includes a cleanup of the connection string handling for pyodbc to favor
the DSN syntax.
2008-12-22 20:20:55 +00:00
Mike Bayer 4bb8489073 also check for primaryjoin/secondaryjoin that equates to False, [ticket:1087] 2008-12-22 17:51:25 +00:00
Mike Bayer b563084d0b - CHANGES update
- added slightly more preemptive message for bad remote_side
2008-12-22 15:10:06 +00:00
Mike Bayer 8dc7bada91 - Fixed mysql bug in exception raise when FK columns not present
during reflection. [ticket:1241]
2008-12-21 18:30:55 +00:00
Mike Bayer f2774461d3 fix unittest import 2008-12-21 02:39:45 +00:00
Michael Trier 12307ecbcf Pulled callable into testlib because path fixup is not available at the point we need it. 2008-12-21 00:47:04 +00:00
Michael Trier 0dc8bce4fe Corrected ColumnsTest for mssql's new explicit nullability behavior. 2008-12-20 22:35:06 +00:00
Mike Bayer 9cccdfb0fd removed the "create_execution_context()" method from dialects and replaced
with a more succinct "dialect.execution_ctx_cls" member
2008-12-19 23:02:45 +00:00
Mike Bayer 16ef52392c more platform neutral way of getting at 'buffer' 2008-12-19 15:32:31 +00:00
Mike Bayer 1b950f13d6 missed an ordering on a set. attempting to nail down linux-specific buildbot errors 2008-12-19 02:07:55 +00:00
Mike Bayer 146ec3469a and try again 2008-12-19 02:03:12 +00:00
Mike Bayer 7c803dfadb 2.4 doesnt have hashlib.... 2008-12-19 02:01:42 +00:00
Mike Bayer 5ddce0ea00 *most* py3k warnings are resolved, with the exception of the various __setslice__ related warnings
I don't really know how to get rid of
2008-12-18 18:46:27 +00:00
Mike Bayer d76dc73f33 merge the test/ directory from -r5438:5439 of py3k_warnings branch. this gives
us a 2.5-frozen copy of unittest so we're insulated from unittest changes.
2008-12-18 18:11:12 +00:00
Mike Bayer be5d326343 merged -r5299:5438 of py3k warnings branch. this fixes some sqlite py2.6 testing issues,
and also addresses a significant chunk of py3k deprecations.  It's mainly
expicit __hash__ methods.  Additionally, most usage of sets/dicts to store columns uses
util-based placeholder names.
2008-12-18 17:57:15 +00:00
Jason Kirtland 98d7d70674 dynamic_loader() accepts query_class= to mix in user Query subclasses. 2008-12-18 17:06:01 +00:00
Jason Kirtland 3c506294e3 Association proxies no longer cloak themselves at the class level. 2008-12-18 16:55:47 +00:00
Mike Bayer b333789336 - Query() can be passed a "composite" attribute
as a column expression and it will be expanded.
Somewhat related to [ticket:1253].
- Query() is a little more robust when passed
various column expressions such as strings,
clauselists, text() constructs (which may mean
it just raises an error more nicely).
- select() can accept a ClauseList as a column
in the same way as a Table or other selectable
and the interior expressions will be used as
column elements. [ticket:1253]
- removed erroneous FooTest from test/orm/query

-This line, and those below, will be ignored--

M    test/orm/query.py
M    test/orm/mapper.py
M    test/sql/select.py
M    lib/sqlalchemy/orm/query.py
M    lib/sqlalchemy/sql/expression.py
M    CHANGES
2008-12-18 16:50:49 +00:00
Mike Bayer 7933395055 document ConnectionProxy 2008-12-18 00:12:12 +00:00
Mike Bayer 6a99f29313 - _execute_clauseelement() goes back to being
a private method.  Subclassing Connection
is not needed now that ConnectionProxy
is available.
- tightened the interface for the various _execute_XXX()
methods to reduce ambiguity
- __distill_params() no longer creates artificial [{}] entry,
blank dict is no longer passed through to do_execute()
in any case unless explicitly sent from the outside
as in connection.execute("somestring"), {})
- fixed a few old sql.query tests which were doing that
- removed needless do_execute() from mysql dialect
- fixed charset param not properly being sent to
_compat_fetchone() in mysql
2008-12-17 23:09:51 +00:00
Mike Bayer 7b7530de19 - sqlite types
- fixed targeting for sqlalchemy.types
2008-12-17 20:53:43 +00:00
Mike Bayer 6788024721 - Fixed bug where many-to-many relation() with
viewonly=True would not correctly reference the
link between secondary->remote.
2008-12-17 20:39:18 +00:00
Mike Bayer 172781e678 - added sphinx handler to allow __init__ methods through
- sqlite module documentation
- some corrections to pool docs
- the example in URL.translate_connect_args() never made any sense anyway so removed it
2008-12-17 20:12:07 +00:00
Gaëtan de Menten 163c4819e0 polymorphic_fetch is deprecated. Mark it so in the documentation. 2008-12-17 14:53:18 +00:00
Mike Bayer 4a662d5966 ok we need find_packages. fine. 2008-12-15 21:58:36 +00:00
Mike Bayer d91219520f corrections 2008-12-15 21:25:17 +00:00
Mike Bayer 4e2d54fdd5 removed dependencies on setuptools. distutils will be used if setuptools is not
present.
2008-12-15 21:23:55 +00:00
Michael Trier c712af4d4c Corrected output on docs and a missing {stop} that prevented python results from displaying in the docs. 2008-12-12 21:59:33 +00:00
Michael Trier f9b8641269 Support for three levels of column nullability: NULL, NOT NULL, and the database's configured default.
The default Column configuration (nullable=True) will now generate NULL in the DDL. Previously no specification was emitted and the database default would take effect (usually NULL, but not always).  To explicitly request the database default, configure columns with nullable=None and no specification will be emitted in DDL. Fixes #1243.
2008-12-12 04:49:24 +00:00
Michael Trier 1d90146210 Modified fails_on testing decorator to take a reason for the failure.
This should assist with helping to document the reasons for testing failures.
Currently unspecified failures are defaulted to 'FIXME: unknown'.
2008-12-12 03:41:05 +00:00
Michael Trier aaac4520d3 Corrected and verified a few more mssql tests. 2008-12-12 03:40:57 +00:00
Michael Trier 7247ca12cf Broke out a specific values test and indicated that it fails on mssql due to duplicate columns in the order by clause. 2008-12-12 01:40:05 +00:00
Mike Bayer b22edf1d8a - turn __visit_name__ into an explicit member.
[ticket:1244]
2008-12-11 23:28:01 +00:00
Jason Kirtland 8d3fab1250 Index entries for thread safety. 2008-12-11 22:09:12 +00:00
Michael Trier f334da1f6b And now for the CHANGES. 2008-12-11 21:55:22 +00:00
Michael Trier 887c403f76 Corrected problem with bindparams not working properly with Query.delete and Query.update. Thanks zzzeek. Fixes #1242. 2008-12-11 21:52:11 +00:00
Michael Trier 1e4eeb8098 We don't need two of these. 2008-12-11 19:27:38 +00:00
Michael Trier 8ffbc9a846 Access doesn't support savepoints or two-phase commit. 2008-12-11 19:24:24 +00:00
Michael Trier 052d7f3643 Implemented experimental savepoint support in mssql. There are still some failing savepoint related tests. 2008-12-11 19:24:22 +00:00
Mike Bayer 5b0c456abd fix circular import 2008-12-11 18:43:05 +00:00
Mike Bayer e98b936129 - Connection.invalidate() checks for closed status
to avoid attribute errors. [ticket:1246]
2008-12-11 17:39:01 +00:00
Mike Bayer f527d3b9af - PickleType now favors == comparison by default,
if the incoming object (such as a dict) implements
__eq__().  If the object does not implement
__eq__() and mutable=True, a deprecation warning
is raised.
2008-12-11 17:27:33 +00:00
Mike Bayer a44f9d1bfd - fixed string-based "remote_side", "order_by" and
others not propagating correctly when used in
backref().
2008-12-11 15:34:45 +00:00
Mike Bayer 609d8e8bc3 - VERSION moves just as a string in __version__
- added modified sphinx.sty with plain Verbatim section
- link to pdf doc in site
2008-12-10 21:27:21 +00:00
Mike Bayer 4d698a28a2 - first() works as expected with Query.from_statement(). 2008-12-10 20:28:54 +00:00
Mike Bayer a2f90fd003 - reworked the "SQL assertion" code to something more flexible and based off of ConnectionProxy. upcoming changes to dependency.py
will make use of the enhanced flexibility.
2008-12-10 02:16:52 +00:00
Mike Bayer 41f5f3465a dont use names to find Annotated subclasses 2008-12-09 21:52:08 +00:00
Mike Bayer 70f55bd2cd - restored the previous API Reference structure
- bumped latex TOC structure, the PDF looks great
- but we need to fix the translate_connect_args docstring bug to really have PDF
2008-12-08 21:32:29 +00:00
Mike Bayer 3e2d6a9a18 fix typos 2008-12-08 20:49:12 +00:00
Mike Bayer 082d5db64f - removed redundant declarative docs
- cleanup of metadata/foreignkey docs
2008-12-08 20:21:02 +00:00
Gaëtan de Menten 4c4568eeb8 further fix that docstring 2008-12-08 10:43:57 +00:00
Gaëtan de Menten 6c7b506f0e fixed invalid docstring example 2008-12-08 10:41:36 +00:00
Mike Bayer dd91a165cc - restored the main search form
- fixed search highlighting
- the url docstring works again from a ReST perspective, still not PDF
2008-12-08 00:20:20 +00:00
Mike Bayer 480436ff7c - moved index.rst around to have the API docs right there, no "Main Documentation" chapter which is fairly needless. this all allows PDF to have a decent TOC on the side with only two levels (can we change that ?)
- added LatexFormatter.
- PDF wont work until issue with the docstirng in url.py/URL.translate_connect_args is fixed.
2008-12-07 23:58:02 +00:00
Mike Bayer 058c2895be worked schema into sections 2008-12-07 21:10:27 +00:00
Mike Bayer 9bab01d37b - convert __init__ and :members: to be compatible with autoclass_content='both' 2008-12-07 20:13:26 +00:00
Gaëtan de Menten 0dbbd6fe66 fix typos 2008-12-07 14:32:37 +00:00
Mike Bayer b11ae3a63a documented onupdate, partially documented server_onupdate 2008-12-07 06:50:47 +00:00
Mike Bayer ee7fcf3110 - re-documented Table and Column constructors, fixed case sensitivity description [ticket:1231]
- turned on autoclass_content="both".  Need to specify __init__ docstring with a newline after the """.
- other docs
2008-12-07 06:30:00 +00:00
Jason Kirtland bdf0117578 Adjusted basis for refs. 2008-12-07 06:26:36 +00:00
Mike Bayer 943abbce79 - removed creepy exec call for now
- removed unnecessary isinstance() from class_mapper()
- removed unnecessary and py3k incompatible "dictionary sort" from association table delete
2008-12-06 23:47:21 +00:00
Mike Bayer ea9db10daf need to use absolutes for these, otherwise its dictionary ordering roulette 2008-12-06 18:40:07 +00:00
Mike Bayer 994ab27aa3 - postgres docstring
- insert/update/delete are documented generatively
- values({}) is no longer deprecated, thus enabling
unicode/Columns as keys
2008-12-06 18:27:04 +00:00
Jason Kirtland de4ed96ec0 Enabled sphinx doctests. 2008-12-06 17:47:20 +00:00
Mike Bayer 65390f035a remove old files 2008-12-06 17:00:17 +00:00
Mike Bayer 1c329624a5 - merged -r5338:5429 of sphinx branch.
- Documentation has been converted to Sphinx.
In particular, the generated API documentation
has been constructed into a full blown
"API Reference" section which organizes
editorial documentation combined with
generated docstrings.   Cross linking between
sections and API docs are vastly improved,
a javascript-powered search feature is
provided, and a full index of all
classes, functions and members is provided.
2008-12-06 16:59:48 +00:00
Mike Bayer 6eca02a31f - union() and union_all() will not whack
any order_by() that has been applied to the
select()s inside.  If you union() a
select() with order_by() (presumably to support
LIMIT/OFFSET), you should also call self_group()
on it to apply parenthesis.
2008-12-06 00:14:50 +00:00
Mike Bayer ecc6c1da2a - Adjusted the format of create_xid() to repair
two-phase commit.   We now have field reports
of Oracle two-phase commit working properly
with this change.
2008-12-05 14:46:27 +00:00
Mike Bayer fff9409a33 - Query.with_polymorphic() now accepts a third
argument "discriminator" which will replace
the value of mapper.polymorphic_on for that
query.  Mappers themselves no longer require
polymorphic_on to be set, even if the mapper
has a polymorphic_identity.   When not set,
the mapper will load non-polymorphically
by default. Together, these two features allow
a non-polymorphic concrete inheritance setup
to use polymorphic loading on a per-query basis,
since concrete setups are prone to many
issues when used polymorphically in all cases.
2008-12-03 21:27:04 +00:00
Mike Bayer 0410eae36b - Two fixes to help prevent out-of-band columns from
being rendered in polymorphic_union inheritance
scenarios (which then causes extra tables to be
rendered in the FROM clause causing cartesian
products):
- improvements to "column adaption" for
  a->b->c inheritance situations to better
  locate columns that are related to one
  another via multiple levels of indirection,
  rather than rendering the non-adapted
  column.
- the "polymorphic discriminator" column is
  only rendered for the actual mapper being
  queried against. The column won't be
  "pulled in" from a subclass or superclass
  mapper since it's not needed.
2008-12-03 17:28:36 +00:00
Mike Bayer 851a14aa1a - Using the same ForeignKey object repeatedly
raises an error instead of silently failing
later. [ticket:1238]
2008-12-03 14:09:34 +00:00
Mike Bayer 3c1aa033e6 - Fixed bug introduced in 0.5rc4 involving eager
loading not functioning for properties which were
added to a mapper post-compile using
add_property() or equivalent.
2008-12-03 06:23:55 +00:00
Michael Trier 55a6edef96 Modified the docstring for Session.add() with lots of help. 2008-12-03 04:52:55 +00:00
Ants Aasma 20b202e220 made Session.merge cascades not trigger autoflush 2008-12-02 19:14:15 +00:00
Mike Bayer c136b7a6e9 - Improved mapper() check for non-class classes.
[ticket:1236]
2008-12-01 22:09:15 +00:00
Mike Bayer 8617b3fe34 propagate docstrings for column/fk collections 2008-12-01 05:04:55 +00:00
Mike Bayer 181424b743 - fixed "double iter()" call causing bus errors
in shard API, removed errant result.close()
left over from the 0.4 version. [ticket:1099]
[ticket:1228]
2008-11-27 15:59:34 +00:00
Michael Trier fc779a8355 Refactored the entity setup code in Query so that it is not duplicated in several places. 2008-11-26 19:44:04 +00:00
Michael Trier 7ea7e0422d A few more order_by statements added to the tests in order to please msql when using offsets. 2008-11-26 19:43:59 +00:00
Mike Bayer 332f5ee266 - Duplicate items in a list-based collection will
be maintained when issuing INSERTs to
a "secondary" table in a many-to-many relation.
Assuming the m2m table has a unique or primary key
constraint on it, this will raise the expected
constraint violation instead of silently
dropping the duplicate entries. Note that the
old behavior remains for a one-to-many relation
since collection entries in that case
don't result in INSERT statements and SQLA doesn't
manually police collections. [ticket:1232]
2008-11-25 04:43:04 +00:00
Mike Bayer e3502f7f9d deprecated CompositeProperty 'comparator' which is now
named 'comparator_factory'.
2008-11-24 01:44:08 +00:00
Mike Bayer 6c8af5108e one more select_table... 2008-11-24 01:21:08 +00:00
Mike Bayer 75e8350e4d - comparator_factory is accepted by all MapperProperty constructors. [ticket:1149]
- added other unit tests as per [ticket:1149]
- rewrote most of the "joined table inheritance" documentation section, removed badly out of
date "polymorphic_fetch" and "select_table" arguments.
- "select_table" raises a deprecation warning.  converted unit tests to not use it.
- removed all references to "ORDER BY table.oid" from mapping docs.
- renamed PropertyLoader to RelationProperty.  Old symbol remains.
- renamed ColumnProperty.ColumnComparator to ColumnProperty.Comparator.  Old symbol remains.
2008-11-24 01:14:32 +00:00
Mike Bayer e7dd4efb3e - Extra checks added to ensure explicit
primaryjoin/secondaryjoin are ClauseElement
instances, to prevent more confusing errors later
on.
2008-11-22 20:37:16 +00:00
Mike Bayer 2c69cdb350 - Tickets [ticket:1200].
- Added note about create_session() defaults.

- Added section about metadata.reflect().

- Updated `TypeDecorator` section.

- Rewrote the "threadlocal" strategy section of
the docs due to recent confusion over this
feature.

- ordered the init arguments in the docs for sessionmaker().

- other edits
2008-11-22 19:22:42 +00:00
Mike Bayer f03e4ca595 prevent extra nested li items from becoming tiny 2008-11-22 17:46:03 +00:00
Mike Bayer 31450d75e5 - Fixed the import weirdness in sqlalchemy.sql
to not export __names__ [ticket:1215].
2008-11-22 16:09:20 +00:00
Mike Bayer c08192ae5b - Comparison of many-to-one relation to NULL is
properly converted to IS NOT NULL based on not_().
2008-11-21 03:49:36 +00:00
Mike Bayer 7c0ba8028c - Added NotImplementedError for params() method
on Insert/Update/Delete constructs.  These items
currently don't support this functionality, which
also would be a little misleading compared to
values().
2008-11-21 01:21:00 +00:00
Mike Bayer 1b2d7b1096 - the "passive" flag on session.is_modified()
is correctly propagated to the attribute manager.
2008-11-18 16:24:00 +00:00
Mike Bayer c2cbdb2ce0 r5281 knocked down callcounts in 2.5.. 2008-11-17 02:02:42 +00:00
Mike Bayer e1268d4f57 - Query.select_from(), from_statement() ensure
that the given argument is a FromClause,
or Text/Select/Union, respectively.

- Query.add_column() can accept FromClause objects
in the same manner as session.query() can.
2008-11-16 19:33:26 +00:00
Mike Bayer 1ff2b78268 - bump, this may become 0.5.0
- Calling alias.execute() in conjunction with
server_side_cursors won't raise AttributeError.
2008-11-14 22:11:05 +00:00
Mike Bayer 1214e74a38 notes on tuning 2008-11-14 21:36:15 +00:00
Mike Bayer 3e486caaab - switched session.save() to session.add() throughout declarative test
- Fixed PendingDeprecationWarning involving order_by
parameter on relation(). [ticket:1226]
- Unit tests still filter pending deprecation warnings but have a commented-out
line to temporarily disable this behavior.  Tests need to be fully converted
before we can turn this on.
2008-11-14 18:57:24 +00:00
Michael Trier b2a7892b10 Pulled out values test that uses boolean evaluation in the SELECT in order to appropriately flag it as not supported on mssql. I sure hope I didn't jack things up for other dialects. Cleaned up a comment and removed some commented pdb statements. 2008-11-14 03:57:07 +00:00
Michael Trier 61178c5d6d Fixed a problem with the casting of a zero length type to a varchar. It now correctly adjusts the CAST accordingly. 2008-11-14 03:57:04 +00:00
Michael Trier 1227a7674f Fixed up a lot of missing order_by statements in the tests when using offset. A lot of dialects don't really require order_by although you'll get unpredictable results. mssql does require order_by with an offset, so this fixes problems with that dialect. 2008-11-14 03:57:00 +00:00
Michael Trier 43ecc7a581 The str(query) output is also correct on the mssql dialect. 2008-11-14 03:56:55 +00:00
Mike Bayer f3de4d545f - Rearranged the load_dialect_impl() method in
`TypeDecorator` such that it will take effect
even if the user-defined `TypeDecorator` uses
another `TypeDecorator` as its impl.
2008-11-13 20:38:56 +00:00
Mike Bayer 0148adec30 - Can now use a custom "inherit_condition" in
__mapper_args__ when using declarative.
2008-11-12 15:43:17 +00:00
Michael Trier 260c201f65 Corrected mssql schema named subqueries from not properly aliasing the columns. Fixes #973. 2008-11-12 05:36:45 +00:00
Michael Trier 097f76b465 Doing my part-time editorial duties. Normalized session references and fixed lots of small spelling and grammar issues. 2008-11-12 03:05:13 +00:00
Mike Bayer 61db44d958 remove errant pdb.set_trace() 2008-11-11 02:04:56 +00:00
Mike Bayer da0a8b913b - Adjustments to the enhanced garbage collection on
InstanceState to better guard against errors due
to lost state.
2008-11-11 01:52:42 +00:00
Jason Kirtland d403d8b865 Quashed import sets deprecation warning on 2.6.. not wild about this but it seems like it will be ok. [ticket:1209] 2008-11-10 22:56:22 +00:00
Mike Bayer ea12a628e4 - converted some more attributes to @memoized_property in expressions
- flattened an unnecessary KeyError in identity.py
- memoized the default list of mapper properties queried in MapperEntity.setup_context
2008-11-10 20:22:18 +00:00
Mike Bayer eaa359f177 - Restored "active rowcount" fetch before ResultProxy
autocloses the cursor.  This was removed in 0.5rc3.
2008-11-10 16:42:35 +00:00
Mike Bayer 2c5f3e8397 - Restored NotImplementedError on Cls.relation.in_()
[ticket:1140] [ticket:1221]
2008-11-10 16:18:57 +00:00
Michael Trier 306bee4ecd Handle the mssql port properly. If we're using the SQL Server driver then use the correct host,port syntax, otherwise use the Port= parameter in the connection string. Fixes #1192. 2008-11-10 04:29:53 +00:00
Michael Trier 6a9b2cb683 Flagged another transaction test as causing mssql to hang. Need to look into these. 2008-11-10 01:11:46 +00:00
Michael Trier d360fd7fe3 Corrected issue with decimal e notation that broke regular decimal tests for mssql. 2008-11-10 01:11:43 +00:00
Michael Trier a8cd5cfd39 If there's a zero offset with mssql just ignore it. 2008-11-10 01:11:40 +00:00
Michael Trier 69cc4e3232 Corrected problem in access dialect that was still referring to the old column.foreign_key property. 2008-11-10 01:11:37 +00:00
Mike Bayer e31c5326c4 flattened _get_from_objects() into a descriptor/class-bound attribute 2008-11-09 21:34:59 +00:00
Mike Bayer 0cff22720b - Removed the 'properties' attribute of the
Connection object, Connection.info should be used.
- Method consoliation in Connection, ExecutionContext
2008-11-09 19:32:25 +00:00
Mike Bayer 043379efa5 - Query.count() has been enhanced to do the "right
thing" in a wider variety of cases. It can now
count multiple-entity queries, as well as
column-based queries. Note that this means if you
say query(A, B).count() without any joining
criterion, it's going to count the cartesian
product of A*B. Any query which is against
column-based entities will automatically issue
"SELECT count(1) FROM (SELECT...)" so that the
real rowcount is returned, meaning a query such as
query(func.count(A.name)).count() will return a value of
one, since that query would return one row.
2008-11-09 16:06:05 +00:00
Michael Trier 3f8914b4b2 Corrected problems with Access dialect. Corrected issue with reflection due to missing Currency type. Functions didn't return the value. JOINS must be specified as LEFT OUTER JOIN or INNER JOIN. Fixes #1017. 2008-11-09 05:21:38 +00:00
Michael Trier 4cd99f5536 Global propigate -> propagate change to correct spelling. Additionally found a couple of insures that should be ensure. 2008-11-09 01:53:08 +00:00
Michael Trier a8c308f349 Corrected problems with reflection on mssql when dealing with schemas. Fixes #1217. 2008-11-09 01:27:25 +00:00
Mike Bayer d6806501f1 usage docstring for pool listener 2008-11-08 23:26:55 +00:00
Mike Bayer e91bc867f5 - Query.count() and Query.get() return a more informative
error message when executed against multiple entities.
[ticket:1220]
2008-11-08 21:18:11 +00:00
Mike Bayer 1901519fa7 removed setup_instance() from the public API
of ClassManager, and made it a private method on
_ClassInstrumentationAdapter.  ClassManager's approach
handles the default task with fewer function calls which chops off
a few hundred calls from the pertinent profile tests.
2008-11-08 21:00:15 +00:00
Michael Trier 17980ba83c Fixed E notation problem in mssql. Closes #1216. 2008-11-08 06:37:45 +00:00
Michael Trier 8924a0e4fe Corrected a lot of mssql limit / offset issues. Also ensured that mssql uses the IN / NOT IN syntax when using a binary expression with a subquery. 2008-11-08 04:43:35 +00:00
Mike Bayer cfca625e94 docstring updates 2008-11-07 22:36:21 +00:00
Mike Bayer f4db072815 docstring fix 2008-11-07 18:56:42 +00:00
Mike Bayer 0a48075161 - added serializer docs to plugins.txt
- CHANGES formatting
2008-11-07 18:43:39 +00:00
Mike Bayer 9b360dda29 - Fixed bug preventing declarative-bound "column" objects
from being used in column_mapped_collection().  [ticket:1174]
2008-11-07 18:20:53 +00:00
Mike Bayer e6141ef8ae formatting 2008-11-07 18:03:37 +00:00
Mike Bayer da59591a9c - zoomark adjustments
- changelog has separate category for 'features'
2008-11-07 17:45:19 +00:00
Mike Bayer 17b758faed avoid some often unnecessary method calls. i think we might have squeezed all we're going to squeeze out of compiler at this point. 2008-11-07 17:08:23 +00:00
Mike Bayer abf9bef1a9 the @memoized_property fairy pays a visit 2008-11-07 16:41:54 +00:00
Mike Bayer 8a04f99784 - Repaired the table.tometadata() method so that a passed-in
schema argument is propigated to ForeignKey constructs.
2008-11-07 16:19:24 +00:00
Mike Bayer c3352e5542 - Fixed bug in Query involving order_by() in conjunction with
multiple aliases of the same class (will add tests in
[ticket:1218])
- Added a new extension sqlalchemy.ext.serializer.  Provides
Serializer/Deserializer "classes" which mirror Pickle/Unpickle,
as well as dumps() and loads().  This serializer implements
an "external object" pickler which keeps key context-sensitive
objects, including engines, sessions, metadata, Tables/Columns,
and mappers, outside of the pickle stream, and can later
restore the pickle using any engine/metadata/session provider.
This is used not for pickling regular object instances, which are
pickleable without any special logic, but for pickling expression
objects and full Query objects, such that all mapper/engine/session
dependencies can be restored at unpickle time.
2008-11-06 23:07:47 +00:00
Martijn Faassen 84003a8d40 add two new hooks for bulk operations to SessionExtension:
* after_bulk_delete

* after_bulk_update
2008-11-06 06:12:11 +00:00
Mike Bayer 7576315169 - Fixed bug in composite types which prevented a primary-key
composite type from being mutated [ticket:1213].
2008-11-05 21:15:19 +00:00
Mike Bayer 9f894d2f26 - Dialects can now generate label names of adjustable length.
Pass in the argument "label_length=<value>" to create_engine()
to adjust how many characters max will be present in dynamically
generated column labels, i.e. "somecolumn AS somelabel".  Any
value less than 6 will result in a label of minimal size,
consiting of an underscore and a numeric counter.
The compiler uses the value of dialect.max_identifier_length
as a default. [ticket:1211]
- removed ANON_NAME regular expression, using string patterns now
- _generated_label() unicode subclass is used to indicate generated names
which are subject to truncation
2008-11-05 20:50:48 +00:00
Jason Kirtland 89b86f41bb Tiny fix to test setup logic. 2008-11-04 18:29:33 +00:00
Mike Bayer c38e5d043f - Simplified the check for ResultProxy "autoclose without results"
to be based solely on presence of cursor.description.
All the regexp-based guessing about statements returning rows
has been removed [ticket:1212].
2008-11-04 17:28:26 +00:00
Mike Bayer 3f1e5e213d - added 'EXPLAIN' to the list of 'returns rows', but this
issue will be addressed more fully by [ticket:1212].
2008-11-04 13:18:13 +00:00
Jason Kirtland 89a28cffa9 Added a label for pg. 2008-11-03 04:41:06 +00:00
Mike Bayer 47d2576365 - Fixed bug when using multiple query.join() with an aliased-bound
descriptor which would lose the left alias.
2008-11-03 03:37:44 +00:00
Mike Bayer a5dfbeedb9 - Improved the behavior of aliased() objects such that they more
accurately adapt the expressions generated, which helps
particularly with self-referential comparisons. [ticket:1171]

- Fixed bug involving primaryjoin/secondaryjoin conditions
constructed from class-bound attributes (as often occurs
when using declarative), which later would be inappropriately
aliased by Query, particularly with the various EXISTS
based comparators.
2008-11-03 02:52:30 +00:00
Mike Bayer 334d5118bb update call count 2008-11-03 01:47:30 +00:00
Jason Kirtland 27b48aa7e6 Added tests for Query.scalar(), .value() [ticket:1163] 2008-11-03 00:09:33 +00:00
Jason Kirtland 837f71eca5 Fixed assoc proxy examples [ticket:1191] 2008-11-02 22:50:12 +00:00
Mike Bayer 50719c0bb0 revert r5220 inadvertently committed to trunk 2008-11-02 22:11:40 +00:00
Mike Bayer ff2f799ba3 progress so far 2008-11-02 22:08:24 +00:00
Michael Trier ea2e7fd365 Corrected some ordering issues with tests. 2008-11-02 17:42:53 +00:00
Mike Bayer be811d23fa - mapper naming/organization cleanup
- gave into peer pressure and removed all __names
- inlined polymorphic_iterator()
- moved methods into categories based on configuration, inspection, persistence, row processing.
a more extreme change would be to make separate mixin classes for these or similar.
2008-11-02 17:10:37 +00:00
Mike Bayer 9562ab8398 pep8 stuff 2008-11-02 15:50:16 +00:00
Mike Bayer ed3e3f2571 - util.flatten_iterator() func doesn't interpret strings with
__iter__() methods as iterators, such as in pypy [ticket:1077].
2008-10-31 21:44:34 +00:00
Mike Bayer bba54e320d the recent change to garbage collection of InstanceState meant that
the deferred lambda: created by lazy_clause would get a state with
no dict.  creates strong reference to the object now.
2008-10-30 14:40:10 +00:00
Michael Trier c2a6ebb96e Added documentation for the MetaData.sorted_tables() method. 2008-10-29 17:43:06 +00:00
Michael Trier 7a0a7af923 Corrected method documentation for MetaData.drop_all(). 2008-10-29 17:31:14 +00:00
Jonathan Ellis 3882bb864b allow repr to leave stuff as unicode. I can't think of any reason for the old behavior except that I didn't understand unicode when I wrote it. Not that I claim to fully understand it now. fixes #1136 2008-10-29 00:48:33 +00:00
Jason Kirtland 554f223f6b Accept USING as a prefix or postfix modifer when reflecting keys. [ticket:1117] 2008-10-28 21:32:24 +00:00
Michael Trier 231839e037 Corrects an import error when using echo_uow. Fixes #1205. 2008-10-28 21:10:31 +00:00
Jonathan Ellis 199257d7fb fix #821 2008-10-28 20:25:25 +00:00
Mike Bayer 0719e6f648 - added some abstraction to the attributes.History object
- Repaired support for "passive-deletes" on a many-to-one
relation() with "delete" cascade. [ticket:1183]
2008-10-28 20:15:26 +00:00
Michael Trier 0fc32d7825 Updated UOWEventHandler so that it uses session.add() instead of session.save_or_update(). Fixes #1208. 2008-10-28 19:59:53 +00:00
Michael Trier aa6c4df395 Corrected typo in Types docs. 2008-10-28 18:09:22 +00:00
Michael Trier d4dcb2e217 Mysql no longer expects include_columns to be specified in lowercase. Fixes #1206. 2008-10-28 16:48:13 +00:00
Jason Kirtland c9591657dd Fixed mysql FK reflection for the edge case where a Table has expicitly provided a schema= that matches the connection's default schema. 2008-10-27 22:56:53 +00:00
Jonathan Ellis d56c1f1663 r/m wildcard imports. fixes #1195 2008-10-27 19:49:49 +00:00
Mike Bayer c36271e23b - InstanceState object now removes circular references to
itself upon disposal to keep it outside of cyclic garbage
collection.
2008-10-26 20:02:19 +00:00
Mike Bayer 76e8175971 - moved _FigureVisitName into visitiors.VisitorType, added Visitor base class to reduce dependencies
- implemented _generative decorator for select/update/insert/delete constructs
- other minutiae
2008-10-25 19:44:21 +00:00
Mike Bayer 25e5157785 call drop # 2 2008-10-25 18:24:13 +00:00
Mike Bayer 9160ce0d45 call drop 2008-10-25 18:23:46 +00:00
Mike Bayer e82eebb368 - When using Query.join() with an explicit clause for the
ON clause, the clause will be aliased in terms of the left
side of the join, allowing scenarios like query(Source).
from_self().join((Dest, Source.id==Dest.source_id)) to work
properly.
2008-10-25 18:04:59 +00:00
Mike Bayer af1bb6b955 small fix 2008-10-25 18:00:37 +00:00
Mike Bayer f7a00f30d5 a couple of refinements 2008-10-25 17:19:15 +00:00
Mike Bayer baa9006c28 remove erroneous comments 2008-10-24 19:43:29 +00:00
Mike Bayer eba763b258 two more cache examples 2008-10-24 19:41:25 +00:00
Mike Bayer ecf22b390b auto_convert_lobs=False honored by OracleBinary, OracleText types
[ticket:1178]
2008-10-24 17:09:58 +00:00
Mike Bayer 3bbf8037f8 - fixed some oracle unit tests in test/sql/
- wrote a docstring for oracle dialect, needs formatting perhaps
- made FIRST_ROWS optimization optional based on optimize_limits=True, [ticket:536]
2008-10-24 15:58:17 +00:00
Mike Bayer 4ba4964425 2.4 callcounts of course go up for no apparent reason 2008-10-23 02:39:52 +00:00
Mike Bayer 99cd1346fb - CompileTests run without the DBAPI being used
- added stack logic back to visit_compound(), pared down is_subquery
2008-10-23 02:35:08 +00:00
Mike Bayer 1ccdfb5172 call count pinata party 2008-10-23 02:22:57 +00:00
Michael Trier 3d0fe5bfe2 Demonstrate mssql url examples for the database engine documentation. Closes #1198. 2008-10-23 02:09:27 +00:00
Michael Trier c4da034f7e Included documentation about the defaults for create_session() and how they differ from sessionmaker(). Closes #1197. 2008-10-23 01:47:44 +00:00
Michael Trier f919df47c7 Corrected case in mssql where binary expression has bind parameters on both sides. 2008-10-23 01:47:40 +00:00
Mike Bayer c356219690 - Added more granularity to internal attribute access, such
that cascade and flush operations will not initialize
unloaded attributes and collections, leaving them intact for
a lazy-load later on.  Backref events still initialize
attrbutes and collections for pending instances.
[ticket:1202]
2008-10-22 16:09:19 +00:00
Mike Bayer ee7e964ad4 add lengths to cols 2008-10-21 21:12:03 +00:00
Mike Bayer 17adfc8adc - polymorphic_union() function respects the "key" of each
Column if they differ from the column's name.
2008-10-21 21:07:04 +00:00
Mike Bayer e17b7f4bc9 - added NoReferencedColumnError, common base class of NoReferenceError
- relation() won't hide unrelated ForeignKey errors inside of
the "please specify primaryjoin" message when determining
join condition.
2008-10-21 16:17:24 +00:00
Michael Trier e7b43dd33f Corrected missing declaration in the mssql dialect test. 2008-10-21 03:21:16 +00:00
Michael Trier 00cec7c088 Corrected the is_subquery() check based on recent changes. Excluded the test_in_filtering_advanced test for mssql. 2008-10-21 02:46:43 +00:00
Ants Aasma 11619ad8ee Slightly changed behavior of IN operator for comparing to empty collections. Now results in inequality comparison against self. More portable, but breaks with stored procedures that aren't pure functions. 2008-10-20 20:41:09 +00:00
Michael Trier 9dd05715de Corrected profiling expected call count down to 42 for the test_insert test. 2008-10-20 16:24:30 +00:00
Michael Trier c81c7ff3d5 Modifications to allow the backends to control the behavior of an empty insert. If supports_empty_insert is True then the backend specifically supports the 'insert into t1 () values ()' syntax. If supports_default_values is True then the backend supports the 'insert into t1 default values' syntax. If both are false then the backend has no support for empty inserts at all and an exception gets raised. Changes here are careful to not change current behavior except where the current behavior was failing to begin with. 2008-10-20 15:21:00 +00:00
Mike Bayer abcb5605f9 - Improved weakref identity map memory management to no longer
require mutexing, resurrects garbage collected instance
on a lazy basis for an InstanceState with pending changes.
2008-10-19 19:26:48 +00:00
Michael Trier 291077f364 Verified that Subqueries are not allowed in VALUES. mssql supports a SELECT syntax but only as the source of all inserts.
(cherry picked from commit 4516db6b322fb1feaa04915f09b8b4fabd6b9735)
2008-10-19 03:00:22 +00:00
Michael Trier dfd71d6ac8 Cleaned up the create_connect_args so that it makes no expectations about keys. Fixes 1193. Added server version info into mssql pyodbc dialect. 2008-10-19 01:18:15 +00:00
Mike Bayer 6ac91ccc8c tiny tiny speed improvements.... 2008-10-18 19:39:34 +00:00
Mike Bayer edec6707ec call count still goes to 131 for 2.4 despite the removal of ~12 lines from visit_select() 2008-10-18 18:25:21 +00:00
Mike Bayer a20222fc22 - 0.5.0rc3, doh
- The internal notion of an "OID" or "ROWID" column has been
removed.  It's basically not used by any dialect, and the
possibility of its usage with psycopg2's cursor.lastrowid
is basically gone now that INSERT..RETURNING is available.

- Removed "default_order_by()" method on all FromClause
objects.
- profile/compile/select test is 8 function calls over on buildbot 2.4 for some reason, will adjust after checking
the results of this commit
2008-10-18 18:14:06 +00:00
Mike Bayer 223bd3688d oracle doesnt seem to like CLOB in unions.... 2008-10-18 17:45:04 +00:00
Mike Bayer 1127b10b27 - "not equals" comparisons of simple many-to-one relation
to an instance will not drop into an EXISTS clause
and will compare foreign key columns instead.

- removed not-really-working use cases of comparing
a collection to an iterable.  Use contains() to test
for collection membership.

- Further simplified SELECT compilation and its relationship
to result row processing.

- Direct execution of a union() construct will properly set up
result-row processing. [ticket:1194]
2008-10-18 17:34:52 +00:00
Jason Kirtland 654794cdcf Moved r5164's @lazy_property to @memoized_property, updated existing @memoize consumers. 2008-10-17 20:04:11 +00:00
Jason Kirtland 6481d24642 Cache polymorphic_iterator in UOWTask; substantial savings for polymorphism-heavy workloads. 2008-10-17 19:19:05 +00:00
Michael Trier fc35f5b6e0 Unless I'm missing something mssql doesn't support and / or within column selects. Even using the case when syntax it's not possible to test truth in this manner. 2008-10-16 17:14:30 +00:00
Mike Bayer 4ca89fd3c4 - String's (and Unicode's, UnicodeText's, etc.) convert_unicode
logic disabled in the sqlite dialect, to adjust for pysqlite
2.5.0's new requirement that only Python unicode objects are
accepted;
http://itsystementwicklung.de/pipermail/list-pysqlite/2008-March/000018.html
2008-10-12 14:39:20 +00:00
Mike Bayer 40b1aa8f24 reduce cruft related to serializable loaders 2008-10-12 05:13:46 +00:00
Mike Bayer 3bf1ddfb91 a much easier way to ArgSingleton 2008-10-12 04:25:53 +00:00
Michael Trier 41e1f5526c Removed the visit_function stuff in mssql dialect. Added some tests for the function overrides. Fixed up the test_select in the sql/defaults.py tests which was a mess. 2008-10-11 16:14:20 +00:00
Michael Trier b3c39decc1 Correction of mssql schema reflection in reflectable. Still a problem since the assumed default is dbo, whereas it could be modified by the connection. Allows SchemaTest.test_select to pass now. 2008-10-11 16:14:07 +00:00
Michael Trier 188a990e22 indicated that test_empty_insert fails on mssql since pyodbc returns a -1 always for the result.rowcount. 2008-10-09 23:28:45 +00:00
Michael Trier e0742ada97 Corrected docstring for Query.one. Fixes #1190. 2008-10-08 16:05:46 +00:00
Mike Bayer ff3df488f5 - Oracle will detect string-based statements which contain
comments at the front before a SELECT as SELECT statements.
      [ticket:1187]
2008-10-07 16:58:53 +00:00
Michael Trier 86c3992318 Added in sqlite3 DBAPI to the SQLite dbengine docs. This along with a wiki edit on Database Features should close #1145. 2008-10-05 13:28:56 +00:00
Michael Trier 9e3d161d0d Documented synonym_for and comparable_using in the main docstring for declarative. Fixes #1144. 2008-10-05 03:51:48 +00:00
Michael Trier f0a40280fd Corrected docs for declarative synonym incorrectly referring to instruments instead of descriptor. 2008-10-05 03:30:58 +00:00
Mike Bayer 4597c678ac fixed test for #1175 2008-10-05 00:24:43 +00:00
Mike Bayer 8f4999a70b - fix outerjoin, add order_by for DB variance 2008-10-04 23:52:14 +00:00
Michael Trier cba429c0bc Change in #1165 tests to prevent MySQL from choking on a varchar without a length. 2008-10-04 23:46:02 +00:00
Michael Trier c9afdb5072 Corrects issue where engine.execute raised exception when given empty list. Fixes #1175. 2008-10-04 23:19:05 +00:00
Mike Bayer a9a4da62cf - using contains_eager() against an alias combined with an overall query alias repaired - the
contains_eager adapter wraps the query adapter, not vice versa.  Test coverage added.
- contains_eager() will now add columns into the "primary" column collection within Query._compile_context(), instead
of the "secondary" collection.  This allows those columns to get wrapped within the subquery generated
by limit/offset in conjunction with an ORM-generated eager join.
Eager strategy also picks up on context.adapter in this case to deliver the columns during result load.
contains_eager() is now compatible with the subquery generated by a regular eager load
with limit/offset. [ticket:1180]
2008-10-04 22:39:19 +00:00
Mike Bayer f487169777 - added a few more assertions for [ticket:1165]
- removed non-2.5 partial.keywords, partial.name, etc., not sure what those are getting us here
2008-10-04 16:09:16 +00:00
Michael Trier dc5f360cd3 Didnt think about <2.5. When will I learn. 2008-10-04 02:57:19 +00:00
Michael Trier 56e88ed7c3 Allowed column types to be callables. Fixes #1165. 2008-10-04 01:49:14 +00:00
Mike Bayer 7005a9a42f - Adjustment to Session's post-flush accounting of newly
"clean" objects to better protect against operating on
objects as they're asynchronously gc'ed. [ticket:1182]
2008-10-03 03:39:52 +00:00
Mike Bayer abe17984fb - identity_map._mutable_attrs is a plain dict since we manage weakref removal explicitly
- call list() around iteration of _mutable_attrs to guard against async gc.collect() while check_modified() is running
2008-10-02 02:02:51 +00:00
Mike Bayer 95bedd0bad the @property / __slots__ fairy pays a visit 2008-10-01 15:23:14 +00:00
Ants Aasma 3eefe60bcf Issue a better error message when someone decides to meddle with the active transaction from within a context manager. 2008-09-30 12:04:23 +00:00
Ants Aasma 4c53406cab Fixed session.transaction.commit() on a autocommit=False session not starting a new transaction.
Moved starting a new transaction in case of previous closing into SessionTransaction.
2008-09-30 09:24:27 +00:00
Mike Bayer 6122c3ad48 - session.execute() will execute a Sequence object passed to
it (regression from 0.4).
- Removed the "raiseerror" keyword argument from object_mapper()
  and class_mapper().  These functions raise in all cases
  if the given class/instance is not mapped.
- Refined ExtensionCarrier to be itself a dict, removed
'methods' accessor
- moved identity_key tests to test/orm/utils.py
- some docstrings
2008-09-28 19:10:22 +00:00
Mike Bayer 5f75197e86 - Fixed up slices on Query (i.e. query[x:y]) to work properly
for zero length slices, slices with None on either end.
[ticket:1177]
2008-09-28 00:39:06 +00:00
Jason Kirtland 15f1a5df20 Tidy. 2008-09-28 00:04:09 +00:00
Mike Bayer 17309da8e6 fixed custom TypeEngine example 2008-09-27 21:15:51 +00:00
Jason Kirtland bcd7c81a46 Fixed mysql TEMPORARY table reflection. 2008-09-27 18:26:53 +00:00
Jason Kirtland 8e5312975b - Fixed shared state bug interfering with ScopedSession.mapper's
ability to apply default __init__ implementations on object
  subclasses.
2008-09-27 18:11:40 +00:00
Jason Kirtland 0d9fc31fa1 re-enabled memusage and connect tests. 2008-09-27 01:41:45 +00:00
Jason Kirtland 29a6af6d46 Added query_cls= override to scoped_session's query_property 2008-09-27 01:37:26 +00:00
Mike Bayer a32c1a1e25 - fixed RLock-related bug in mapper which could deadlock
upon reentrant mapper compile() calls, something that
occurs when using declarative constructs inside of
ForeignKey objects.
2008-09-25 15:59:37 +00:00
Mike Bayer 1cc094a004 random cleanup 2008-09-22 22:15:24 +00:00
Mike Bayer a964a42000 genericized the relationship between bind_processor() and _bind_processor() a little more 2008-09-19 23:05:25 +00:00
Mike Bayer 8d2fd5f87a - Overhauled SQLite date/time bind/result processing
to use regular expressions and format strings, rather
than strptime/strftime, to generically support
pre-1900 dates, dates with microseconds.  [ticket:968]
2008-09-19 22:59:28 +00:00
Mike Bayer e395c05b9a the wisdom of SQLite accepting strings for columns with the INT type....priceless 2008-09-19 13:59:16 +00:00
Mike Bayer d6f6afe1f4 fix up element sorting in declarative 2008-09-19 13:58:12 +00:00
Gaëtan de Menten 0264fea050 Get a bit more speed into the new _sort_states function. It's probably possible
to get even more speed by getting rid of the decorator and call the method
directly, but it makes for slightly less readable code so I won't do it since I
don't know whether this code is speed-critical or not.
2008-09-19 07:11:46 +00:00
Mike Bayer bf493ac0b7 - Fixed bug involving read/write relation()s that
contain literal or other non-column expressions
within their primaryjoin condition equated
to a foreign key column.
- fixed UnmappedColumnError exception raise to not assume it was passed a column
2008-09-19 01:34:28 +00:00
Mike Bayer 2c2ecbae86 un-stupified insert/update/delete sorting 2008-09-19 00:04:38 +00:00
Mike Bayer 73b591b8ff more failing cases 2008-09-18 22:39:34 +00:00
Mike Bayer 16dd8aab74 "nested sets" example. needs work. 2008-09-18 22:14:29 +00:00
Mike Bayer 9632a752d2 - "non-batch" mode in mapper(), a feature which allows
mapper extension methods to be called as each instance
is updated/inserted, now honors the insert order
of the objects given.
- added some tests, some commented out, involving [ticket:1171]
2008-09-18 21:41:37 +00:00
Mike Bayer d47a469732 - version bump
- turned properties in sql/expressions.py to @property
- column.in_(someselect) can now be used as
a columns-clause expression without the subquery
bleeding into the FROM clause [ticket:1074]
2008-09-16 18:17:34 +00:00
Mike Bayer e0cc32c937 added gc.collect() for pypy/jython compat, [ticket:1076] 2008-09-16 17:43:13 +00:00
Mike Bayer 5b99a7ccc8 - annual unitofwork cleanup
- moved conversion of cyclical sort to UOWTask structure to be non-recursive
- reduced some verbosity
- rationale for the "tree" sort clarified
- would love to flatten all of uow topological sorting, sorting within mapper._save_obj() into a single sort someday
2008-09-15 21:29:28 +00:00
Mike Bayer 01de711098 - 0.5.0rc1
- removed unneeded grouping from BooleanClauseList, generated needless parens
2008-09-11 20:48:39 +00:00
Mike Bayer 37d59c1b7f - Added scalar() and value() methods to Query, each return a
single scalar value.  scalar() takes no arguments and is
roughly equivalent to first()[0], value()
takes a single column expression and is roughly equivalent to
values(expr).next()[0].
2008-09-11 19:35:40 +00:00
Jason Kirtland 42d7298a50 Note to self: save buffers before committing. 2008-09-11 18:44:36 +00:00
Jason Kirtland 53a3943b11 Added Query.scalar() sugar method, eases migration from old query.sum() methods. Needs tests. 2008-09-11 17:52:35 +00:00
Mike Bayer a21dc3d3e3 - the function func.utc_timestamp() compiles to UTC_TIMESTAMP, without
the parenthesis, which seem to get in the way when using in
conjunction with executemany().
2008-09-10 21:09:04 +00:00
Mike Bayer 8010017e45 return type of exists() is boolean, duh 2008-09-09 18:09:07 +00:00
Mike Bayer 3b724ae1cc - Bind params now subclass ColumnElement which allows them to be
selectable by orm.query (they already had most ColumnElement
semantics).

- Added select_from() method to exists() construct, which becomes
more and more compatible with a regular select().

- Bind parameters/literals given a True/False value will detect
their type as Boolean
2008-09-09 15:54:10 +00:00
Paul Johnston 8204fa721d Fix bug with MSSQL reflecting and schemas 2008-09-09 12:44:57 +00:00
Mike Bayer e158234478 - The exists() construct won't "export" its contained list
of elements as FROM clauses, allowing them to be used more
effectively in the columns clause of a SELECT.

- and_() and or_() now generate a ColumnElement, allowing
boolean expressions as result columns, i.e.
select([and_(1, 0)]).  [ticket:798]
2008-09-08 22:50:37 +00:00
Mike Bayer bf71da5ee6 reverted inheritance tweak which fails tests on non-sqlite 2008-09-08 03:57:25 +00:00
Mike Bayer 58c5bb7fc1 - Added func.min(), func.max(), func.sum() as "generic functions",
which basically allows for their return type to be determined
automatically.  Helps with dates on SQLite, decimal types,
others. [ticket:1160]

- added decimal.Decimal as an "auto-detect" type; bind parameters
and generic functions will set their type to Numeric when a
Decimal is used.
2008-09-08 03:51:47 +00:00
Mike Bayer cc0dcca7b4 - Removed conflicting contains() operator from
`InstrumentedAttribute` which didn't accept `escape` kwaarg
[ticket:1153].
2008-09-07 01:31:01 +00:00
Mike Bayer 36570c6595 - Dropped 0.3-compatibility for user defined types
(convert_result_value, convert_bind_param).
2008-09-07 00:13:28 +00:00
Mike Bayer c336ac6063 - query.order_by().get() silently drops the "ORDER BY" from
the query issued by GET but does not raise an exception.
2008-09-06 23:58:05 +00:00
Mike Bayer f3cca5255b - rearranged delete() so that the object is attached before
cascades fire off [ticket:5058]
- after_attach() only fires if the object was not previously attached
2008-09-05 17:16:11 +00:00
Mike Bayer 0fbb67b71a synchronize inherited does not need to be called for the full mapper hierarchy 2008-09-05 15:23:44 +00:00
Mike Bayer f432bd3550 - Fixed exception throw which would occur when string-based
primaryjoin condition was used in conjunction with backref.
2008-09-04 21:26:49 +00:00
Mike Bayer c586c0fe89 allow the no_criterion call in _get() to copy the method name thorugh 2008-09-04 20:41:51 +00:00
Mike Bayer 94c32b19a0 - Fixed bug whereby mapper couldn't initialize if a composite
primary key referenced another table that was not defined
yet [ticket:1161]
2008-09-04 17:44:48 +00:00
Mike Bayer cd8a3390e6 added BFILE to reflected type names [ticket:1121] 2008-09-03 18:16:55 +00:00
Mike Bayer a229f0d9c7 correct extra space in SQL assertions 2008-09-03 18:03:03 +00:00
Mike Bayer 57f79671bf - has_sequence() now takes the current "schema" argument into
account [ticket:1155]
2008-09-03 17:59:43 +00:00
Mike Bayer 9eafb43c0f - limit/offset no longer uses ROW NUMBER OVER to limit rows,
and instead uses subqueries in conjunction with a special
      Oracle optimization comment.  Allows LIMIT/OFFSET to work
      in conjunction with DISTINCT. [ticket:536]
2008-09-03 16:53:05 +00:00
Ants Aasma 920281ab55 Make Query.update and Query.delete return the amount of rows matched 2008-09-02 20:02:02 +00:00
Mike Bayer c164c174a5 correction 2008-09-02 19:59:55 +00:00
Mike Bayer 3829b89d69 - column_property(), composite_property(), and relation() now
accept a single or list of AttributeExtensions using the
"extension" keyword argument.
- Added a Validator AttributeExtension, as well as a
@validates decorator which is used in a similar fashion
as @reconstructor, and marks a method as validating
one or more mapped attributes.
- removed validate_attributes example, the new methodology replaces it
2008-09-02 19:51:48 +00:00
Mike Bayer 3e25e6e6b0 - AttributeListener has been refined such that the event
is fired before the mutation actually occurs.  Addtionally,
the append() and set() methods must now return the given value,
which is used as the value to be used in the mutation operation.
This allows creation of validating AttributeListeners which
raise before the action actually occurs, and which can change
the given value into something else before its used.
A new example "validate_attributes.py" shows one such recipe
for doing this.   AttributeListener helper functions are
also on the way.
2008-09-02 17:57:35 +00:00
Mike Bayer d578d67035 - Fixed custom instrumentation bug whereby get_instance_dict()
was not called for newly constructed instances not loaded
by the ORM.
2008-09-02 16:07:46 +00:00
Mike Bayer 714e629aeb - broke pool tests out into QueuePoolTest/SingletonThreadPoolTest
- added test for r5061/r5062 [ticket:1157]
2008-09-01 18:14:03 +00:00
Mike Bayer 91d8a87604 recheck the dirty list if extensions are present 2008-08-30 18:30:53 +00:00
Mike Bayer 66dd5d79e8 - The "extension" argument to Session and others can now
optionally be a list, supporting events sent to multiple
SessionExtension instances.  Session places SessionExtensions
in Session.extensions.
2008-08-29 16:31:58 +00:00
Mike Bayer 4c6c996f9b - add an example illustrating attribute event reception. 2008-08-29 16:15:41 +00:00
Mike Bayer ebbae4fa0a check extensions each time; user-defined code will be appending to "extensions" after the AttributeImpl has been constructed 2008-08-29 15:41:43 +00:00
Mike Bayer a18035cfb1 - starargs_as_list was not actually issuing SAPendingDeprecationWarning, fixed
- implemented code cleanup from [ticket:1152] but not including using the decorators module
2008-08-28 18:21:42 +00:00
Mike Bayer af342bba56 - Fixed bug whereby deferred() columns with a group in conjunction
with an otherwise unrelated synonym() would produce
an AttributeError during deferred load.
2008-08-28 17:11:18 +00:00
Michael Trier c99d54e762 Corrected typo in the mapper docs. Fixes #1159. 2008-08-28 14:21:07 +00:00
Jason Kirtland 0b4b2454af Type processors get a dialect, not an engine... 2008-08-27 19:10:03 +00:00
Mike Bayer f39e9b8418 ugh...try again 2008-08-27 06:01:16 +00:00
Mike Bayer ad6f932538 critical fix to r5028 repairs SingleThreadPool to return a connection in case one had been removed via cleanup() 2008-08-27 05:58:18 +00:00
Ants Aasma dcad710de2 - expire/fetch strategies are now default for Query.update/Query.delete.
- added API docs for Query.update/Query.delete
2008-08-25 00:04:01 +00:00
Mike Bayer 24ee97c610 - Fixed bug whereby changing a primary key attribute on an
entity where the attribute's previous value had been expired
would produce an error upon flush(). [ticket:1151]
2008-08-24 21:52:38 +00:00
Mike Bayer 34ecc55261 - Session.delete() adds the given object to the session if
not already present.  This was a regression bug from 0.4
[ticket:1150]
2008-08-24 21:31:00 +00:00
Mike Bayer ae573e047a - Added MSMediumInteger type [ticket:1146]. 2008-08-24 21:20:05 +00:00
Mike Bayer 4c29ed71d0 - logging scale-back; the echo_uow flag on Session is deprecated, and unit of work logging is now
class level like all the other logging.
- trimmed back the logging API, centralized class_logger() as the single point of configuration for
logging, removed per-instance logging checks from ORM.
- Engine and Pool logging remain at the instance level.  The modulus of "instance ids" has been upped
to 65535.  I'd like to remove the modulus altogether but I do see a couple of users each month
calling create_engine() on a per-request basis, an incorrect practice but I'd rather their applications
don't just run out of memory.
2008-08-24 21:10:36 +00:00
Mike Bayer 6f60e76883 - The 'length' argument to all Numeric types has been renamed
to 'scale'.  'length' is deprecated and is still accepted
with a warning. [ticket:827]
- The 'length' argument to MSInteger, MSBigInteger, MSTinyInteger,
MSSmallInteger and MSYear has been renamed to 'display_width'.
[ticket:827]
- mysql._Numeric now consumes 'unsigned' and 'zerofill' from
the given kw, so that the same kw can be passed along to Numeric
and allow the 'length' deprecation logic to still take effect
- added testlib.engines.all_dialects() to return a dialect for
every db module
- informix added to sqlalchemy.databases.__all__.  Since other
"experimental" dbs like access and sybase are there, informix
should be as well.
2008-08-24 19:52:54 +00:00
Mike Bayer e01e972ac3 - fixed tearDown to reverse sorted table list 2008-08-23 19:38:04 +00:00
Mike Bayer c03b6c2e41 - attributes now has an "active_history" flag. This flag indicates that when new value is set or the existing value is deleted, we absolutely need the previous value to be present, including if it requires hitting a lazy loader. Since somewhere around 0.4 we had not been loading the previous value as a performance optimization.
- the flag is set by a ColumnLoader which contains a primary key column.  This allows the mapper to have an accurate record of a primary key column when _save_obj() performs an UPDATE.
- the definition of who gets "active_history" may be expanded to include ForeignKey and any columns participating in a primaryjoin/seconddary join, so that lazyloaders can execute correctly on an expired object with pending changes to those attributes.
- expire-on-commit is why this is becoming a more important issue as of late
- fixes [ticket:1151], but unit tests, CHANGES note is pending
2008-08-22 15:09:27 +00:00
Mike Bayer 3c80e59ebc - column_property() and synonym() both accept comparator_factory argument, allowing
custom comparison functionality
- made the mapper's checks for user-based descriptors when defining synonym or comparable property
stronger, such that a synonym can be used with declarative without having a user-based descriptor
2008-08-21 18:10:35 +00:00
Jason Kirtland d08821ee5e - Another old-style mixin fix and an explicit mapper() test for it. 2008-08-21 14:24:45 +00:00
Gaëtan de Menten 90ba350099 - Fix occurences of Class.c.column_name
- Fix a few typos/mistakes
- removed trailing whitespaces
- tried to achieve a more consistent syntax for spaces in properties
  declaration
2008-08-21 09:12:54 +00:00
Mike Bayer 427ed1966f - fixed a bug in declarative test which was looking for old version of history
- Added "sorted_tables" accessor to MetaData, which returns
Table objects sorted in order of dependency as a list.
This deprecates the MetaData.table_iterator() method.
The "reverse=False" keyword argument has also been
removed from util.sort_tables(); use the Python
'reversed' function to reverse the results.
[ticket:1033]
2008-08-19 21:27:34 +00:00
Mike Bayer 20c82967ca catch AttributeError in case thread local storage was not configured 2008-08-19 16:33:01 +00:00
Jason Kirtland ee7366611b attributes.get_history now reports some zero-length slots as the empty tuple rather than an empty list. nice speed boost and memory reduction. 2008-08-18 18:57:05 +00:00
Jason Kirtland 5ba782841e hack tweak: exc.NO_STATE is a tuple. 2008-08-18 18:19:52 +00:00
Jason Kirtland f893bb0b51 more ORM @decorator fliparoo 2008-08-18 18:09:27 +00:00
Mike Bayer 1b65c7eed5 - The before_flush() hook on SessionExtension takes place
before the list of new/dirty/deleted is calculated for the
final time, allowing routines within before_flush() to
further change the state of the Session before the flush
proceeds.   [ticket:1128]

- Reentrant calls to flush() raise an error.  This also
serves as a rudimentary, but not foolproof, check against
concurrent calls to Session.flush().
2008-08-17 22:21:23 +00:00
Mike Bayer 9b6a9b7aea temporary check for unmapped class, until [ticket:1142] is resolved 2008-08-17 21:55:00 +00:00
Mike Bayer dd9d9bd9be - fixed primary key update for many-to-many collections
where the collection had not been loaded yet
[ticket:1127]
2008-08-16 23:10:56 +00:00
Mike Bayer 8e08949eb5 - class.someprop.in_() raises NotImplementedError pending
the implementation of "in_" for relation [ticket:1140]
2008-08-16 22:41:57 +00:00
Jason Kirtland 05ac1efe0d Applied .append(x, **kw) removal patch from [ticket:1124] and general cleanup. 2008-08-15 23:33:43 +00:00
Jason Kirtland 10848388a3 - Mock engines take on the .name of their dialect. [ticket:1123]
Slightly backward incompatible: the .name is a read-only property.
  The test suite was assigning .name = 'mock'; this no longer works.
2008-08-15 23:17:24 +00:00
Jason Kirtland 4556f4b3df - Don't choke when instrumenting a class with an old-style mixin. [ticket:1078] 2008-08-15 22:59:13 +00:00
Mike Bayer cc87685c93 removing this example until further notice (append_result() not an easy road to travel) 2008-08-15 22:57:14 +00:00
Jason Kirtland aaf72e05f1 - Ignore old-style classes when building inheritance graphs. [ticket:1078] 2008-08-15 22:54:35 +00:00
Jason Kirtland d70ed586c7 Re-use func_defaults when generating wrapper functions. [ticket:1139] 2008-08-15 22:28:55 +00:00
Jason Kirtland 2d4908e88c - Renamed on_reconstitute to @reconstructor and reconstruct_instance
- Moved @reconstructor hooking to mapper
- Expanded reconstructor tests, docs
2008-08-15 22:03:42 +00:00
Jason Kirtland 47f1d41473 Tidy. 2008-08-15 18:02:30 +00:00
Jason Kirtland 7acaa6c083 Ignore egg stuff. 2008-08-15 17:55:30 +00:00
Mike Bayer 282313ced6 adjust counts for 2.4 based on buildbot observation, remove 2.3 counts 2008-08-13 22:51:27 +00:00
Mike Bayer 10aeb7346d dont rely upon AttributeError to test for None 2008-08-13 22:49:38 +00:00
Mike Bayer cd7678a965 - with 2.3 support dropped,
all usage of thread.get_ident() is removed, and replaced
with threading.local() usage.  this allows potentially
faster and safer thread local access.
2008-08-13 22:41:17 +00:00
Mike Bayer c374f7b8b5 added import for interfaces, otherwise tsa.interfaces is undef if the test is run standalone 2008-08-13 21:11:42 +00:00
Mike Bayer d98caf9da8 - joins along a relation() from a mapped
class to a mapped subclass, where the mapped
subclass is configured with single table
inheritance, will include an
IN clause which limits the subtypes of the
joined class to those requsted, within the
ON clause of the join.  This takes effect for
eager load joins as well as query.join().
Note that in some scenarios the IN clause will
appear in the WHERE clause of the query
as well since this discrimination has multiple
trigger points.
2008-08-12 20:31:14 +00:00
Mike Bayer 6d01d96229 - Improved the behavior of query.join()
when joining to joined-table inheritance
subclasses, using explicit join criteria
(i.e. not on a relation).
2008-08-12 15:19:09 +00:00
Mike Bayer 7897dd9827 added info on named tuples 2008-08-12 14:55:38 +00:00
Jason Kirtland aba1c2b118 - When generating __init__, use a copy of the func_defaults, not a repr of them. 2008-08-11 18:27:25 +00:00
Mike Bayer bf43d45cea added col with no name example 2008-08-11 18:00:10 +00:00
Mike Bayer d55d29329e - The composite() property type now supports
a __set_composite_values__() method on the composite
class which is required if the class represents
state using attribute names other than the
column's keynames; default-generated values now
get populated properly upon flush.  Also,
composites with attributes set to None compare
correctly.  [ticket:1132]
2008-08-11 17:18:10 +00:00
Mike Bayer 50c4825b54 merged r5018 from 0.4 branch, but using contextual_connect() (will fix in 0.4 too) 2008-08-10 05:26:16 +00:00
Mike Bayer c20f8b1cfe comment 2008-08-08 15:50:36 +00:00
Mike Bayer 14c0dc7b4a - cleaned up the attributes scan for reconstitute hooks
- added more careful check for "_should_exclude", guard against possible heisenbug activity
2008-08-08 15:37:41 +00:00
Mike Bayer 4ab87682e1 added unit tests for [ticket:1024] 2008-08-08 14:56:53 +00:00
Mike Bayer cdd673f2e4 added missing **kwargs 2008-08-08 14:31:09 +00:00
Mike Bayer c73391c34c even better... 2008-08-08 05:15:18 +00:00
Mike Bayer 29d4335b73 - Fixed @on_reconsitute hook for subclasses
which inherit from a base class.
[ticket:1129]
2008-08-08 05:13:23 +00:00
Mike Bayer 2829092cb8 - Improved the determination of the FROM clause
when placing SQL expressions in the query()
list of entities.  In particular scalar subqueries
should not "leak" their inner FROM objects out
into the enclosing query.
2008-08-06 20:58:48 +00:00
Mike Bayer 665cd4d36a - Temporarily rolled back the "ORDER BY" enhancement
from [ticket:1068].  This feature is on hold
pending further development.
2008-08-06 15:46:31 +00:00
Mike Bayer 4cd1902796 - The RowTuple object returned by Query(*cols) now
features keynames which prefer mapped attribute
names over column keys, column keys over
column names, i.e.
Query(Class.foo, Class.bar) will have names
"foo" and "bar" even if those are not the names
of the underlying Column objects.  Direct
Column objects such as Query(table.c.col) will
return the "key" attribute of the Column.
2008-08-05 20:15:28 +00:00
Gaëtan de Menten 3c26d57003 slightly more user-friendly repr method for CascadeOptions 2008-08-05 09:15:31 +00:00
Michael Trier 045d0d63ea Corrected problem in docstring. 2008-08-04 19:53:04 +00:00
Mike Bayer d371637af0 - fixed endless loop bug which could occur
within a mapper's deferred load of
inherited attributes.
- declarative initialization of Columns adjusted so that
non-renamed columns initialize in the same way as a non
declarative mapper.   This allows an inheriting mapper
to set up its same-named "id" columns in particular
such that the parent "id" column is favored over the child
column, reducing database round trips when this value
is requested.
2008-08-04 15:21:29 +00:00
Lele Gaifax 28ff190475 Typo 2008-08-04 13:17:40 +00:00
Mike Bayer e53339cf74 some doc stuff 2008-08-04 03:05:26 +00:00
Mike Bayer 18a4912f77 removed redundant check to _enable_transaction_accounting 2008-08-03 21:35:44 +00:00
Mike Bayer edf6b16fae - compiler visit_label() checks a flag "within_order_by" and will render its own name
and not its contained expression, if the dialect reports true for supports_simple_order_by_label.
the flag is not propagated forwards, meant to closely mimic the syntax Postgres expects which is
that only a simple name can be in the ORDER BY, not a more complex expression or function call
with the label name embedded (mysql and sqlite support more complex expressions).

This further sets the standard for propigation of **kwargs within compiler, that we can't just send
**kwargs along blindly to each XXX.process() call; whenever a **kwarg needs to propagate through,
most methods will have to be aware of it and know when they should send it on forward and when not.
This was actually already the case with result_map as well.

The supports_simple_order_by dialect flag defaults to True but is conservatively explicitly set to
False on all dialects except SQLite/MySQL/Postgres to start.

[ticket:1068]
2008-08-03 21:19:32 +00:00
Mike Bayer 8df49e7194 descriptive error message raised when string-based relation() expressions inadvertently mistake a PropertyLoader for a ColumnLoader property 2008-08-03 18:39:53 +00:00
Mike Bayer 4769ea895b - renamed autoexpire to expire_on_commit
- renamed SessionTransaction autoflush to reentrant_flush to more clearly state its purpose
- added _enable_transaction_accounting, flag for Mike Bernson which disables the whole 0.5 transaction state management; the system depends on expiry on rollback in order to function.
2008-08-03 18:03:57 +00:00
Mike Bayer 312647647d a correction to the recent should_exclude change. should_exclude is a little mixed
up as to when it honors "column_prefix" and when it doesn't, depending on whether or not
the prop is coming from a column name or from an inherited class.  Will need more testing
to uncover potential issues here.
2008-08-03 16:52:31 +00:00
Mike Bayer d28ba32271 - The "entity_name" feature of SQLAlchemy mappers
has been removed.  For rationale, see
http://groups.google.com/group/sqlalchemy/browse_thread/thread/9e23a0641a88b96d?hl=en
2008-08-02 22:21:42 +00:00
Jason Kirtland 19bc91c757 - Refactored declarative_base() as a thin wrapper over type()
- The supplied __init__ is now optional
- The name of the generated class can be specified
- Accepts multiple bases
2008-08-02 17:07:33 +00:00
Jason Kirtland 8c261ab7b7 - declarative.declarative_base():
takes a 'metaclass' arg, defaulting to DeclarativeMeta
  renamed 'engine' arg to 'bind', backward compat
  documented
2008-08-02 16:32:02 +00:00
Jonathan Ellis 72a4e6b265 make ProxyImpl a top-level class (this makes it importable by FormAlchemy, making reverse-engineering synonyms a bit easier) 2008-08-01 23:02:02 +00:00
Mike Bayer c7eeea8a95 further refinement to the inheritance "descriptor" detection such that
local columns will still override superclass descriptors.
2008-08-01 17:13:31 +00:00
Mike Bayer 5d55eac6b6 test case to disprove [ticket:1126] 2008-08-01 15:10:36 +00:00
Mike Bayer 2cb79b9808 added MutableType, Concatenable to __all__ 2008-07-31 16:48:32 +00:00
Mike Bayer 86126c98a2 - Fixed bug whereby the "unsaved, pending instance"
FlushError raised for a pending orphan would not take
superclass mappers into account when generating
the list of relations responsible for the error.
2008-07-31 16:41:41 +00:00
Mike Bayer 449b33e059 relation.order_by requires _literal_as_column conversion as well 2008-07-29 19:49:46 +00:00
Gaëtan de Menten fd51706903 typo 2008-07-29 08:43:30 +00:00
Michael Trier bdbf3e302a Corrects reflecttable in firebird database. Closes #1119. 2008-07-29 03:17:02 +00:00
Michael Trier c622a86286 Raised an error when sqlite version does not support default values. Addresses #909 in a purposeful way. 2008-07-29 03:08:38 +00:00
Mike Bayer e411a83783 added dummy column to correct results on sqlite 2008-07-26 21:40:36 +00:00
Jason Kirtland b403f156fe - func.count() with no argument emits COUNT(*) 2008-07-24 21:36:16 +00:00
Michael Trier 951fe224fa Corrected problem with detecting closed connections. Fixed issues in reflecttable for reflecting the mssql tables. Removed unicode reflection test from mssql. Need to investigate this further. 2008-07-23 05:10:04 +00:00
Mike Bayer cfb9bbde7d allow SQLA-defaults on table columns that are excluded in the mapper 2008-07-22 13:45:29 +00:00
Mike Bayer 59b25a513a - more accurate changelog message
- generalized the descriptor detection to any object with a __get__ attribute
2008-07-20 18:36:44 +00:00
Mike Bayer 419753f59b - An inheriting class can now override an attribute
inherited from the base class with a plain descriptor,
or exclude an inherited attribute via the
include_properties/exclude_properties collections.
2008-07-20 18:23:44 +00:00
Mike Bayer a4781e4d76 - A critical fix to dynamic relations allows the
"modified" history to be properly cleared after
a flush().
2008-07-19 21:33:58 +00:00
Mike Bayer d56862cbca - Some improvements to the _CompileOnAttr mechanism which
should reduce the probability of "Attribute x was
not replaced during compile" warnings. (this generally
applies to SQLA hackers, like Elixir devs).
2008-07-19 19:23:37 +00:00
Mike Bayer c5141f2981 - Class-bound attributes sent as arguments to
relation()'s remote_side and foreign_keys parameters
are now accepted, allowing them to be used with declarative.
2008-07-19 18:55:11 +00:00
Mike Bayer e78d06a4db - reverted r4955, that was wrong. The backref responsible for the operation is the one where the "cascade" option should take effect.
- can use None as a value for cascade.
- documented cascade options in docstring, [ticket:1064]
2008-07-19 18:18:50 +00:00
Michael Trier 5c75aed9be Corrected a couple of lingering transactional=True statements in the docs. 2008-07-19 17:52:31 +00:00
Mike Bayer 0cb0dc0694 zoomarks have gone up as a result of r4936, possibly others. not clear why 2008-07-18 22:28:16 +00:00
Mike Bayer 100c17229f - save-update and delete-orphan cascade event handler
now considers the cascade rules of the event initiator only, not the local
attribute.  This way the cascade of the initiator controls the behavior
regardless of backref events.
2008-07-18 22:11:22 +00:00
Mike Bayer 5e7f90a1d6 - Fixed a series of potential race conditions in
Session whereby asynchronous GC could remove unmodified,
no longer referenced items from the session as they were
present in a list of items to be processed, typically
during session.expunge_all() and dependent methods.
2008-07-18 17:42:11 +00:00
Mike Bayer 5af88c5df1 - MapperProperty gets its .key attribute assigned early, in _compile_property.
MapperProperty compilation is detected using a "_compiled" flag.
- A mapper which inherits from another, when inheriting
the columns of its inherited mapper, will use any
reassigned property names specified in that inheriting
mapper.  Previously, if "Base" had reassigned "base_id"
to the name "id", "SubBase(Base)" would still get
an attribute called "base_id".   This could be worked
around by explicitly stating the column in each
submapper as well but this is fairly unworkable
and also impossible when using declarative [ticket:1111].
2008-07-16 21:56:23 +00:00
Mike Bayer 0d9985588b added a new test illustrating a particular inheritance bug. will add ticket 2008-07-16 21:23:17 +00:00
Jason Kirtland a00b42b289 - mysql.MSEnum value literals now automatically quoted when used in a CREATE.
The change is backward compatible. Slight expansion of patch from catlee.
  Thanks! [ticket:1110]
2008-07-16 18:24:20 +00:00
Jason Kirtland b155b60280 - Spiffed up the deprecated decorators & @flipped 'em up top 2008-07-16 17:34:41 +00:00
Jason Kirtland 437dd22d49 Removed deprecated get_version_info, use server_version_info 2008-07-16 15:25:33 +00:00
Jason Kirtland bb784c8244 - Overhauled _generative and starargs decorators and flipped to 2.4 @syntax 2008-07-16 06:47:22 +00:00
Jason Kirtland 8b169bdc1a - Fixed some over-long ReST lines & general formatting touchups 2008-07-15 22:01:15 +00:00
Jason Kirtland 5f8554f0de Completed engine_descriptors() removal (started in r4900) 2008-07-15 21:50:48 +00:00
Jason Kirtland 38f982c022 - Moved to 2.4+ import syntax (w/ some experimental merge-friendly formatting) 2008-07-15 21:43:02 +00:00
Jason Kirtland 3da6297d78 Whitespace tweaks suggested by pep8.py 2008-07-15 20:20:41 +00:00
Jason Kirtland 901d76beba - Removed the last of the 2.3 dict compat & some formatting tweaks. 2008-07-15 20:06:56 +00:00
Jason Kirtland acbfb9ea2b - Always use native threading.local (or the native dummy version) 2008-07-15 19:56:30 +00:00
Jason Kirtland 4f56fd23bc - Always use native itemgetter & attrgetter 2008-07-15 19:53:17 +00:00
Jason Kirtland 4eb747b61f - Always use native deque 2008-07-15 19:46:24 +00:00
Jason Kirtland 8b12c8f1c2 - Removed 2.3 Decimal compat 2008-07-15 19:40:08 +00:00
Jason Kirtland 1d37472fdd - Dropped reversed emulation 2008-07-15 19:29:41 +00:00
Jason Kirtland 8fa48edbf9 - Removed 2.3 set emulations/enhancements.
(sets.Set-based collections & DB-API returns still work.)
2008-07-15 19:23:52 +00:00
Jason Kirtland 6917ffb9bd And thus ends support for Python 2.3. 2008-07-15 18:21:24 +00:00
Jason Kirtland 4fe4127958 - Fixed a couple lingering exceptions->exc usages
- Some import tidying
2008-07-15 16:39:27 +00:00
Mike Bayer deaff3e97f - Fixed bug when calling select([literal('foo')])
or select([bindparam('foo')]).
2008-07-15 15:04:43 +00:00
Mike Bayer af38982273 - Added a new SessionExtension hook called after_attach().
This is called at the point of attachment for objects
via add(), add_all(), delete(), and merge().
2008-07-15 14:54:37 +00:00
Paul Johnston 16e8d44686 Fix reflection where the table name has a duplicate name in a different schema 2008-07-15 09:15:59 +00:00
Mike Bayer 44aa875212 - The "allow_column_override" flag from mapper() has
been removed.  This flag is virtually always misunderstood.
Its specific functionality is available via the
include_properties/exclude_properties mapper arguments.
2008-07-14 20:04:35 +00:00
Mike Bayer 01bce5c129 fix adjacency list examples 2008-07-14 19:44:37 +00:00
Mike Bayer 05b44779a7 2.4 support ! 2008-07-14 19:42:42 +00:00
Mike Bayer 45a2b0fedf possible fix for MS-SQL version of match() test, but the real solution here may be to have the correct default paramstyle set up on the MS-SQL dialect. 2008-07-14 19:39:50 +00:00
Mike Bayer e91072a95c bump 2008-07-14 19:34:39 +00:00
Michael Trier 29247e6b01 Fixed up some very annoying lengthy lines. 2008-07-14 18:20:06 +00:00
Michael Trier 861ac64607 Reverted CHANGES change. Not necessary for this type of fix. 2008-07-14 17:48:38 +00:00
Michael Trier bdc0400ac0 Added notation about MSSmallDate fix into CHANGES.
(cherry picked from commit 461ee7e08bb2a6f7b10b1c9f1348cc29bfecacbb)
2008-07-14 04:24:22 +00:00
Mike Bayer 472215eef2 added a passing test for [ticket:1105] 2008-07-14 00:17:04 +00:00
Jason Kirtland 23db10d940 And more 2008-07-13 17:52:12 +00:00
Jason Kirtland faf347b5b9 Typo 2008-07-13 17:50:26 +00:00
Michael Trier 151cac8bd2 Fixed messed up __init__ in MSSmallDate. Fixes #1040. 2008-07-13 17:48:32 +00:00
Michael Trier f899157ca9 Added new basic match() operator that performs a full-text search. Supported on PostgreSQL, SQLite, MySQL, MS-SQL, and Oracle backends. 2008-07-13 04:45:37 +00:00
Gaëtan de Menten bad78d9767 typo 2008-07-11 14:21:05 +00:00
Gaëtan de Menten b2edf4a0fd typo 2008-07-11 07:39:21 +00:00
Jason Kirtland 4611ad6889 Let doc font sizes adapt to browser prefs (experimental) 2008-07-10 20:31:19 +00:00
Jason Kirtland 8b6855fc2c Added default support to OrderedDict.pop [ticket:585] 2008-07-10 19:16:08 +00:00
Jason Kirtland f299d9ea04 Flag beta docs with a big red capsule 2008-07-10 18:32:38 +00:00
Mike Bayer 5d375cd730 - Declarative supports a __table_args__ class variable, which
is either a dictionary, or tuple of the form
(arg1, arg2, ..., {kwarg1:value, ...}) which contains positional
+ kw arguments to be passed to the Table constructor.
[ticket:1096]
2008-07-09 20:38:35 +00:00
Mike Bayer ff9c5007a8 - Unicode, UnicodeText types now set "assert_unicode" and
"convert_unicode" by default, but accept overriding
      **kwargs for these values.
2008-07-09 16:33:38 +00:00
Mike Bayer e7e60c05c0 - SQLite Date, DateTime, and Time types only accept Python
datetime objects now, not strings.  If you'd like to format
dates as strings yourself with SQLite, use a String type.
If you'd like them to return datetime objects anyway despite
their accepting strings as input, make a TypeDecorator around
String - SQLA doesn't encourage this pattern.
2008-07-09 16:15:14 +00:00
Michael Trier 36e7efa4eb Fixed borked testlib due to r4901. 2008-07-08 02:48:13 +00:00
Michael Trier cec5947b5b Refactored the mock_engine in the tests so it's not duplicated in several places. Closes #1098 2008-07-08 01:37:38 +00:00
Mike Bayer 93c706a03b - re-fixed the fix to the prefixes fix
- removed ancient descriptor() functions from dialects; replaced with Dialect.name
- removed similarly ancient sys.modules silliness in Engine.name
2008-07-06 00:47:56 +00:00
Mike Bayer 0f42004dee - session.refresh() raises an informative error message if
the list of attributes does not include any column-based
attributes.

- query() raises an informative error message if no columns
or mappers are specified.

- lazy loaders now trigger autoflush before proceeding.  This
allows expire() of a collection or scalar relation to
function properly in the context of autoflush.

- whitespace fix to new Table prefixes option
2008-07-05 20:37:44 +00:00
Mike Bayer cf9edea203 commented out bus erroring section for now pending [ticket:1099] resolution 2008-07-05 20:35:26 +00:00
Michael Trier 9d7c6901cb Added prefixes option to that accepts a list of string to insert after CREATE in the CREATE TABLE statement. Closes #1075. 2008-07-05 03:31:30 +00:00
Michael Trier b11a772d1e Corrected grammar on session documents. Closes #1097. 2008-07-04 21:36:00 +00:00
Gaëtan de Menten 7ade777df2 update poly_assoc examples for 0.4+ syntax 2008-07-03 13:19:31 +00:00
Michael Trier 5af78e2946 Fixed typo where plugins docs were referencing synonyn_for instead of synonym_for. Closes #1029 2008-07-03 04:38:28 +00:00
Michael Trier d3581d1c09 Added PGCidr type to postgres. Closes #1092
(cherry picked from commit 2394a6bb6c5f77afd448640ce03cf6fda0335a23)
2008-07-03 04:21:13 +00:00
Michael Trier 4af4968926 Corrected a reference to alt_schema_2 and fixed a docstring indentation for Table. 2008-07-03 03:10:46 +00:00
Mike Bayer f3a9acc7e2 merge r4889, SQLite Float type, from 0.4 branch 2008-07-02 18:29:36 +00:00
Michael Trier 644d7ea600 Corrected docstring for Pool class to show that the default value for use_threadlocal is False. closes #1095. 2008-07-02 15:37:02 +00:00
Gaëtan de Menten 38180f855a simplified _get_colspec 2008-07-02 12:58:57 +00:00
Ants Aasma c571fd6f8a Ugh, learning to use git-svn, [4884] was not supposed to go upstream. Reverting. 2008-07-01 17:00:51 +00:00
Ants Aasma 64a23962ee Session.bind gets used as a default even when table/mapper specific binds are defined. 2008-07-01 16:51:25 +00:00
Ants Aasma c33e7c7bfa query update and delete need to autoflush 2008-07-01 16:51:14 +00:00
Mike Bayer 5873525e48 0.5 2008-06-30 21:33:57 +00:00
Mike Bayer 4095056a01 removed fairly pointless test which relied on PK generation artifacts 2008-06-30 05:09:04 +00:00
Jason Kirtland eaa4328aac - consider args[0] as self when introspecting def(*args): ... [ticket:1091] 2008-06-29 18:58:11 +00:00
Mike Bayer 8755dc3d66 - fixed up vertical.py
- Fixed query.join() when used in conjunction with a
columns-only clause and an SQL-expression
ON clause in the join.
2008-06-28 15:23:08 +00:00
Mike Bayer 4a66683c9f - Modified SQLite's representation of "microseconds" to
match the output of str(somedatetime), i.e. in that the
microseconds are represented as fractional seconds in
string format.  [ticket:1090]
- implemented a __legacy_microseconds__ flag on DateTimeMixin which can
be used per-class or per-type instances to get the old behavior, for
compatibility with existing SQLite databases encoded by a previous
version of SQLAlchemy.
- will implement the reverse legacy behavior in 0.4.
2008-06-27 20:12:11 +00:00
Jonathan Ellis a02b12da7e use normal ScopedSession, with autoflush, instead of custom one 2008-06-27 17:23:36 +00:00
Gaëtan de Menten d0c243711b session.Query().iterate_instances() has been renamed to just instances(). The old instances() method returning a list instead of an iterator no longer
exists. If you were relying on that behavior, you should use `list(your_query.instances())`.
2008-06-25 15:55:49 +00:00
Mike Bayer 22fcee0e07 - Repaired __str__() method on Query. [ticket:1066] 2008-06-24 19:27:32 +00:00
Mike Bayer 3e8b095178 - Fixed explicit, self-referential joins between two
joined-table inheritance mappers when using
query.join(cls, aliased=True).  [ticket:1082]
2008-06-22 19:02:19 +00:00
Mike Bayer f2af07217f fixed the quote() call within dropper.visit_index() 2008-06-22 19:01:42 +00:00
Mike Bayer 8c1f08c8aa merged r4870 from 0.4 branch, index name truncation, [ticket:820] 2008-06-22 17:52:13 +00:00
Mike Bayer b2b754c2ce - merged r4868, disallow overly long names from create/drop, from 0.4 branch, [ticket:571] 2008-06-22 16:56:16 +00:00
Mike Bayer be2d349ade - fixed some concrete inheritance ramifications regarding r4866
- added explicit test coverage for r4866 with joined table inheritance
2008-06-21 18:08:34 +00:00
Mike Bayer c5e2d673a9 - implemented [ticket:887], refresh readonly props upon save
- moved up "eager_defaults" active refresh step (this is an option used by just one user pretty much)
to be per-instance instead of per-table
- fixed table defs from previous deferred attributes enhancement
- CompositeColumnLoader equality comparison fixed for a/b == None; I suspect the composite capability in SA
needs a lot more work than this
2008-06-21 17:23:14 +00:00
Mike Bayer 060c3ce33c - In addition to expired attributes, deferred attributes
also load if their data is present in the result set
[ticket:870]
2008-06-21 16:08:04 +00:00
Gaëtan de Menten 8af372585a better comment 2008-06-20 13:33:43 +00:00
Jason Kirtland c49a7dd910 - Oops, convert @decorator to 2.3 syntax and strengthen raw_append test. 2008-06-19 17:40:13 +00:00
Mike Bayer c1363d8aca - Added is_active flag to Sessions to detect when
a transaction is in progress [ticket:976].  This
flag is always True with a "transactional"
(in 0.5 a non-"autocommit") Session.
2008-06-17 20:52:11 +00:00
Mike Bayer 3bff98d530 test coverage for server side statement detection 2008-06-17 20:16:26 +00:00
Mike Bayer be818baf28 merged r4857, postgres server_side_cursors fix, from 0.4 branch 2008-06-17 15:15:16 +00:00
Mike Bayer fd107f2329 remove old test 2008-06-12 20:46:22 +00:00
Mike Bayer d3fb8ff904 updated verbiage for 0.5beta1 release 2008-06-12 20:29:13 +00:00
Jason Kirtland c8de80f4ea - Don't insist on locals() mutability [ticket:1073] 2008-06-12 20:09:02 +00:00
Mike Bayer c48ea0ae43 will call this beta1 (same as 0.4 version did) 2008-06-12 20:04:01 +00:00
Mike Bayer 887c8efc5b - merged r4841 from 0.4 branch (enable_typechecks lockdown) 2008-06-12 03:53:39 +00:00
Mike Bayer d733c2f829 restored a "distinct" setting that got whacked 2008-06-09 19:14:39 +00:00
Mike Bayer e83459d37d docstrings for instances()/iterate_instances() 2008-06-09 01:49:59 +00:00
Mike Bayer 3cd10102e4 - Query.UpdateDeleteTest.test_delete_fallback fails on mysql due to subquery in DELETE; not sure how to do this exact operation in MySQL
- added query_cls keyword argument to sessionmaker(); allows user-defined Query subclasses to be generated by query().
- added @attributes.on_reconstitute decorator, MapperExtension.on_reconstitute, both receieve 'on_load' attribute event allowing
non-__init__ dependent instance initialization routines.
- push memusage to the top to avoid pointless heisenbugs
- renamed '_foostate'/'_fooclass_manager' to '_sa_instance_state'/'_sa_class_manager'
- removed legacy instance ORM state accessors
- query._get() will use _remove_newly_deleted instead of expunge() on ObjectDeleted, so that transaction rollback
restores the previous state
- removed MapperExtension.get(); replaced by a user-defined Query subclass
- removed needless **kwargs from query.get()
- removed Session.get(cls, id); this is redundant against Session.query(cls).get(id)
- removed Query.load() and Session.load(); the use case for this method has never been clear, and the same functionality is available in more explicit ways
2008-06-09 01:24:08 +00:00
Mike Bayer cde133c45d merged merge fix from r4834/rel_0_4 branch 2008-06-03 14:33:08 +00:00
Mike Bayer 86ded90c6e make Query._clone() class-agnostic 2008-06-02 15:32:17 +00:00
Mike Bayer c998539b40 illustrates a simple Query "hook" to implement query caching. 2008-06-02 15:27:38 +00:00
Mike Bayer e525aee015 - removed query.min()/max()/sum()/avg(). these should be called using column arguments or values in conjunction with func.
- fixed [ticket:1008], count() works with single table inheritance
- changed the relationship of InstrumentedAttribute to class such that each subclass in an inheritance hierarchy gets a unique InstrumentedAttribute per column-oriented attribute, including for the same underlying ColumnProperty.  This allows expressions from subclasses to be annotated accurately so that Query can get a hold of the exact entities to be queried when using column-based expressions.  This repairs various polymorphic scenarios with both single and joined table inheritance.
- still to be determined is what does something like query(Person.name, Engineer.engineer_info) do; currently it's problematic.  Even trickier is query(Person.name, Engineer.engineer_info, Manager.manager_name)
2008-06-02 03:07:12 +00:00
Mike Bayer e3e1535720 merged r4829 of rel_0_4, [ticket:1058] 2008-06-01 14:15:41 +00:00
Mike Bayer 29e34c2a4b merged [ticket:1062] fix from 0.4 branch r4827 2008-05-30 21:01:20 +00:00
Mike Bayer 59c1887945 - improved the attribute and state accounting performed by query.update() and query.delete()
- added autoflush support to same
2008-05-29 14:45:40 +00:00
Jason Kirtland ff7574ffa3 - Lengthless String type 2008-05-29 02:42:58 +00:00
Ants Aasma 87718fa82b Add delete and update methods to query 2008-05-29 02:12:17 +00:00
Ants Aasma c194844692 Not implemenented binary ops also raise UnevaluatableError 2008-05-29 02:12:06 +00:00
Ants Aasma 4cccb50228 add with_only_columns to Select to allow for removing columns from selects 2008-05-29 02:11:59 +00:00
Ants Aasma 77c308367f Preliminary implementation for the evaluation framework 2008-05-29 02:11:49 +00:00
Mike Bayer 5ccfa64294 - bumped PG's call count on test #6 to 1193 for py2.4; this is due to non-pool-threadlocal nature adding some checkout overhead 2008-05-27 03:08:35 +00:00
Mike Bayer 946e3b7114 - added "CALL" to Mysql select keywords
- NameError doesn't have "message" in py2.4
2008-05-27 02:10:21 +00:00
Mike Bayer cd95c479e9 added string argument resolution to relation() in conjunction with declarative for: order_by,
primaryjoin, secondaryjoin, secondary, foreign_keys, and remote_side.
2008-05-26 23:01:05 +00:00
Mike Bayer ae9297bd18 a comment indicating why we can't raise an error for relation(Foo, uselist=False, order_by=something) 2008-05-26 18:35:24 +00:00
Mike Bayer fa42abd213 oracle dialect takes schema name into account when checking for existing tables
of the same name. [ticket:709]
2008-05-24 23:34:04 +00:00
Mike Bayer 6510bfbcf7 - PropertyLoader.foreign_keys becomes private
- removed most __foo() defs from properties.py
- complexity reduction in PropertyLoader.do_init()
2008-05-24 18:53:57 +00:00
Mike Bayer 4a742751d5 - removed info about _local_remote_pairs from PropertyLoader.__determine_fks
- added order_by(), group_by(), having() to the list of "no offset()/limit()", [ticket:851]
2008-05-24 17:37:58 +00:00
Mike Bayer 1b1b68a337 merged r4809 from rel_0_4, oracle fix 2008-05-24 17:11:41 +00:00
Jason Kirtland b985f34f7a Removed inlining for list.append. 2008-05-23 16:58:20 +00:00
Jason Kirtland eb8a6ed51a - unrolled loops for the simplified Session.get_bind() args
- restored the chunk of test r4806 deleted (!)
2008-05-21 23:58:16 +00:00
Mike Bayer cff1686cb9 - globally renamed refresh_instance to refresh_state
- removed 'instance' arg from session.get_bind() and friends, this is not a public API
- renamed 'state' arg on same to '_state'
- fixes [ticket:1055]
2008-05-21 21:40:58 +00:00
Jason Kirtland 07a3b8520b Updated fixmes. 2008-05-21 20:34:59 +00:00
Jason Kirtland afb2ca13a3 Updated some todos. 2008-05-21 20:32:00 +00:00
Jason Kirtland 38681c6b39 Removed deprecated Dialect.prexecute_sequences aliasing 2008-05-21 19:45:42 +00:00
Jason Kirtland 790f3d44d9 - Fixed ORM orphaning bug with _raw_append method
- Promoted _reorder to reorder
- Now horking docstrings of overloaded methods from list
- Added a doctest
2008-05-21 18:31:52 +00:00
Jason Kirtland a66c441043 - Be a little smarter about aliased funcs/methods by ignoring func_name 2008-05-21 18:28:24 +00:00
Jason Kirtland 3d17498c1d - Another namespace cleanup tweak, why not. 2008-05-21 18:27:21 +00:00
Jason Kirtland d1c80500c5 - Docstring fix. 2008-05-21 18:25:44 +00:00
Jason Kirtland b1911f0efa Duh. 2008-05-21 18:17:23 +00:00
Jason Kirtland cfc59b1713 - Removed deprecated append(val, **kw)
- dict/set/list proxies are now docstring'd like their python counterparts
2008-05-21 15:43:00 +00:00
Jason Kirtland 8adc6f2008 - More uses of exc.NO_STATE 2008-05-21 15:11:06 +00:00
Jason Kirtland 92c7a834d7 - Centralized 'x is not mapped' reporting into sa.orm.exc.
- Guards are now present on all public Session methods and passing in an
  unmapped hoho anywhere yields helpful exception messages, going to some
  effort to provide hints for debugging situations that would otherwise seem
  hopeless, such as broken user instrumentation or half-pickles.
2008-05-21 03:39:06 +00:00
Jason Kirtland 6536af53fc - ...and added bind.py into the orm suite 2008-05-21 03:33:54 +00:00
Jason Kirtland af11465840 - Moved an ORM test out of engine... 2008-05-21 03:31:20 +00:00
Jason Kirtland 3b90b8d772 - 2.3 compat. 2008-05-21 02:49:14 +00:00
Jason Kirtland 731115ffbd Refactor-o fix. 2008-05-21 00:18:51 +00:00
Jonathan Ellis 30ad04ba6d handle null tablespace_name 2008-05-20 23:16:45 +00:00
Jason Kirtland 938badb2bb - Fleshed out Session.get_bind(), generating a couple todos: [ticket:1053], [ticket:1054], [ticket:1055]
- Trotted out util.pending_deprecation, replacing some 'TODO: deprecate's
- Big session docstring content edit fiesta
- session.py line length and whitespace non-fiesta
2008-05-20 21:44:43 +00:00
Jonathan Ellis 9be3b882b2 add CHAR to ischema_names map; some minor cleanup 2008-05-20 21:30:11 +00:00
Jason Kirtland 18a31d0316 - Quick cleanup of defaults.py. The main DefaultTest is still a mess. 2008-05-20 00:14:51 +00:00
Jason Kirtland 8e9fce417a Split out a couple true autoincrement/identity tests from emulated-with-sequences autoincrement=True tests. 2008-05-19 23:15:41 +00:00
Jason Kirtland 856e8b1cae Formatting. 2008-05-19 23:10:02 +00:00
Mike Bayer b3102a097a - changed char_length() to use a fake, neutral "generic function"
- assert_compile() reports the dialect in use
2008-05-19 22:46:14 +00:00
Mike Bayer ad23f3b068 - zoomark/zoomark_orm seem to work with pool_threadlocal turned off, [ticket:1050] becomes WORKSFORME
- fixed probably errenous unique=True checkin on unitofwork.py
2008-05-19 22:35:32 +00:00
Jason Kirtland ced326dc1f - Implemented generic CHAR_LENGTH for sqlite (-> LENGTH())
- Updated .requires for firebird
2008-05-19 19:37:44 +00:00
Lele Gaifax f071ba1d3b Remove some noise from the uow test 2008-05-19 18:35:34 +00:00
Mike Bayer dbda75ec5f pool_threadlocal is off by default [ticket:1049] 2008-05-19 15:46:32 +00:00
Mike Bayer 89a8546d00 -removed useless log statement (merge garbage?)
- clarified autocommit mechanism
2008-05-18 17:09:21 +00:00
Mike Bayer 82f80559b9 put a cleanup handler on the "echo" property to try preventing log garbage in the buildbot 2008-05-18 16:40:02 +00:00
Mike Bayer 01b952c336 added ORM version of zoomark, python 2.5 only for starters 2008-05-18 16:19:21 +00:00
Mike Bayer d4f2a5d6c5 some order by's failing on the buildbot 2008-05-18 15:55:58 +00:00
Mike Bayer fa56f0acb4 - added test for threadlocal not supporting begin_nested()
- removed query.compile(); use explicit query.with_labels().statement instead
- moved statement annotation step upwards from query._compile_context() to outliers from_self()/statement.  speeds zoomark.step_6_editing by 16%
2008-05-18 15:49:14 +00:00
Lele Gaifax 664687adb9 The column default has been renamed server_default in 0.5 2008-05-16 23:09:54 +00:00
Mike Bayer fcc4f9193a begin() pre-issues a flush() in all cases, better fix for [ticket:1046] and allows rollback to work properly with autocommit=True/begin() 2008-05-16 22:25:53 +00:00
Mike Bayer 7d3ba0dec7 dont raise assertions when in autocommit mode [ticket:1046] 2008-05-16 22:15:44 +00:00
Mike Bayer 07496da9b5 - added some help for a heavily flush-order-dependent test
- quote flag propagates to _Label, [ticket:1045]
2008-05-16 22:10:04 +00:00
Mike Bayer 2beb99a60e added an assertion to prevent against the use in [ticket:1048] 2008-05-16 21:38:56 +00:00
Mike Bayer e314a548bd auto exists remembers to alias in the case of explicit selectable with of_type(), [ticket:1047] 2008-05-16 21:27:51 +00:00
Mike Bayer 806c7024b3 a particular test which fails in 0.4 2008-05-16 21:27:15 +00:00
Jason Kirtland f7cc199d37 Don't blat Table.quote= when resolving foreign keys. 2008-05-15 16:20:50 +00:00
Lele Gaifax 590e74182f Fix table.delete() arguments 2008-05-15 12:45:50 +00:00
Lele Gaifax 2f2bce5651 Followup to [4760]: forward **kwargs on TableClause.delete() 2008-05-15 12:44:13 +00:00
Lele Gaifax d044346a3c Corrected Firebird failure reasons 2008-05-15 00:18:58 +00:00
Lele Gaifax 00475df2ef Fix typo 2008-05-15 00:08:14 +00:00
Lele Gaifax d415a8edd4 Minor doc fixes 2008-05-15 00:07:32 +00:00
Lele Gaifax f56065c098 Correct failure reason 2008-05-15 00:07:00 +00:00
Lele Gaifax 0905bb4ba6 Augment expression.Delete() with a kwargs, like Insert() and Update() 2008-05-15 00:06:04 +00:00
Jason Kirtland c6e654db79 Eep. 2008-05-14 22:41:32 +00:00
Mike Bayer 746bd51c48 raise NotImplemented for begin_nested() 2008-05-14 22:21:38 +00:00
Jason Kirtland dd20ca5cb9 - Removed @unsupported 2008-05-14 22:09:23 +00:00
Jason Kirtland b2504db4f7 Fix fix. 2008-05-14 21:27:06 +00:00
Jason Kirtland 81b1df8fe1 Query.one() raises either NoResultFound or MultipleResultsFound, [ticket:1034] 2008-05-14 20:44:16 +00:00
Jason Kirtland 78dc35822e - Adjusted zoomoark
- Added test/orm/defaults.  Ambitiously uses ansi triggers.
2008-05-14 19:55:35 +00:00
Jason Kirtland 65f4f02ec8 Columns now have default= and server_default=. PassiveDefault fades away. 2008-05-14 19:49:40 +00:00
Mike Bayer a52a8dd876 fixes for PG, Mysql 2008-05-14 18:28:39 +00:00
Mike Bayer 918853a27d fixed mysql not supported declarations 2008-05-14 18:16:13 +00:00
Lele Gaifax 623adee1c4 Support Firebird 2.0+ RETURNING 2008-05-14 15:31:29 +00:00
Lele Gaifax 1524330c10 Whitespace 2008-05-14 15:19:20 +00:00
Mike Bayer ac934fc533 - renamed query.slice_() to query.slice()
- pulled out DeclarativeMeta.__init__ into its own function, added instrument_declarative()
which will do the "declarative" thing to any class independent of its lineage (for ctheune)
- added "cls" kwarg to declarative_base() allowing user-defined base class for declarative base [ticket:1042]
2008-05-13 20:35:41 +00:00
Mike Bayer 17684a1b81 - LIMIT/OFFSET of zero is detected within compiler and is counted
- Query.__getitem__ now returns list/scalar in all cases, not generative (#1035)
- added Query.slice_() which provides the simple "limit/offset from a positive range" operation,
we can rename this to range_()/section()/_something_private_because_users_shouldnt_do_this() as needed
2008-05-13 19:55:49 +00:00
Mike Bayer 1b6306c69e - fixed propagation of operate() for aliased relation descriptors
- ColumnEntity gets a selectable
2008-05-13 19:52:15 +00:00
Jason Kirtland 72dc624bb6 Removed declared_synonym(), pep-8 clean ups. 2008-05-13 18:14:28 +00:00
Jason Kirtland 6524df9214 Removed: all legacy users migrated. 2008-05-13 17:44:49 +00:00
Jason Kirtland b97c8f2d60 - Reworked test/orm/mapper
- Exposed some uncovered (and broken) functionality
- Fixed [ticket:1038]
2008-05-13 16:39:47 +00:00
Lele Gaifax 559af99865 Tag PKs with test_needs_autoincrement on a few test 2008-05-13 09:40:44 +00:00
Lele Gaifax a11b5fc176 Support for under Firebird 2008-05-13 09:31:30 +00:00
Lele Gaifax 78c9603af4 Tag some tests that fail under Firebird 2008-05-13 09:06:27 +00:00
Mike Bayer b0fd5ac899 scaled back the equivalents determined in _equivalent_columns to just current polymorphic_union
behavior, fixes [ticket:1041]
2008-05-12 22:44:04 +00:00
Mike Bayer e02114ce49 - clause adaption hits _raw_columns of a select() (though no ORM tests need this feature currently)
- broke up adapter chaining in eagerload, erroneous "wrapping" in row_decorator.  column_property() subqueries are now affected only by the ORMAdapter for that mapper.  fixes [ticket:1037], and may possibly impact some of [ticket:949]
2008-05-12 16:15:28 +00:00
Lele Gaifax 4b6e1a40e5 Check for the presence of the Firebird generator, when creating/dropping a sequence 2008-05-12 15:33:55 +00:00
Lele Gaifax b7d0832968 Add another exception case to Firebird' is_disconnect() 2008-05-12 13:51:14 +00:00
Lele Gaifax 6d27d85297 Typo 2008-05-12 10:19:47 +00:00
Lele Gaifax 52c965a188 Tagged two tests that fail under Firebird 2008-05-12 10:10:21 +00:00
Lele Gaifax 5200cd16f5 Use a BLOB when asked for a [VAR]CHAR without a length under Firebird 2008-05-12 10:09:16 +00:00
Lele Gaifax 973bc8831c Added explicit sequences on a few primary keys and minor fixes wrt Firebird 2008-05-12 09:33:16 +00:00
Mike Bayer 426bf407e0 tweak 2008-05-10 23:15:13 +00:00
Mike Bayer 81860dc0a4 another approach 2008-05-10 22:15:47 +00:00
Mike Bayer 526db1d944 cant reproduce buildbot's memory profiling, random fix attempt 2008-05-10 21:36:21 +00:00
Mike Bayer 0490fbc666 added blurb on session.rollback() 2008-05-10 20:00:10 +00:00
Mike Bayer 92fbce57a3 py2.4 seems to have different memory behavior than 2.5, test for both "adjusting down" as well as "flatline" 2008-05-10 19:27:05 +00:00
Mike Bayer 8413454b50 - removed all the order by's that no longer apply.
- realized about declarative that foobar: relation("SomeFutureClass") is not very useful for collections since
we can't set "order_by" there.
2008-05-10 19:19:47 +00:00
Mike Bayer 9d700bc60c correcting dataload profiles for various tests 2008-05-10 18:38:20 +00:00
Mike Bayer 2e2eb9bdb5 bizarre duplicate keyword seems to not raise in py2.5 2008-05-10 17:44:54 +00:00
Mike Bayer 0ca037c65e merged r4720 from 04 branch for [ticket:1036] 2008-05-10 17:42:09 +00:00
Lele Gaifax 5a77974f7d Fix typo in the ORM tutorial 2008-05-10 09:02:55 +00:00
Jason Kirtland a27deecfef Reworked & stripped. 2008-05-10 01:59:07 +00:00
Mike Bayer 77a707d629 order by doc 2008-05-10 00:54:17 +00:00
Mike Bayer 333a163ac2 edits 2008-05-10 00:31:35 +00:00
Mike Bayer 9fcc995b9d removed deprecated plugins docs 2008-05-10 00:31:09 +00:00
Mike Bayer bd8514e80b backref() function uses primaryjoin/secondaryjoin of the parent relation() if not otherwise specified, removing the frequently annoying need to specify primaryjoin twice. 2008-05-10 00:26:28 +00:00
Jason Kirtland 2de2900e59 Chipping away at remaining cruft. 2008-05-10 00:05:03 +00:00
Mike Bayer d3621ae961 - fixed a fairly critical bug in clause adaption/corresponding column in conjunction with annotations
- implicit order by is removed, modified many tests to explicitly set ordering, probably many more to go
once it hits the buildbot.
2008-05-09 23:58:30 +00:00
Gaëtan de Menten 6935add67e add target_fullname as a public property for _get_colspec 2008-05-09 20:49:32 +00:00
Jason Kirtland e41c0f4107 Test suite modernization in progress. Big changes:
- @unsupported now only accepts a single target and demands a reason
   for not running the test.
 - @exclude also demands an exclusion reason
 - Greatly expanded @testing.requires.<feature>, eliminating many
   decorators in the suite and signficantly easing integration of
   multi-driver support.
 - New ORM test base class, and a featureful base for mapped tests
 - Usage of 'global' for shared setup going away, * imports as well
2008-05-09 20:26:09 +00:00
Mike Bayer a2122a89f6 more order bys... 2008-05-09 20:04:29 +00:00
Mike Bayer d3b4e624a1 some tweaks to help MySQL 2008-05-09 19:42:32 +00:00
Mike Bayer 6840fc6bb6 - more portable tests for eager/inheritance joins
- bumped 2.4 call count for profile test_select
- don't need initialize_properties() during reentrant compile() call (for now)
2008-05-09 19:20:49 +00:00
Mike Bayer 84ec085d47 MSText no longer implicitly creates TEXT for string with no length
(this actually allows CAST (foo, VARCHAR) to render too)
2008-05-09 19:00:55 +00:00
Mike Bayer cbabd91c21 added query.subquery() as shorthand for query.statement.alias() 2008-05-09 18:57:40 +00:00
Mike Bayer 0bbb0e9b57 identified case where pending upon commit() is needed; since attribute rollback functionality is gone its safe to revert to this 2008-05-09 18:43:05 +00:00
Ants Aasma 54296c9628 move the definition of sessions public methods closer to the source 2008-05-09 17:50:38 +00:00
Mike Bayer 42bfd060a0 added "add", "add_all", "expire_all" to SS 2008-05-09 17:42:24 +00:00
Mike Bayer da44a7ac56 tweak 2008-05-09 17:28:43 +00:00
Mike Bayer 497b962f73 need delete-orphan 2008-05-09 17:21:10 +00:00
Mike Bayer 6dffbb2571 - warnings about Query invalid operations become InvalidRequestErrors
- __no_criterion() checks for more pre-existing conditions
- helpful note in 0.5 svn readme
2008-05-09 17:05:13 +00:00
Mike Bayer 4a6afd469f r4695 merged to trunk; trunk now becomes 0.5.
0.4 development continues at /sqlalchemy/branches/rel_0_4
2008-05-09 16:34:10 +00:00
Mike Bayer 46b7c9dc57 doc update on quote 2008-05-08 21:29:07 +00:00
Mike Bayer db4449455f remove **kwargs from execute(), scalar(), connection(), and get_bind(). document all args, [ticket:1028] 2008-05-08 00:47:23 +00:00
Mike Bayer a5290b0d0f - backported 0.5's contains_eager() behavior such that rendering of eager clauses are disabled. workaround here is compatible with 0.5 but not compatible with the little-known "decorator" argument to contains_eager() (which was also removed in 0.5). Doesn't remove any existing 0.4 functionality. 2008-05-08 00:19:06 +00:00
Mike Bayer 30f55a5016 - added an example dynamic_dict/dynamic_dict.py, illustrating
a simple way to place dictionary behavior on top of
a dynamic_loader.
2008-05-07 14:48:04 +00:00
Mike Bayer e435591259 - Fixed "concatenate tuple" bug which could occur with
Query.order_by() if clause adaption had taken place.
[ticket:1027]
2008-05-07 03:48:14 +00:00
Mike Bayer f0be8f1264 Query.select() wont call filter() if arg is None 2008-05-06 19:06:43 +00:00
Mike Bayer b9e1a24898 fix foreign_keys example 2008-05-06 16:06:22 +00:00
Jason Kirtland fc2c8d68b7 Added missing argument check on CheckConstraint 2008-05-06 14:40:24 +00:00
Mike Bayer 538861143f - _Label adds itself to the proxy collection so that it works in correspoinding column. fixes some eager load with column_property bugs.
- this partially fixes some issues in [ticket:1022] but leaving the "unlabeled" fix for 0.5 for now
2008-05-06 00:55:49 +00:00
Mike Bayer a9ff52f18b - added "after_begin()" hook to Session
- Session.rollback() will rollback on a prepared session
2008-05-06 00:47:36 +00:00
Jason Kirtland 6cfea9df08 Tidy. 2008-05-06 00:46:00 +00:00
Lele Gaifax ba06967cce Fix typo 2008-05-05 23:08:11 +00:00
Jason Kirtland 586e5be1bb Adjusted inplace-binops on set-based collections and association proxies to
more closely follow builtin (2.4+) set semantics.  Formerly any set duck-type
was accepted, now only types or subtypes of set, frozenset or the collection
type itself are accepted.
2008-05-05 21:33:29 +00:00
Mike Bayer a566964ac8 failing case 2008-05-05 17:50:19 +00:00
Jason Kirtland 71e266f77e Fixed duplicate append event emission on repeated instrumented set.add() operations. 2008-05-05 17:08:00 +00:00
Jason Kirtland 18d0066f8f Update for r4643 2008-05-05 16:53:39 +00:00
Jason Kirtland 1589bc5f25 Renamed rollback_returned to reset_on_return. Future, dialect-aware pools can do better than rollback for this function. 2008-05-05 16:50:58 +00:00
Mike Bayer fd5543b78e - same as [ticket:1019] but repaired the non-labeled use case
[ticket:1022]
2008-05-05 16:46:24 +00:00
Mike Bayer 635e61bdeb - added "rollback_returned" option to Pool which will
disable the rollback() issued when connections are
      returned.  This flag is only safe to use with a database
      which does not support transactions (i.e. MySQL/MyISAM).
2008-05-05 15:52:09 +00:00
Mike Bayer dc5ab50a43 - Column.copy() respects the value of "autoincrement",
fixes usage with Migrate [ticket:1021]
2008-05-05 14:59:07 +00:00
Mike Bayer b69bb282a5 - fixes to the "exists" function involving inheritance (any(), has(),
~contains()); the full target join will be rendered into the
EXISTS clause for relations that link to subclasses.
2008-05-02 19:23:58 +00:00
Jason Kirtland a61dfeeff3 - The collection instrumentation sweep now skips over descriptors that raise AttributeError. 2008-05-02 18:47:05 +00:00
Mike Bayer 99f02b59af - fixed reentrant mapper compile hang when
a declared attribute is used within ForeignKey,
ie. ForeignKey(MyOtherClass.someattribute)
2008-05-02 18:23:16 +00:00
Jason Kirtland 0e3021baf4 - Backported attribute sweep removal (instrumentation) and r4493 from 0.5 2008-05-02 17:46:44 +00:00
Rick Morrison d89b840be9 one-off workaround for mssql + odbc options, user patch 2008-05-02 17:26:38 +00:00
Mike Bayer 5932e8649d - an unfortunate naming conflict
- needed sql import on and()
2008-05-02 01:15:26 +00:00
Mike Bayer e3460573d0 - factored out the logic used by Join to create its join condition
- With declarative, joined table inheritance mappers use a slightly relaxed
function to create the "inherit condition" to the parent
table, so that other foreign keys to not-yet-declared
Table objects don't trigger an error.
2008-05-02 01:02:23 +00:00
Mike Bayer c407a608c3 added some reference tests for the any() situation 2008-05-01 15:17:07 +00:00
Jason Kirtland e7f3f997fe - Fix 2.3 regression from 4598 2008-04-30 02:52:14 +00:00
Mike Bayer 009cfe67c4 - added a feature to eager loading whereby subqueries set
as column_property() with explicit label names (which is not
necessary, btw) will have the label anonymized when
the instance is part of the eager join, to prevent
conflicts with a subquery or column of the same name
on the parent object.  [ticket:1019]
2008-04-30 01:16:05 +00:00
Jason Kirtland f035981a66 And a copy.copy() test for the proxy cache. 2008-04-29 18:28:36 +00:00
Jason Kirtland 629d6dc568 - Refresh the cached proxy if the cache was built for a different instance. 2008-04-29 18:22:23 +00:00
Gaëtan de Menten ee27bfdbb7 added multi-level concrete inheritance test (testing with_polymorphic mapper argument) 2008-04-29 13:35:59 +00:00
Mike Bayer d95b063618 more declarative doc updates 2008-04-28 00:04:05 +00:00
Mike Bayer 032e358970 fix docs for declarative 2008-04-27 23:37:02 +00:00
Lele Gaifax 2ffe47e32b Savepoints are supported under Firebird 2008-04-27 08:54:36 +00:00
Mike Bayer 3560379ff7 fix order by for MySQL environment 2008-04-26 20:18:31 +00:00
Mike Bayer f3bcc15c5c - improved behavior of text() expressions when used as
FROM clauses, such as select().select_from(text("sometext"))
[ticket:1014]
- removed _TextFromClause; _TextClause just adds necessary FromClause descriptors
at the class level
2008-04-26 16:34:14 +00:00
Mike Bayer b089e8615b - refined mapper._save_obj() which was unnecessarily calling
__ne__() on scalar values during flush [ticket:1015]
2008-04-26 16:13:49 +00:00
Jason Kirtland 49895a84fd Expanded --noncomparable to cover all comparision ops 2008-04-25 20:44:02 +00:00
Gaëtan de Menten b24f982a90 typo 2008-04-25 12:31:47 +00:00
Jason Kirtland c2fd90a4d4 Update docstring [ticket:873] 2008-04-22 20:04:51 +00:00
Jason Kirtland 9feff14932 Explicit test of .autoflush(False) to avoid issues with save_on_init=True [ticket:869] 2008-04-22 19:57:13 +00:00
Jason Kirtland f472959846 flush(objects=[]) is a no-op [ticket:928] 2008-04-22 19:29:56 +00:00
Mike Bayer ab5adea360 - fixed Class.collection==None for m2m relationships
[ticket:4213]
2008-04-22 15:05:10 +00:00
Mike Bayer 4cf26a9e1b - restored usage of append_result() extension method for primary
query rows, when the extension is present and only a single-
entity result is being returned.
2008-04-18 14:49:21 +00:00
Rick Morrison c94ccaf932 Added 'odbc_options' keyword to the MSSQL dialect. Allows a partial ODBC connection string to be passed through to the connection string generator. 2008-04-17 19:07:12 +00:00
Jason Kirtland 2479711370 - Support for COLLATE: collate(expr, col) and expr.collate(col) 2008-04-16 00:53:21 +00:00
Mike Bayer f5126ab3a1 - simplified __create_lazy_clause to make better usage of the new local/remote pairs collection
- corrected the direction of local/remote pairs for manytoone
- added new tests which demonstrate lazyloading working when the bind param is embedded inside of a SQL function,
when _local_remote_pairs argument is used; fixes the viewonly version of [ticket:610]
- removed needless kwargs check from visitors.traverse
2008-04-14 18:23:59 +00:00
Mike Bayer 6a98ffb059 added info about _local_remote_pairs to error message 2008-04-14 15:54:23 +00:00
Mike Bayer 1d1484b210 - added experimental relation() flag to help with primaryjoins
across functions, etc., _local_remote_pairs=[tuples].
This complements a complex primaryjoin condition allowing
you to provide the individual column pairs which comprise
the relation's local and remote sides.
2008-04-14 15:49:39 +00:00
Jason Kirtland 2a3f58b5b8 Pass connection to get_default_schema_name 2008-04-13 16:21:17 +00:00
Lele Gaifax c79342ef95 Firebird 2 has a SUBSTRING() builtin, expose it thru a function 2008-04-11 22:14:34 +00:00
Mike Bayer ce109f7adf - re-established viewonly relation() configurations that
join across multiple tables.
2008-04-11 15:58:18 +00:00
Rick Morrison a27cc907a5 Add a new 'odbc_autotranslate' engine/dburi kwd parm to the MSSQL pyodbc dialect; string kwd contents will be passed through to ODBC connection string.
[ticket:1005]
2008-04-08 19:09:33 +00:00
Matt Harrison 86d088835a remove monetdb typo 2008-04-07 22:48:39 +00:00
Matt Harrison fdb9440d2e refactor of default_paramstyle, use paramstyle argument on Dialect to change 2008-04-07 22:42:28 +00:00
Jason Kirtland 28ecb49eac - Avoid cProfile on 2.4 (available via lsprof?) 2008-04-07 22:17:42 +00:00
Mike Bayer 5e2170f56a remove unneeded compile assertion test, doesn't work on MySQL 2008-04-07 21:53:13 +00:00
Mike Bayer 9130f64522 *headslap* those mutators cant mutate the collections except for never-generated selectables; its not worth it 2008-04-07 20:44:39 +00:00
Mike Bayer 5080a17409 - removed ancient assertion that mapped selectables require
"alias names" - the mapper creates its own alias now if
none is present.  Though in this case you need to use
the class, not the mapped selectable, as the source of
column attributes - so a warning is still issued.
2008-04-07 19:49:41 +00:00
Mike Bayer c7587e4d6c some fk fixes for PG 2008-04-07 01:15:37 +00:00
Mike Bayer e3b2305d67 - merged -r4458:4466 of query_columns branch
- this branch changes query.values() to immediately return an iterator, adds a new "aliased" construct which will be the primary method to get at aliased columns when using values()
- tentative ORM versions of _join and _outerjoin are not yet public, would like to integrate with Query better (work continues in the branch)
- lots of fixes to expressions regarding cloning and correlation.  Some apparent ORM bug-workarounds removed.
- to fix a recursion issue with anonymous identifiers, bind parameters generated against columns now just use the name of the column instead of the tablename_columnname label (plus the unique integer counter).  this way expensive recursive schemes aren't needed for the anon identifier logic.   This, as usual, impacted a ton of compiler unit tests which needed a search-n-replace for the new bind names.
2008-04-07 01:12:44 +00:00
Mike Bayer 5b3cddc48e refined "local_remote_pairs" a bit to account for the same columns repeated multiple times 2008-04-04 20:21:03 +00:00
Jason Kirtland afd8431d4d - Pool listeners may now be specified as a duck-type of PoolListener or a dict of callables, your choice. 2008-04-04 19:07:30 +00:00
Mike Bayer de209ded31 factored down exportable_columns/flatten_cols/proxy_column/oid_etc_yada down to a single, streamlined "_populate_column_collection" method called for all selectables 2008-04-04 18:41:08 +00:00
Mike Bayer 1a68456795 fixed union() bug whereby oid_column would not be available if no oid_column in embedded selects 2008-04-04 16:06:58 +00:00
Mike Bayer c4eeabc60b bump 2008-04-04 02:11:56 +00:00
Jason Kirtland a83d216ab6 Yep. 2008-04-04 00:58:11 +00:00
Mike Bayer 9d3ab4aaee - ReST fixes
- reverted strange jeklike symbol syntax
2008-04-04 00:49:13 +00:00
Mike Bayer ea4900dcd6 - changed the name to "local/remote pairs"
- added closing ' to symbol str()  (I'm assuming it's supposed to be that way)
2008-04-04 00:31:00 +00:00
Mike Bayer 1dbed0b2b4 - merged sync_simplify branch
- The methodology behind "primaryjoin"/"secondaryjoin" has
been refactored.  Behavior should be slightly more
intelligent, primarily in terms of error messages which
have been pared down to be more readable.  In a slight
number of scenarios it can better resolve the correct
foreign key than before.
- moved collections unit test from relationships.py to collection.py
- PropertyLoader now has "synchronize_pairs" and "equated_pairs"
collections which allow easy access to the source/destination
parent/child relation between columns (might change names)
- factored out ClauseSynchronizer (finally)
- added many more tests for priamryjoin/secondaryjoin
error checks
2008-04-04 00:21:28 +00:00
Jason Kirtland 9dd01e52e2 - microcleanup 2008-04-03 17:32:22 +00:00
Jason Kirtland d78f39d005 - Experimental: prefer cProfile over hotspot for 2.5+
- The latest skirmish in the battle against zoomark and sanity:
  3rd party code is factored out in the function call count canary tests
2008-04-03 17:08:08 +00:00
Ants Aasma ca1ad4cbb9 A couple of usage examples for the case statement 2008-04-03 16:54:26 +00:00
Mike Bayer abb10856dc - case() interprets the "THEN" expressions
as values by default, meaning case([(x==y, "foo")]) will
interpret "foo" as a bound value, not a SQL expression.
use text(expr) for literal SQL expressions in this case.
For the criterion itself, these may be literal strings
only if the "value" keyword is present, otherwise SA
will force explicit usage of either text() or literal().
2008-04-03 16:34:03 +00:00
Mike Bayer a27d6be28a some cleanup, some method privating, some pep8, fixed up _col_aggregate and merged
its functionality with _count()
2008-04-03 16:25:47 +00:00
Ants Aasma 921efb250c The case() function now also takes a dictionary as its whens parameter. But beware that it doesn't escape literals, use the literal construct for that. 2008-04-03 14:08:22 +00:00
Mike Bayer f899d79005 - Added some convenience descriptors to Query:
query.statement returns the full SELECT construct,
query.whereclause returns just the WHERE part of the
SELECT construct.
2008-04-03 13:12:42 +00:00
Rick Morrison 6b5051845b Added a new 'max_identifier_length' keyword to the mssql_pyodbc dialect 2008-04-02 23:03:00 +00:00
Ants Aasma d17cb855bf Cascade traversal algorithm converted from recursive to iterative to support deep object graphs. 2008-04-02 22:45:43 +00:00
Mike Bayer bf77ddaabb - Got PG server side cursors back into shape, added fixed
unit tests as part of the default test suite.  Added
better uniqueness to the cursor ID [ticket:1001]
- update().values() and insert().values() take keyword
arguments.
2008-04-02 22:33:50 +00:00
Jason Kirtland 0359a6a13d - Re-tuned call counts for 2.3 through 2.5. 2008-04-02 18:13:53 +00:00
Jason Kirtland d9dce78a3b - Run profiling tests first. 2008-04-02 17:55:11 +00:00
Mike Bayer f16c41b00b fixed OracleRaw type adaptation [ticket:902] 2008-04-02 17:34:24 +00:00
Mike Bayer 38b2869bb8 some fixes to the MS-SQL aliasing so that result_map is properly populated 2008-04-02 16:35:06 +00:00
Mike Bayer fef66e5c5b doh 2008-04-02 16:15:57 +00:00
Mike Bayer cfd7807838 some test fixup for oracle 2008-04-02 16:12:07 +00:00
Mike Bayer 52b69d0e25 reduced 2.4 callcounts... 2008-04-02 15:22:33 +00:00
Mike Bayer ac7d092ef0 slight function call reduction 2008-04-02 15:07:06 +00:00
Jason Kirtland d8d3637389 - Assorted flakes. 2008-04-02 11:51:06 +00:00
Jason Kirtland f12969a4d8 - Revamped the Connection memoize decorator a bit, moved to engine
- MySQL character set caching is more aggressive but will invalidate the cache if a SET is issued.
- MySQL connection memos are namespaced: info[('mysql', 'server_variable')]
2008-04-02 11:39:26 +00:00
Jason Kirtland a000007543 - More 2.4 generator squashing. 2008-04-02 10:39:06 +00:00
Mike Bayer c08c6c2185 continue attempting to get proper count for pybot on 2.5, ensure order_by for oracle query 2008-04-02 02:42:29 +00:00
Mike Bayer 61d8644320 - added verbose activity to profiling.function_call_count
- simplified oracle non-ansi join generation, removed hooks from base compiler
- removed join() call from _label generation, fixed repeat label gen
2008-04-01 22:36:40 +00:00
Jason Kirtland f01e13ca72 - More zzzeek enablement. 2008-04-01 18:33:03 +00:00
Jason Kirtland b93eb67f4b - Squashed 2.4 generators. 2008-04-01 18:31:20 +00:00
Mike Bayer 8f2ff2a648 added an order by to fix potential mysql test failure 2008-04-01 18:11:13 +00:00
Mike Bayer b041ff1e62 seems like the recent itertools add to select()._get_display_froms() adds overhead in 2.4? not sure why 2008-04-01 18:08:42 +00:00
Mike Bayer 7ae89c28f0 fix up some unit tests 2008-04-01 17:46:36 +00:00
Mike Bayer ad231da3b8 - merge() may actually work now, though we've heard that before...
- merge() uses the priamry key attributes on the object if _instance_key not present.  so merging works for instances that dont have an instnace_key, will still issue UPDATE for existing rows.
- improved collection behavior for merge() - will remove elements from a destination collection that are not in the source.
- fixed naive set-mutation issue in Select._get_display_froms
- simplified fixtures.Base a bit
2008-04-01 17:13:09 +00:00
Jason Kirtland 1e0a91fe81 - Tighten up r4399 _set_iterable docs 2008-04-01 16:49:55 +00:00
Jason Kirtland 73a4e9481d - Light collections refactor, added public collections.bulk_replace.
- Collection attribs gain some private load-from-iterable flexiblity.
2008-04-01 16:38:23 +00:00
Mike Bayer 6f65b002c8 weird, old cruft 2008-04-01 04:51:02 +00:00
Mike Bayer b371c0c9dd - removed redundant get_history() method
- the little bit at the bottom of _sort_circular_dependencies is absolutely covered by test/orm/cycles.py !  removing it breaks the test as run on PG.
2008-04-01 03:16:47 +00:00
Jason Kirtland 85497ae58d C-u 66 C-x f M-q 2008-03-31 22:45:34 +00:00
Rick Morrison 58b3f3aa9a MSSQL adjustments to pyodbc connection string building 2008-03-31 21:54:32 +00:00
Rick Morrison 128ee627e1 Add a new 'driver' keyword to the MSSQL pyodbc Dialect.
Refresh items that were recently reverted by another checkin
2008-03-31 17:19:32 +00:00
Mike Bayer 0f8896e6fa - reverted previous "strings instead of tuples" change due to more specific test results showing tuples faster
- changed cache decorator call on default_schema_name call to a connection.info specific one
2008-03-30 23:30:31 +00:00
Jason Kirtland 5291acd597 *whistle* 2008-03-30 23:24:05 +00:00
Jason Kirtland 833696aa0a - Removed cache decorator. 2008-03-30 22:37:48 +00:00
Mike Bayer f11e9585b8 some cache decorator calls... 2008-03-30 22:15:19 +00:00
Mike Bayer bd0bcc9f57 using concatenated strings as keys in generated_ids collection; they hash slightly faster than tuples 2008-03-30 22:01:15 +00:00
Mike Bayer 7512b5e548 - schema-qualified tables now will place the schemaname
ahead of the tablename in all column expressions as well
as when generating column labels.  This prevents cross-
schema name collisions in all cases [ticket:999]
- the "use_schema" argument to compiler.visit_column() is removed.  It uses
schema in all cases now.
- added a new test to the PG dialect to test roundtrip insert/update/delete/select
statements with full schema qualification
2008-03-30 21:48:19 +00:00
Rick Morrison c096aeefe0 MSSQL fixes for tickets 979, 916, 884 2008-03-30 20:32:17 +00:00
Mike Bayer 6aaa74e283 - added _from_self()
- changelog authoring
2008-03-30 18:05:33 +00:00
Mike Bayer 0f0b9552fb - rearranged LoaderStrategies a bit
- removed awareness of "dynamic" from attributes and replaced with "impl_class"
- moved DynaLoader into dynamic.py
- removed create_strategy() method from StrategizedProperty; they set up
'strategy_class' so that StrategizedProperty treats the default the same
as the optional loaders
2008-03-30 16:03:23 +00:00
Mike Bayer 6a1970e978 turned starargs conversion to a decorator, per jek's advice. select().order_by()/group_by() already take *args. 2008-03-29 16:31:31 +00:00
Jason Kirtland 4fef0a23a1 - Added PendingDeprecationWarning support
- Deprecation decorator is now a real decorator
2008-03-29 15:54:50 +00:00
Mike Bayer 99ed392267 - declarative_base() takes optional kwarg "mapper", which
is any callable/class/method that produces a mapper,
such as declarative_base(mapper=scopedsession.mapper).
This property can also be set on individual declarative
classes using the "__mapper_cls__" property.
2008-03-29 14:41:41 +00:00
Mike Bayer c4955c05a3 - merged with_polymorphic branch, which was merged with query_columns branch
- removes everything to do with select_table, which remains as a keyword argument synonymous with
with_polymorphic=('*', select_table).
- all "polymorphic" selectables find their way to Query by way of _set_select_from() now, so that
all joins/aliasing/eager loads/etc. is handled consistently.  Mapper has methods for producing
polymorphic selectables so that Query and eagerloaders alike can get to them.
- row aliasing simplified, so that they don't need to nest.  they only need the source selectable
and adapt to whatever incoming columns they get.
- Query is more egalitarian about mappers/columns now.  Still has a strong sense of "entity zero",
but also introduces new unpublished/experimental _values() method which sets up a columns-only query.
- Query.order_by() and Query.group_by() take *args now (also still take a list, will likely deprecate
in 0.5).  May want to do this for select() as well.
- the existing "check for False discriminiator" "fix" was not working completely, added coverage
- orphan detection was broken when the target object was a subclass of the mapper with the orphaned
relation, fixed that too.
2008-03-29 00:00:49 +00:00
Mike Bayer 30020880d9 - can now allow selects which correlate all FROM clauses
and have no FROM themselves.  These are typically
used in a scalar context, i.e. SELECT x, (SELECT x WHERE y)
FROM table.  Requires explicit correlate() call.
2008-03-28 15:55:26 +00:00
Jason Kirtland 784ff76cef - Notes for r4338 2008-03-25 17:58:38 +00:00
Mike Bayer fc2cf22038 - fixed SQL function truncation of trailing underscores
[ticket:996]
2008-03-25 17:25:20 +00:00
Jason Kirtland 92a5df7753 - Added generic func.random (non-standard SQL) 2008-03-25 16:51:29 +00:00
Mike Bayer df1000839b a few more tweaks 2008-03-25 00:02:45 +00:00
Mike Bayer bade0092d1 removed AbstractClauseProcessor, merged its copy-and-visit behavior into ClauseVisitor 2008-03-24 23:55:21 +00:00
Mike Bayer dde6466660 - already-compiled mappers will still trigger compiles of
other uncompiled mappers when used [ticket:995]
2008-03-23 16:36:47 +00:00
Mike Bayer 8ef1bded27 added nicer error message to dependent class not found 2008-03-22 22:30:08 +00:00
Mike Bayer 82198afee9 - the "owner" keyword on Table is now deprecated, and is
exactly synonymous with the "schema" keyword.  Tables
      can now be reflected with alternate "owner" attributes,
      explicitly stated on the Table object or not using
      "schema".

    - all of the "magic" searching for synonyms, DBLINKs etc.
      during table reflection
      are disabled by default unless you specify
      "oracle_resolve_synonyms=True" on the Table object.
      Resolving synonyms necessarily leads to some messy
      guessing which we'd rather leave off by default.
      When the flag is set, tables and related tables
      will be resolved against synonyms in all cases, meaning
      if a synonym exists for a particular table, reflection
      will use it when reflecting related tables.  This is
      stickier behavior than before which is why it's
      off by default.
2008-03-22 19:30:42 +00:00
Mike Bayer 2cff3ad9f8 - inheritance in declarative can be disabled when sending
"inherits=None" to __mapper_args__.
2008-03-22 18:05:46 +00:00
Mike Bayer 98f212667e reverted r4315 - a basic test works the way it was and fails with this change 2008-03-22 15:06:28 +00:00
Mike Bayer 6bc2784a4d - made some fixes to the "from_joinpoint" argument to
query.join() so that if the previous join was aliased
and this one isn't, the join still happens successfully.
2008-03-21 17:21:41 +00:00
Mike Bayer 55192da899 - adjusted the definition of "self-referential" to be
any two mappers with a common parent (this affects
whether or not aliased=True is required when joining
with Query).
2008-03-21 16:43:51 +00:00
Catherine Devlin 6d5cb2522b Undoing patch #994, for now; more testing needed. Sorry. Also modifying test for query equivalence to account for underscoring of bind variables. 2008-03-20 16:48:46 +00:00
Catherine Devlin 50206ec2ad adding zzzeek's patch from ticket #994, which fixed virtually all remaining broken unit tests in the Oracle module 2008-03-20 02:47:46 +00:00
Catherine Devlin 869f9e0a2a bugfix: preserving remote_owner during reflecttable setup of referential integrity 2008-03-20 00:44:01 +00:00
Catherine Devlin 04b81eecba added a runtime-incrementing counter for default primary keys to testlib/schema for Oracle 2008-03-19 22:53:31 +00:00
Mike Bayer 8d0c5672f0 added escape kw arg to contains(), startswith(), endswith(), [ticket:791] 2008-03-19 20:25:51 +00:00
Mike Bayer 0cc04e6e1b - like() and ilike() take an optional keyword argument
"escape=<somestring>", which is set as the escape character
using the syntax "x LIKE y ESCAPE '<somestring>'"
[ticket:993]
2008-03-19 19:35:42 +00:00
Jason Kirtland a86dc8cbac - symbols now depickle properly
- fixed some symbol __new__ abuse
2008-03-19 18:02:47 +00:00
Gaëtan de Menten fb9f459d71 typo 2008-03-19 12:48:09 +00:00
Mike Bayer 6b387602d6 test not supported on sqlite 2008-03-18 23:50:30 +00:00
Mike Bayer 7d33391e29 some fixup to one-to-many delete cascade 2008-03-18 23:35:47 +00:00
Mike Bayer 13475937e4 - fixed/added coverage for various cascade scenarios
- added coverage for some extra cases in dynamic relations
- removed some unused methods from unitofwork
2008-03-18 20:59:52 +00:00
Mike Bayer b9a67d1458 - added support for declarative deferred(Column(...))
- changed "instrument" argument on synonym() to "descriptor", for consistency with comparable_proeprty()
2008-03-18 17:42:07 +00:00
Jason Kirtland 031c500ff4 - Column._set_parent will complete the key==name contract for instances constructed anonymously 2008-03-18 03:07:54 +00:00
Jason Kirtland 67fc7abe6d - reST fixes 2008-03-18 03:05:34 +00:00
Mike Bayer 3d5a852b1d mapper double checks that columns in _compile_property are in the _cols_by_table collection 2008-03-18 02:38:11 +00:00
Jason Kirtland c35a717140 - Start coverage for Class.prop = Column(), promote nameless Columns 2008-03-18 02:16:15 +00:00
Jason Kirtland c462e42dfd - Declarative will complete setup for Columns lacking names, allows
a more DRY syntax.

    class Foo(Base):
        __tablename__ = 'foos'
        id = Column(Integer, primary_key=True)
2008-03-18 00:25:55 +00:00
Mike Bayer ad4e425676 - fixed order_by calculation in Query to properly alias
mapper-config'ed order_by when using select_from()
2008-03-18 00:21:11 +00:00
Jason Kirtland f979c19841 - 'name' is no longer a require constructor argument for Column(). It (and .key) may now be deferred until the Column is added to a Table. 2008-03-18 00:15:34 +00:00
Jason Kirtland 22197ca9c5 - Declarative gains @synonym_for and @comparable_using decorators 2008-03-17 22:55:43 +00:00
Jason Kirtland c37ed5cbb3 - Added comparable_property(), adds query Comparator behavior to regular, unmanaged Python properties
- Some aspects of MapperProperty initialization are streteched pretty thin now
  and need a refactor; will proceed with these on the user_defined_state branch
2008-03-17 22:06:49 +00:00
Jason Kirtland e17edb3240 - trailing whitespace... 2008-03-17 21:59:02 +00:00
Jason Kirtland dbf45bd4a6 - DEFAULT VALUES again. 2008-03-17 21:58:31 +00:00
Mike Bayer 190436e58d - fixed "cascade delete" operation of dynamic relations,
which had only been implemented for foreign-key nulling
behavior in 0.4.2 and not actual cascading deletes
[ticket:895]
2008-03-16 23:49:55 +00:00
Mike Bayer ef07d002d7 fix datatypes #2 2008-03-16 19:10:45 +00:00
Mike Bayer 763575246d fix insert() to have values (supports buildbot's SQLite) 2008-03-16 19:07:22 +00:00
Jason Kirtland 0bb1406bfb - Fixed descriminator col type for poly test 2008-03-16 18:51:43 +00:00
Jason Kirtland 9e15993083 Issue a warning when a declarative detects a likely trailing comma: foo = Column(foo), 2008-03-15 23:13:35 +00:00
Mike Bayer 288f9d53e3 - the "synonym" function is now directly usable with
"declarative".  Pass in the decorated property using
the "instrument" keyword argument, e.g.:
somekey = synonym('_somekey', instrument=property(g, s))
- declared_synonym deprecated
2008-03-15 20:18:54 +00:00
Ants Aasma e15813837f Session.execute can now find binds from metadata 2008-03-12 21:40:11 +00:00
Mike Bayer a4003c0883 - fixed bug which was preventing synonym() attributes
from being used with inheritance
2008-03-12 21:32:32 +00:00
Mike Bayer ec89c5ae51 typo 2008-03-12 21:04:19 +00:00
Jason Kirtland dff5e27c0d - fixed missing import [ticket:989] 2008-03-12 14:17:15 +00:00
Jonathan Ellis 2396541930 add relate(), entity() methods 2008-03-12 12:11:12 +00:00
Mike Bayer 100338f436 - fixed/covered case when using a False value as a
polymorphic discriminator
2008-03-12 03:02:28 +00:00
Mike Bayer d1326cf549 - when attributes are expired on a pending instance, an
error will not be raised when the "refresh" action
is triggered and returns no result
2008-03-12 01:52:36 +00:00
Jason Kirtland 713279c53d - Retroactive textmate damage control 2008-03-12 00:09:23 +00:00
Jason Kirtland f58037b0a5 Bump. 2008-03-12 00:07:37 +00:00
Mike Bayer 07d89b09bb more edits 2008-03-12 00:02:41 +00:00
Mike Bayer 3bcf4e69b9 fix a typo.... 2008-03-11 23:52:46 +00:00
Jason Kirtland 35902d0c2a (Whoops,) 2008-03-11 23:27:30 +00:00
Jason Kirtland 073880269e - Take broken mysql 4.1 column defaulting into account. 2008-03-11 23:22:44 +00:00
Jason Kirtland 2d1acf1abc - Don't create implicit DDL column defaults 2008-03-11 23:16:10 +00:00
Jason Kirtland 9f3f2accc8 - increased assert_tabels_equal failure verbosity 2008-03-11 23:11:06 +00:00
Mike Bayer 0cb4ceeb82 filled in some of the types documentation 2008-03-11 20:03:14 +00:00
Mike Bayer 39355cfef7 updated SQL output, fixed String/Text type 2008-03-11 19:51:48 +00:00
Mike Bayer d910cce949 reflection tests require foreign key reflection support 2008-03-11 19:15:04 +00:00
Ants Aasma 50d476ebca - fix expunging of orphans with more than one parent
- move flush error for orphans from Mapper to UnitOfWork
2008-03-10 20:49:27 +00:00
Mike Bayer 80d52d7a24 remove redundant test_rekey() test method 2008-03-10 20:19:38 +00:00
Jason Kirtland 0ad1aaa388 - Test autoload with a FK override 2008-03-10 19:21:49 +00:00
Jason Kirtland 6eeb43e5c2 - Added a primaryjoin= test 2008-03-10 18:40:36 +00:00
Jason Kirtland f405880ca3 eh, that __autoload_with__ idea was half baked. 2008-03-10 18:39:12 +00:00
Jason Kirtland ac13a8445b - Added __autoload__ = True for declarative
- declarative Base.__init__ is pickier about its kwargs
2008-03-10 18:32:07 +00:00
Mike Bayer 79004f1ede removed the "__main__" code from below 2008-03-10 17:15:51 +00:00
Mike Bayer 88a8cc0c9e - a new super-small "declarative" extension has been added,
which allows Table and mapper() configuration to take place
inline underneath a class declaration.  This extension differs
from ActiveMapper and Elixir in that it does not redefine
any SQLAlchemy semantics at all; literal Column, Table
and relation() constructs are used to define the class
behavior and table definition.
2008-03-10 17:14:08 +00:00
Mike Bayer 9d3216d3f3 - relation() can accept a callable for its first argument,
which returns the class to be related.  This is in place
to assist declarative packages to define relations without
classes yet being in place.
2008-03-10 00:59:51 +00:00
Mike Bayer 4bd956b50f - dynamic_loader() / lazy="dynamic" now accepts and uses
the order_by parameter in the same way in which it works
with relation().
2008-03-09 22:51:55 +00:00
Mike Bayer 9ea6574841 added sanity test for order_by 2008-03-09 18:00:04 +00:00
Jason Kirtland aa033afeee Added support for vendor-extended INSERT syntax like INSERT DELAYED INTO 2008-03-07 16:56:37 +00:00
Mike Bayer 90a7553b5b weed whacking is not Nones 2008-03-07 03:26:48 +00:00
Mike Bayer f621df6ce6 - moved property._is_self_referential() to be more generalized; returns True for any mapper.isa() relationship between parent and child, and indicates that aliasing should be used for any join/correlation across the relation. allows joins/any()/has() to work with inherited mappers referencing the parent etc.
- the original _is_self_referential() is now _refers_to_parent_table() and is only used during "direction" calculation to indicate the relation is from a single table to itself
2008-03-07 03:16:46 +00:00
Mike Bayer afc0cdfe74 corrected assert_raises to be consistent with existing assertRaises() unittest method 2008-03-06 18:59:23 +00:00
Mike Bayer 4f35e8120f - added assert_raises() to TestBase class
- session.refresh() and session.expire() raise an error when
called on instances which are not persistent within the session
- session._validate_persistent() properly raises an error for false check
2008-03-06 18:44:45 +00:00
Mike Bayer 63b904881b check the isinsert/isupdate flags before calling __process_defaults 2008-03-06 16:54:55 +00:00
Mike Bayer d8e258410e - adjusted generative.py test for revised error message
- mapper with non_primary asserts primary mapper already created
- added any()/instance compare test to query
2008-03-06 16:53:40 +00:00
Jason Kirtland 50155f8c9f Import fixup & trailing whitespace 2008-03-06 14:16:19 +00:00
Jason Kirtland 4f6d0ff71e - Synonyms riding on top of existing descriptors are now full proxies
to those descriptors.
2008-03-06 14:12:22 +00:00
Jason Kirtland 06d55b8e1d - constraint constructor docstring fiesta 2008-03-05 00:46:58 +00:00
Jason Kirtland b536650170 - More docs for r4223 2008-03-04 23:30:37 +00:00
Jason Kirtland e193854f95 - Tweaked error messaging for unbound DDL().execute() 2008-03-04 22:50:14 +00:00
Jason Kirtland 38606681e7 - Gave DDL() statements the same .bind treatment as the DML ones in r4220 2008-03-04 22:47:35 +00:00
Jason Kirtland 5413a207f0 - whitespace/docstring/linewrap freakout 2008-03-04 22:29:59 +00:00
Jason Kirtland 70d8a9c7a6 - Updated exception messaging for r4220 2008-03-04 21:35:15 +00:00
Mike Bayer 793b7c2e6b - added "bind" keyword argument to insert(), update(), delete();
.bind property is settable on those as well as select().
2008-03-04 20:57:32 +00:00
Mike Bayer 41e7542220 unit test for mutable PGArray, thanks to AlexB !!! 2008-03-04 20:14:28 +00:00
Mike Bayer 97e32c995c check for None 2008-03-04 19:41:40 +00:00
Mike Bayer 390c3e4d5c - postgres PGArray is a "mutable" type by default;
when used with the ORM, mutable-style equality/
copy-on-write techniques are used to test for changes.
2008-03-04 19:31:33 +00:00
Mike Bayer cef292c042 fixed negated self-referential m2m contains(), [ticket:987] 2008-03-04 19:26:29 +00:00
Mike Bayer 8e0ea84c33 - fixed bug which was preventing UNIONS from being cloneable,
[ticket:986]
2008-03-04 18:20:09 +00:00
Mike Bayer 2f0a163656 fix markdown bug 2008-03-04 00:40:40 +00:00
Mike Bayer 3175686514 - repaired behavior of == and != operators at the relation()
level when compared against NULL for one-to-one and other
relations [ticket:985]
2008-03-03 17:06:27 +00:00
Gaëtan de Menten 56d18ef597 (very) minor speed optimization to ResultProxy fetchall & fetchmany methods 2008-03-03 15:10:38 +00:00
Mike Bayer 83459e47e7 added dispose() for StaticPool 2008-03-02 17:44:17 +00:00
Mike Bayer 8bd3707031 fix maddening ReST bug 2008-03-02 06:09:45 +00:00
Mike Bayer eb51dfc4d5 document with_polymorphic() 2008-03-02 05:55:05 +00:00
Jason Kirtland afa08cbd06 - Raise a friendly error when assigning an unmapped something (like a string) to a scalar-object attribute 2008-03-02 04:24:47 +00:00
Mike Bayer a8c2322588 - state.commit() and state.commit_all() now reconcile the current dict against expired_attributes
and unset the expired flag for those attributes.  This is partially so that attributes are not
needlessly marked as expired after a two-phase inheritance load.
- fixed bug which was introduced in 0.4.3, whereby loading an
already-persistent instance mapped with joined table inheritance
would trigger a useless "secondary" load from its joined
table, when using the default "select" polymorphic_fetch.
This was due to attributes being marked as expired
during its first load and not getting unmarked from the
previous "secondary" load.  Attributes are now unexpired
based on presence in __dict__ after any load or commit
operation succeeds.
2008-03-01 22:30:02 +00:00
Mike Bayer 8fee8e963d add note about global metadata removed [ticket:983] 2008-03-01 16:23:49 +00:00
Mike Bayer 075eb9076b - fixed bug whereby session.expire() attributes were not
loading on an polymorphically-mapped instance mapped
by a select_table mapper.

- added query.with_polymorphic() - specifies a list
of classes which descend from the base class, which will
be added to the FROM clause of the query.  Allows subclasses
to be used within filter() criterion as well as eagerly loads
the attributes of those subclasses.

- deprecated Query methods apply_sum(), apply_max(), apply_min(),
apply_avg().  Better methodologies are coming....
2008-03-01 01:46:23 +00:00
Mike Bayer bda6f1e06f - setting the relation()-level order by to a column in the
many-to-many "secondary" table will now work with eager
loading, previously the "order by" wasn't aliased against
the secondary table's alias.
2008-02-29 21:54:40 +00:00
Mike Bayer 76e9f646b1 some cleanup of TypeDecorator, moved PickleType / Interval to the newer style for readability 2008-02-28 00:47:43 +00:00
Mike Bayer 9302f2b2fb - postgres TIMESTAMP renders correctly [ticket:981] 2008-02-27 20:23:23 +00:00
Mike Bayer 82edfd0e91 - implemented two-phase API for "threadlocal" engine,
via engine.begin_twophase(), engine.prepare()
[ticket:936]
2008-02-26 19:32:49 +00:00
Mike Bayer 47418e0f87 - added exception wrapping/reconnect support to result set
fetching.  Reconnect works for those databases that
raise a catchable data error during results
(i.e. doesn't work on MySQL) [ticket:978]
2008-02-25 18:32:11 +00:00
Mike Bayer 98d54ac067 silliness reduction 2008-02-24 08:53:26 +00:00
Jason Kirtland b80245d746 - Invalid SQLite connection URLs now raise an error. 2008-02-23 21:59:46 +00:00
Jason Kirtland 5ec76a5bc3 C-u 66 C-x f M-q 2008-02-22 23:52:12 +00:00
Mike Bayer bb6ec17708 - the value of a bindparam() can be a callable, in which
case it's evaluated at statement execution time to
get the value.
- expressions used in filter(), filter_by() and others,
when they make usage of a clause generated from a
relation using the identity of a child object
(e.g. filter(Parent.child==<somechild>)), evaluate
the actual primary key value of <somechild> at
execution time so that the autoflush step of the
Query can complete, thereby populating the PK value
of <somechild> in the case that <somechild> was
pending.
- cleanup of attributes.get_committed_value() to never return
the NO_VALUE value; evaluates to None
2008-02-22 23:17:15 +00:00
Jason Kirtland e203b4dd4e - Converted MAGICCOOKIE=object() to a little symbol implementation to ease object inspection and debugging 2008-02-22 19:03:44 +00:00
Mike Bayer b1c9082c94 er, ok, dont do that (reversed last change). PG relies upon _register_clean for
new PK switch even if no SQL is emitted.
2008-02-21 23:11:30 +00:00
Mike Bayer 6abe14f852 dont treat "listonly" objects as newly clean 2008-02-21 22:12:46 +00:00
Mike Bayer 65da266daa - preventive code against a potential lost-reference
bug in flush()
2008-02-21 21:41:53 +00:00
Mike Bayer 334668d904 - added a new "higher level" operator called "of_type()" -
used in join() as well as with any() and has(), qualifies
the subclass which will be used in filter criterion,
e.g.:

query.filter(Company.employees.of_type(Engineer).
  any(Engineer.name=='foo')),

query.join(Company.employees.of_type(Engineer)).
  filter(Engineer.name=='foo')
2008-02-21 01:01:24 +00:00
Mike Bayer f827e3c0b7 - fixed potential generative bug when the same Query was
used to generate multiple Query objects using join().
2008-02-20 17:09:25 +00:00
Mike Bayer 7f43bc55e0 - can again create aliases of selects against textual
FROM clauses, [ticket:975]
2008-02-19 23:46:14 +00:00
Mike Bayer 3e6e61dbe7 - modernized cascade.py tests
- your cries have been heard:  removing a pending item
from an attribute or collection with delete-orphan
expunges the item from the session; no FlushError is raised.
Note that if you session.save()'ed the pending item
explicitly, the attribute/collection removal still knocks
it out.
2008-02-17 18:13:14 +00:00
Mike Bayer 1aebdb231f get basic compilation working for [ticket:972] 2008-02-17 15:35:30 +00:00
Mike Bayer a3f67fecb2 - any(), has(), contains(), attribute level == and != now
work properly with self-referential relations - the clause
inside the EXISTS is aliased on the "remote" side to
distinguish it from the parent table.
2008-02-17 01:15:43 +00:00
Mike Bayer 191dbee5c8 - remove some old cruft
- deprecate ancient engine_descriptors() method
2008-02-16 06:07:28 +00:00
Jason Kirtland 29e456d51c Bump. 2008-02-15 16:54:42 +00:00
Mike Bayer 14b3a913ac fixing recent schema.py changes to work with oracle 'owner' attribute 2008-02-14 23:41:17 +00:00
Jason Kirtland 911c1ec513 - comment typo 2008-02-14 22:42:53 +00:00
Jason Kirtland 90e3532d0c - Made testlib's --unhashable and r3935's set changes play nice
- A bonus overhead reduction for IdentitySet instances
2008-02-14 22:39:42 +00:00
Jason Kirtland bd27fb07d4 - Corrected __eq__ pragma drift. 2008-02-14 22:07:58 +00:00
Jason Kirtland c055143ba9 Restore 2.3 compat for the sharding test 2008-02-14 21:47:01 +00:00
Mike Bayer 14542f4c07 fixed (still uncovered) incorrect variable name... 2008-02-14 20:07:38 +00:00
Jason Kirtland 71e745e96b - Fixed a couple pyflakes, cleaned up imports & whitespace 2008-02-14 20:02:10 +00:00
Rick Morrison 8dd5eb402e MSSQL now compiles func.now() to CURRENT_TIMESTAMP 2008-02-14 18:38:24 +00:00
Mike Bayer 84485fb7bb - fixed bug in result proxy where anonymously generated
column labels would not be accessible using their straight
string name
2008-02-14 18:22:47 +00:00
Rick Morrison eddae08fdf Added EXEC to MSSQL _is_select regexp; should now detect row-returning stored procedures
Added experimental implementation of limit/offset using row_number()
2008-02-14 18:03:57 +00:00
Mike Bayer a612c8a5e2 a TODO comment 2008-02-13 17:27:47 +00:00
Jason Kirtland 3b23d1d6d6 0.4.3 edits 2008-02-12 21:27:18 +00:00
Mike Bayer 9fffa2c7e1 - fixed bug introduced in r4070 where union() and other compound selects would not get
an OID column if it only contained one selectable element, due to missing return in _proxy_column()
- visit_column() calls itself to render a primary key col being used as the interpretation of the oid col instead of relying upon broken partial logic
2008-02-12 21:16:31 +00:00
Mike Bayer 85e8cb7ffb add pk cols to assocaition table 2008-02-12 16:45:39 +00:00
Jason Kirtland adc929c0f1 - Added two new vertical dict mapping examples. 2008-02-12 01:44:20 +00:00
Mike Bayer 6f9aa3a900 - added expire_all() method to Session. Calls expire()
for all persistent instances.  This is handy in conjunction
with .....

- instances which have been partially or fully expired
will have their expired attributes populated during a regular
Query operation which affects those objects, preventing
a needless second SQL statement for each instance.
2008-02-11 19:22:34 +00:00
Jason Kirtland 645fa5255d - Fixed .get(<int>) of a String PK (exposed by pg 8.3) 2008-02-11 19:14:38 +00:00
Mike Bayer c0b5a0446b - updated the naming scheme of the base test classes in test/testlib/testing.py;
tests extend from either TestBase or ORMTest, using additional mixins for
special assertion methods as needed
2008-02-11 00:28:39 +00:00
Mike Bayer 90c572b513 - Table columns and constraints can be overridden on a
an existing table (such as a table that was already
reflected) using the 'useexisting=True' flag, which now
takes into account the arguments passed along with it.
- fixed one element of [ticket:910]
- refactored reflection test
2008-02-10 23:39:09 +00:00
Jason Kirtland 1ddf4af355 - Better error messaging on failed collection bulk-assignments 2008-02-09 19:15:45 +00:00
Jason Kirtland 7313f5c714 - Note about future CollectionAttributeImp.collection_intrface removal + whitespace cleanup. 2008-02-09 18:45:50 +00:00
Jason Kirtland 2269bee87f - Determine the basic collection interface dynamically when adapting a collection to an interable 2008-02-09 18:45:11 +00:00
Mike Bayer 3ab7149e25 added info on foreign_keys attribute 2008-02-09 17:26:48 +00:00
Mike Bayer 1a3dc51993 - lazy loader can now handle a join condition where the "bound"
column (i.e. the one that gets the parent id sent as a bind
parameter) appears more than once in the join condition.
Specifically this allows the common task of a relation()
which contains a parent-correlated subquery, such as "select
only the most recent child item". [ticket:946]
- col_is_part_of_mappings made more strict, seems to be OK
with tests
- memusage will dump out the size list in an assertion fail
2008-02-09 01:48:19 +00:00
Mike Bayer 0b890e1ccd heisenbug in aisle 3
(when db.dispose is called in unitofwork test with sqlite, the first test that runs in memusage grows by two gc'ed objects on every iteration; then the problem vanishes.  doesnt matter what test runs in memusage.  doing a dispose() in memusage solves the problem also.  screwing wiht the mechanics of engine.dispose() only fix it when both the pool.dispose() *and* the pool.ressurect() are disabled.  its just a subtle python/pysqlite bug afaict)
2008-02-09 01:24:01 +00:00
Mike Bayer 842934f40c - added generative where(<criterion>) method to delete()
and update() constructs which return a new object with
criterion joined to existing criterion via AND, just
like select().where().
- compile assertions use assertEquals()
2008-02-08 22:57:45 +00:00
Jason Kirtland 1b228e8481 - Added deferrability support to constraints 2008-02-08 20:50:33 +00:00
Jason Kirtland 426b6d9baf - psycopg2 can raise un-str()able exceptions; don't croak when trying to log them 2008-02-08 20:38:28 +00:00
Paul Johnston 21c2976870 Fix: deletes with schemas on MSSQL 2000 [ticket:967] 2008-02-08 16:48:37 +00:00
Mike Bayer 2999ea9554 test for session close efficiency 2008-02-08 15:45:54 +00:00
Paul Johnston 5049242f3b Fix some mssql unit tests 2008-02-08 13:45:19 +00:00
Paul Johnston 7e6cd582ca Strip schema from access tables 2008-02-08 12:05:28 +00:00
Lele Gaifax 668a8ae21b Avoid using common keywords as field names: the test executes literal selects 2008-02-06 17:52:48 +00:00
Mike Bayer 6e498f1a21 check for unicode first before encoding 2008-02-06 17:44:48 +00:00
Ants Aasma 4506425966 unit-of-work flush didn't close the failed transaction when the session was not in a transaction and commiting the transaction failed. 2008-02-06 17:38:29 +00:00
Jason Kirtland 56410e3158 - Some more reST docstring corrections 2008-02-06 01:40:40 +00:00
Jason Kirtland 9bc19046d1 - clean up the print version of the docs a bit [ticket:745] 2008-02-06 01:32:33 +00:00
Jason Kirtland b0991bf661 - A few quick docstring typo fixes, including [ticket:766] 2008-02-06 01:09:08 +00:00
Jason Kirtland e0f41c6475 C-u 66 C-x f M-q 2008-02-06 00:11:05 +00:00
Jason Kirtland 23836ee146 ChangeLog for r4115 2008-02-06 00:01:44 +00:00
Jason Kirtland 5320a47a14 - Enabled schema support on SQLite, added the temporary table namespace to table name reflection
- TODO: add sqlite to the standard alternate schema tests. a little tricky, because unlike CREATE SCHEMA, an ATTACH DATABASE won't survive a pool dispose...
2008-02-05 23:31:14 +00:00
Jason Kirtland 28af2439d9 - doc edits- thanks asmodai! [ticket:906] 2008-02-05 20:26:08 +00:00
Mike Bayer b06491586c better that it doesn't get a scalar loader callable 2008-02-05 19:42:51 +00:00
Mike Bayer 2b6e2defe5 expire with synonyms [ticket:964] 2008-02-05 19:41:51 +00:00
Jason Kirtland 96549e6b8f - Autodetect mysql's ANSI_QUOTES mode, sometimes. [ticket:845]
The dialect needs a hook run on first pool connect to detect this most of
  the time, and a refactor with Dialect-per-Connection to get it right all of
  the time. (It's a connection-session scoped setting with dialect-modifying
  behavior)
2008-02-05 17:26:35 +00:00
Jason Kirtland dc98505f2f hmmm. 2008-02-05 15:34:28 +00:00
Jason Kirtland 6d843aeeb2 - Added free-form DDL statements, can be executed standalone or tied to the DDL create/drop lifecycle of Tables and MetaData. [ticket:903]
- Added DDL event hooks, triggers callables before and after create / drop.
2008-02-05 05:46:33 +00:00
Mike Bayer 6c73fbb422 *more* tweaks to avoid DEFAULT VALUES on sqlite 2008-02-04 22:40:52 +00:00
Mike Bayer d2f9015c12 lock in replacing '%' with '%%' 2008-02-04 22:35:29 +00:00
Mike Bayer bb1dd85dcc - add dummy column to appease older SQLite verisons in unicode.py
- add test "escape_literal_column" comiler method to start addressing literal '%' character
2008-02-04 21:47:42 +00:00
Jason Kirtland 0de289921c - ColumnDefault callables can now be any kind of compliant callable, previously only actual functions were allowed. 2008-02-04 20:49:38 +00:00
Mike Bayer 66df4b4958 forcibly clean out _sessions, _mapper_registry at test start to eliminate leftovers from other unit tests (from other test scripts) still stored in memory 2008-02-04 20:35:25 +00:00
Mike Bayer 72184bc814 add some extra assertions to ensure all mappers are gone after clear_mappers() (for [ticket:963]) 2008-02-04 02:44:04 +00:00
Jason Kirtland b79f23d3d0 - fixed reflection of Time columns on sqlite 2008-02-01 08:11:12 +00:00
Mike Bayer a0ffeb5464 - some consolidation of tests in select.py, moved
other tests to more specific modules
- added "now()" as a generic function; on SQLite and
Oracle compiles as "CURRENT_TIMESTAMP"; "now()"
on all others [ticket:943]
2008-02-01 01:16:18 +00:00
Jason Kirtland 7bf0fca858 - Workaround for datetime quirk, LHS comparisons to SA expressions now work. 2008-01-31 21:32:38 +00:00
Jason Kirtland 3aed5fa544 - Friendlier exception messages for unbound, implicit execution
- Implicit binding failures now raise UnboundExecutionError
2008-01-31 19:48:13 +00:00
Mike Bayer e1aa7573f2 - added "autocommit=True" kwarg to select() and text(),
as well as generative autocommit() method on select();
for statements which modify the database through some
user-defined means other than the usual INSERT/UPDATE/
DELETE etc., this flag will enable "autocommit" behavior
during execution if no transaction is in progress
[ticket:915]
2008-01-31 17:48:22 +00:00
Jason Kirtland e13fdb965f - implemented RowProxy.__ne__ [ticket:945], thanks knutroy
- test coverage for same
2008-01-31 04:49:31 +00:00
Mike Bayer a5b23bda66 - the startswith(), endswith(), and contains() operators
now concatenate the wildcard operator with the given
operand in SQL, i.e. "'%' || <bindparam>" in all cases,
accept text('something') operands properly [ticket:962]

- cast() accepts text('something') and other non-literal
operands properly [ticket:962]
2008-01-31 03:57:20 +00:00
Mike Bayer 19c3c4c2e0 escapedefaultstest passes on everything 2008-01-30 21:33:17 +00:00
Mike Bayer 2d2042cc2b moved default escaping test to its own test so oracle gets it 2008-01-30 21:31:32 +00:00
Mike Bayer d3e6ccc625 - Oracle and others properly encode SQL used for defaults
like sequences, etc., even if no unicode idents are used
since identifier preparer may return a cached unicode
identifier.
2008-01-30 21:08:11 +00:00
Mike Bayer 204e7201d2 docstring fix 2008-01-30 19:19:21 +00:00
Mike Bayer d2e4c52b9f - next release will be 0.4.3
- fixed merge() collection-doubling bug when merging
transient entities with backref'ed collections.
[ticket:961]
- merge(dont_load=True) does not accept transient
entities, this is in continuation with the fact that
merge(dont_load=True) does not accept any "dirty"
objects either.
2008-01-30 17:35:20 +00:00
Mike Bayer da7fef941c - "Passive defaults" and other "inline" defaults can now
be loaded during a flush() call if needed; in particular,
this allows constructing relations() where a foreign key
column references a server-side-generated, non-primary-key
column. [ticket:954]
2008-01-28 23:15:40 +00:00
Jason Kirtland b5dd96590a - Added a simple @future test marker. 2008-01-28 19:58:39 +00:00
Jason Kirtland 7ed5faff26 - Fixed little think-o in fails_if 2008-01-28 19:52:04 +00:00
Mike Bayer 88f42cf1f4 - Fixed bug in polymorphic inheritance where incorrect
exception is raised when base polymorphic_on
column does not correspond to any columns within
the local selectable of an inheriting mapper more
than one level deep
2008-01-27 02:21:23 +00:00
Mike Bayer 63d2ce5191 encourage usage of union() and other composites as module-level 2008-01-25 20:52:13 +00:00
Mike Bayer 33a6724e33 - added standalone "query" class attribute generated
by a scoped_session.  This provides MyClass.query
without using Session.mapper.  Use via:

MyClass.query = Session.query_property()
2008-01-25 18:16:12 +00:00
Jason Kirtland fadc61e8ca - Ignore jython debris 2008-01-24 19:08:22 +00:00
Jason Kirtland ed74083956 - Flipped join order of __radd__ on association proxied lists. 2008-01-24 01:12:46 +00:00
Jason Kirtland e94c3ba27a - IdentitySet binops no longer accept plain sets. 2008-01-24 01:00:41 +00:00
Jason Kirtland 42a3344eba A little clarity tweak to r4093 2008-01-24 00:21:58 +00:00
Jason Kirtland f6439ffa2c Corrected behavior of get_cls_kwargs and friends 2008-01-24 00:08:40 +00:00
Mike Bayer 29f7a38ee0 added an intro for the code sample so that its not construed as a "synopsis" 2008-01-23 20:00:53 +00:00
Mike Bayer c8b50c1ffb - query.join() can also accept tuples of attribute
name/some selectable as arguments.  This allows
construction of joins *from* subclasses of a
polymorphic relation, i.e.:

query(Company).\
join(
  [('employees', people.join(engineer)), Engineer.name]
)
2008-01-23 19:20:49 +00:00
Jason Kirtland f980c5c88f Added notes about 2.3 improvements 2008-01-23 18:26:50 +00:00
Jason Kirtland bc998a14a5 Edits 2008-01-23 18:20:26 +00:00
Mike Bayer 2dbf2a3ef8 whups, args in wrong order 2008-01-23 15:21:18 +00:00
Mike Bayer a829b5c51b more descriptive error message for m2m concurrency error 2008-01-23 15:18:28 +00:00
Mike Bayer 74a128c686 more capability added to reduce_columns 2008-01-23 15:16:43 +00:00
Jason Kirtland 47a8b6d10d - Migrated zoomark to profiling.function_call_count(), tightened up the numbers. Is there variation by platform too? Buildbots will tell... 2008-01-22 22:43:04 +00:00
Jason Kirtland 7e0f72cde4 rein in r3840 find and replace rampage 2008-01-22 21:32:51 +00:00
Jason Kirtland b3cc2f7e0c - 2.3 fixup part three: 100% on postgres, mysql 2008-01-22 21:08:21 +00:00
Jason Kirtland 5bc0fe9e16 - Removed some test bogosity 2008-01-22 19:42:12 +00:00
Jason Kirtland 342adac637 - Cover 2.3 Decimal fallback 2008-01-22 18:06:46 +00:00
Jason Kirtland 412c80dd6c - 2.3 fixup, part two: 100% passing for sqlite
- added 2.4-style binops to util.Set on 2.3
  - OrderedSets pickle on 2.3
  - more lib/sqlalchemy set vs Set corrections
  - fixed InstrumentedSet.discard for 2.3
  - set, sorted compatibility for test suite
- added testing.fails_if decorator
2008-01-21 23:19:39 +00:00
Mike Bayer 08bbc3dfd8 clean up a little close() silliness 2008-01-20 19:04:06 +00:00
Mike Bayer 8c3ede4b6f factor create_row_adapter into sql.util.row_adapter 2008-01-20 05:06:55 +00:00
Mike Bayer ef63a84a49 further clarification on transaction state 2008-01-20 04:47:16 +00:00
Ants Aasma e15f6c7327 fix rollback behavior with transaction context manager and failed two phase transactions 2008-01-20 04:31:53 +00:00
Ants Aasma f645c0a420 example of using try-catch to do transaction commit/rollback was wrong in the docs 2008-01-20 03:39:43 +00:00
Ants Aasma 9f366afdda - parent transactions weren't started on the connection when adding a connection to a nested session transaction.
- session.transaction now always refers to the innermost active transaction, even when commit/rollback are called directly on the session transaction object.
- when preparing a two-phase transaction fails on one connection all the connections are rolled back.
- two phase transactions can now be prepared.
- session.close() didn't close all transactions when nested transactions were used.
- rollback() previously erroneously set the current transaction directly to the parent of the transaction that could be rolled back to.
- autoflush for commit() wasn't flushing for simple subtransactions.
2008-01-20 03:22:00 +00:00
Jason Kirtland 4be99db15b - Restored 2.3 compat. in lib/sqlalchemy
- Part one of test suite fixes to run on 2.3
  Lots of failures still around sets; sets.Set differs from __builtin__.set
  particularly in the binops. We depend on set extensively now and may need to
  provide a corrected sets.Set subclass on 2.3.
2008-01-19 23:37:11 +00:00
Jason Kirtland 21193cebe2 - Added source transformation framework for non-2.4 parser implementations
- test/clone.py can create and update (transformed) copies of the test suite
- Added Python 2.4 decorator -> 2.3 source transform
2008-01-19 23:11:47 +00:00
Mike Bayer bd3a65252d - Oracle assembles the correct columns in the result set
column mapping when generating a LIMIT/OFFSET subquery,
  allows columns to map properly to result sets even
  if long-name truncation kicks in [ticket:941]
2008-01-19 20:11:29 +00:00
Mike Bayer 840a2fabb8 - some expression fixup:
- the '.c.' attribute on a selectable now gets an
entry for every column expression in its columns
clause; previously, "unnamed" columns like functions
and CASE statements weren't getting put there.  Now
they will, using their full string representation
if no 'name' is available.
- The anonymous 'label' generated for otherwise
unlabeled functions and expressions now propagates
outwards at compile time for expressions like
select([select([func.foo()])])
- a CompositeSelect, i.e. any union(), union_all(),
intersect(), etc. now asserts that each selectable
contains the same number of columns.  This conforms
to the corresponding SQL requirement.
- building on the above ideas, CompositeSelects
now build up their ".c." collection based on
the names present in the first selectable only;
corresponding_column() now works fully for all
embedded selectables.
2008-01-19 18:36:52 +00:00
Mike Bayer 6d486fb2e7 check for session is none, [ticket:940] 2008-01-17 16:24:26 +00:00
Jason Kirtland b640456af1 Updated bit about coverage.py 2008-01-16 23:53:01 +00:00
Mike Bayer 55c0ab5b2b - dynamic relations, when referenced, create a strong
reference to the parent object so that the query
still has a parent to call against even if the
parent is only created (and otherwise dereferenced)
within the scope of a single expression [ticket:938]
2008-01-16 22:06:15 +00:00
Jason Kirtland 626e83dc28 - default the root logger level only if unset 2008-01-16 18:10:08 +00:00
Mike Bayer 924b639215 maintain the ordering of the given collection of columns when reducing so that primary key collections remain
ordered the same as in the mapped table
2008-01-15 18:33:30 +00:00
Mike Bayer 2c06e82557 avoid unnecessary mapper.extension copy 2008-01-15 18:21:13 +00:00
Mike Bayer 9eced72c03 finally, a really straightforward reduce() method which reduces cols
to the minimal set for every test case I can come up with, and
now replaces all the cruft in Mapper._compile_pks() as well as
Join.__init_primary_key().  mappers can now handle aliased selects
and figure out the correct PKs pretty well [ticket:933]
2008-01-15 17:59:27 +00:00
Mike Bayer 4870a41d27 - select_table mapper turns straight join into aliased select + custom PK, to allow
joins onto select_table mappers
- starting a generalized reduce_columns func
2008-01-15 02:34:17 +00:00
Mike Bayer 868a0584ba added more (failing) tests to query, will need to fix [ticket:932] [ticket:933] 2008-01-14 18:50:10 +00:00
Mike Bayer b2d1c5aa87 - query.join() can now accept class-mapped attributes
as arguments, which can be used in place or in any
combination with strings.  In particular this allows
construction of joins to subclasses on a polymorphic
relation, i.e.
query(Company).join(['employees', Engineer.name]),
etc.
2008-01-14 04:20:26 +00:00
Mike Bayer 9e1a35ef3d - applying some refined versions of the ideas in the smarter_polymorphic
branch
- slowly moving Query towards a central "aliasing" paradigm which merges
the aliasing of polymorphic mappers to aliasing against arbitrary select_from(),
to the eventual goal of polymorphic mappers which can also eagerload other
relations
- supports many more join() scenarios involving polymorphic mappers in
most configurations
- PropertyAliasedClauses doesn't need "path", EagerLoader doesn't need to
guess about "towrap"
2008-01-14 02:45:30 +00:00
Mike Bayer 188c2ac8e5 - _get_equivalents() converted into a lazy-initializing property; Query was calling it
for polymorphic loads which is really expensive
- surrogate_mapper adapts the given order_by, so that order_by can be against the mapped
table and is usable for sub-mappers as well.  Query properly calls select_mapper.order_by.
2008-01-13 19:04:55 +00:00
Jason Kirtland 17d3c8764e - testbase is gone, replaced by testenv
- Importing testenv has no side effects- explicit functions provide similar behavior to the old immediate behavior of testbase
- testing.db has the configured db
- Fixed up the perf/* scripts
2008-01-12 22:03:42 +00:00
Jason Kirtland c194962019 - Undeclared SAWarnings are now fatal to tests as well.
- Fixed typo that was killing runs of individual named tests.
2008-01-12 04:52:05 +00:00
Mike Bayer 05a693fcb7 fixed NOT ILIKE 2008-01-11 21:30:02 +00:00
Mike Bayer 6d2d5e923e - added "ilike()" operator to column operations.
compiles to ILIKE on postgres, lower(x) LIKE lower(y)
on all others [ticket:727]
2008-01-11 21:24:01 +00:00
Lele Gaifax 664ba44679 Reverted to False Firebird's supports_sane_rowcount
Slipped in: even if it seems it could be set to True, I'm still testing the rowcount affair
2008-01-11 15:31:15 +00:00
Lele Gaifax a19dc80cfb Try to reflect also the Sequence on the PK under Firebird 2008-01-11 15:27:02 +00:00
Jason Kirtland 8dd825b413 - Warnings are now issued as SAWarning instead of RuntimeWarning; util.warn() wraps this up.
- SADeprecationWarning has moved to exceptions. An alias remains in logging until 0.5.
2008-01-11 01:28:43 +00:00
Jason Kirtland 3e9df22546 Include column name in length-less String warning (more [ticket:912]) 2008-01-10 23:16:56 +00:00
Mike Bayer 04ad3303dc - unit test for r4048 2008-01-10 22:45:07 +00:00
Mike Bayer f1cb136a62 - added a mapper() flag "eager_defaults"; when set to
True, defaults that are generated during an INSERT
or UPDATE operation are post-fetched immediately,
instead of being deferred until later.  This mimics
the old 0.3 behavior.
2008-01-10 22:32:51 +00:00
Mike Bayer 062b8c0eb1 - added extra fk override test
- proper error message is raised when trying to
access expired instance attributes with no session
present
2008-01-10 18:05:20 +00:00
Lele Gaifax 37570dc25e Recognize another Firebird exception in dialect.is_disconnect() 2008-01-10 17:40:38 +00:00
Mike Bayer 63662e37ca - finally added PGMacAddr type to postgres
[ticket:580]
2008-01-10 15:24:14 +00:00
Mike Bayer fc537e41cb converted tests to use remote_side and foreign_keys. but...wow these are hard tests.. 2008-01-10 06:51:51 +00:00
Jason Kirtland bf36c648f2 Reworked r4042- undeclared deprecation warnings are now *fatal* to tests. No surprises. 2008-01-10 02:37:39 +00:00
Jason Kirtland 84576e3258 test suite deprecation rampage 2008-01-09 22:54:51 +00:00
Jason Kirtland 046ec98a0b bump. 2008-01-09 22:53:33 +00:00
Mike Bayer 0a860c6fb0 formatting, added UnicodeText 2008-01-09 21:37:42 +00:00
Jason Kirtland 912432179a Silenced deprecation warnings when testing deprecated extensions... 2008-01-09 20:41:14 +00:00
Jason Kirtland 4c705b6089 Added explicit length to more testing String columns. 2008-01-09 20:29:04 +00:00
Jason Kirtland 54f7111662 re-bump 2008-01-09 20:23:53 +00:00
Jason Kirtland c83bb94e0d Added UnicodeText alias 2008-01-09 20:22:41 +00:00
Mike Bayer 979c9323dc - fixed bug with session.dirty when using "mutable scalars"
(such as PickleTypes)

- added a more descriptive error message when flushing on a
relation() that has non-locally-mapped columns in its primary or
secondary join condition
2008-01-09 18:52:35 +00:00
Mike Bayer eefd1f78a2 redid the _for_ddl String/Text deprecation warning correctly [ticket:912] 2008-01-09 18:09:49 +00:00
Mike Bayer 6eb9c11e7b - fixed bug in union() so that select() statements which don't derive
from FromClause objects can be unioned
2008-01-08 21:53:37 +00:00
Mike Bayer 93d6f1d58a - Text type is properly exported now and does not raise a warning
on DDL create
2008-01-08 08:55:18 +00:00
Jason Kirtland c111f6f93d Fixed reflection of mysql empty string column defaults. 2008-01-08 07:46:37 +00:00
Mike Bayer 2fc1bf2615 bump 2008-01-07 20:13:29 +00:00
Mike Bayer 500982b7b3 logged [ticket:923] fix 2008-01-07 19:59:23 +00:00
Rick Morrison 46720f0bbc Fix for ticket [923] 2008-01-07 19:33:29 +00:00
Mike Bayer e8feacf1db - fixed an attribute history bug whereby assigning a new collection
to a collection-based attribute which already had pending changes
would generate incorrect history [ticket:922]

- fixed delete-orphan cascade bug whereby setting the same
object twice to a scalar attribute could log it as an orphan
[ticket:925]
- generative select.order_by(None) / group_by(None) was not managing to
reset order by/group by criterion, fixed [ticket:924]
2008-01-07 18:52:02 +00:00
Mike Bayer acd13f99f1 - suppressing *all* errors in InstanceState.__cleanup() now. 2008-01-06 20:41:48 +00:00
Mike Bayer d879135085 - fixed bug which could occur with polymorphic "union" mapper
which falls back to "deferred" loading of inheriting tables

- the "columns" collection on a mapper/mapped class (i.e. 'c')
is against the mapped table, not the select_table in the
case of polymorphic "union" loading (this shouldn't be
noticeable)
2008-01-06 20:32:45 +00:00
Mike Bayer a78914942c - synonyms can now be created against props that don't exist yet,
which are later added via add_property().  This commonly includes
backrefs. (i.e. you can make synonyms for backrefs without
worrying about the order of operations) [ticket:919]
2008-01-05 23:27:02 +00:00
Mike Bayer 8fe38c7e95 - changed name of TEXT to Text since its a "generic" type; TEXT name is
deprecated until 0.5.  The "upgrading" behavior of String to Text
when no length is present is also deprecated until 0.5; will issue a
warning when used for CREATE TABLE statements (String with no length
for SQL expression purposes is still fine) [ticket:912]
2008-01-05 22:59:18 +00:00
Jason Kirtland 68a9e6cb1f Added 'function_call_count' assertion decorator. The full-suite vs. isolated run call count discrepancy needs to be ironed out before this can be applied to zoomark. 2008-01-05 21:56:22 +00:00
Jason Kirtland 40efd3a9c1 Updates 2008-01-05 21:46:08 +00:00
Jason Kirtland 35f7a0b594 Added lots o' info. 2008-01-05 20:50:10 +00:00
Jason Kirtland 2bc1c28c44 More overloads: fix cascades for += on a list relation, added operator support to association proxied lists. 2008-01-05 19:11:58 +00:00
Jason Kirtland f9fd5bfb86 bump. 2008-01-05 18:42:46 +00:00
Mike Bayer 829e9f8a91 calling this 0.4.2a 2008-01-05 18:27:12 +00:00
Mike Bayer e5b6c7bc33 - fixed fairly critical bug whereby the same instance could be listed
more than once in the unitofwork.new collection; most typically
reproduced when using a combination of inheriting mappers and
ScopedSession.mapper, as the multiple __init__ calls per instance
could save() the object with distinct _state objects
2008-01-05 18:26:28 +00:00
Jason Kirtland 9123a1d5ce Experimental: modestly more informative repr() for some expressions (using .description) 2008-01-05 00:37:08 +00:00
Jason Kirtland 5e12394aac Migrated a few in-function 'from x import y' to the 'global x; if x is None' style. 2008-01-05 00:25:57 +00:00
Jason Kirtland 38bcc2b2f8 Refined bulk-assignment aspects of the r3999 in-place collection operator fix. Also? r4000! 2008-01-04 20:40:22 +00:00
Jason Kirtland a230391a37 Fixed in-place set mutation operator support [ticket:920] 2008-01-04 20:17:42 +00:00
Jason Kirtland 0c515c5117 Added REPLACE statements to mysql autocommit list. 2008-01-04 19:10:18 +00:00
Mike Bayer 57a5b5f58e func unittest fix 2008-01-04 03:13:20 +00:00
Mike Bayer 64de56e55e fix select tests for labeled functions 2008-01-04 03:09:17 +00:00
Mike Bayer 1e69e26924 add anonymous labels to function calls 2008-01-04 01:00:42 +00:00
Ants Aasma efb89f2113 fix not calling the result processor of PGArray subtypes. (a rather embarrasing copypaste error) [ticket:913] 2008-01-03 23:38:55 +00:00
Mike Bayer ebe83b95ad - added very rudimentary yielding iterator behavior to Query. Call
query.yield_per(<number of rows>) and evaluate the Query in an
iterative context; every collection of N rows will be packaged up
and yielded.  Use this method with extreme caution since it does
not attempt to reconcile eagerly loaded collections across
result batch boundaries, nor will it behave nicely if the same
instance occurs in more than one batch.  This means that an eagerly
loaded collection will get cleared out if it's referenced in more than
one batch, and in all cases attributes will be overwritten on instances
that occur in more than one batch.
2008-01-02 23:13:02 +00:00
494 changed files with 112691 additions and 63446 deletions
+3001 -2992
View File
File diff suppressed because it is too large Load Diff
+4107
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,7 +1,7 @@
This is the MIT license: http://www.opensource.org/licenses/mit-license.php
Copyright (c) 2005, 2006, 2007, 2008 Michael Bayer and contributors. SQLAlchemy is a trademark of Michael
Bayer.
Copyright (c) 2005, 2006, 2007, 2008, 2009, 2010 Michael Bayer and contributors.
SQLAlchemy is a trademark of Michael Bayer.
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
+2 -2
View File
@@ -1,2 +1,2 @@
include doc/*.html
include doc/*.css
recursive-include doc *.html *.css *.txt *.js *.jpg
prune doc/build/output
+43 -11
View File
@@ -1,17 +1,49 @@
SQLAlchemy is licensed under an MIT-style license (see LICENSE).
Other incorporated projects may be licensed under different licenses.
All licenses allow for non-commercial and commercial use.
SQLAlchemy
++++++++++
To install:
The Python SQL Toolkit and Object Relational Mapper
python setup.py install
Requirements
------------
SVN checkouts also include setup.cfg file allowing setuptools to create
an svn-tagged build.
SQLAlchemy requires Python 2.4 or higher. One or more DB-API implementations
are also required for database access. See docs/intro.html for more
information on supported DB-API implementations.
Documentation is available in HTML format in the ./doc/ directory.
Installing
----------
Information running unit tests is in README.unittests.
To install::
good luck !
python setup.py install
To use without installation, include the ``lib`` directory in your Python
path.
Package Contents
----------------
doc/
HTML documentation, including tutorials and API reference.
examples/
Fully commented and executable implementations for a variety of tasks.
lib/
SQLAlchemy.
test/
Unit tests for SQLAlchemy. See ``README.unittests`` for more
information.
Help
----
Mailing lists, wiki, and more are available on-line at
http://www.sqlalchemy.org.
License
-------
SQLAlchemy is distributed under the `MIT license
<http://www.opensource.org/licenses/mit-license.php>`_.
+45
View File
@@ -0,0 +1,45 @@
=================
PYTHON 3 SUPPORT
=================
Current Python 3k support in SQLAlchemy is provided by a customized
2to3 script which wraps Python's 2to3 tool.
This document will refer to the Python 2.6 interpreter binary as
"python26" and the Python 3.xx interpreter binary as "python3".
To build the Python 3K version, use the Python 2.6 interpreter to
run the 2to3 script on the lib/ directory, and optionally the test/
directory. The -w flag indicates that the new files should be
written.
python26 sa2to3.py ./lib/ ./test/ -w
You now have a Python 3 version of SQLAlchemy in lib/.
Current 3k Issues
-----------------
Current bugs and tickets related to Py3k are on the Py3k milestone in trac:
http://www.sqlalchemy.org/trac/query?status=new&status=assigned&status=reopened&milestone=py3k
Running Tests
-------------
The unit test runner, described in README.unittests, is built on
Nose, and uses a plugin that is ordinarily installed using setuptools
entry points. At the time of this writing setuptools isn't available
for Python 3 although the "Distribute" project does seem to provide support.
Additionally, Nose itself is only available in an old version for Python 3,
which is available at http://bitbucket.org/jpellerin/nose3/ .
To run the unit tests using the old version of nose and without the usage of
setuptools, use the "sqla_nose.py" script:
python3 sqla_nose.py
When running with Python 3, lots of debug output is dumped to the console.
This is due to hacking around the old version of Nose to support the
SQLAlchemy test plugin without setuptools (details at
http://groups.google.com/group/nose-dev/browse_thread/thread/c6a25531baaa2531).
+191 -70
View File
@@ -1,101 +1,222 @@
=====================
SQLALCHEMY UNIT TESTS
----------------------
=====================
SQLAlchemy unit tests by default run using Python's built-in sqlite3
module. If running on Python 2.4, pysqlite must be installed.
As of 0.5.5, unit tests are run using nose. Documentation and
downloads for nose are available at:
http://somethingaboutorange.com/mrl/projects/nose/0.11.1/index.html
SQLAlchemy implements a nose plugin that must be present when tests are run.
This plugin is available when SQLAlchemy is installed via setuptools.
INSTANT TEST RUNNER
-------------------
A plain vanilla run of all tests using sqlite can be run via setup.py:
$ python setup.py test
Setuptools will take care of the rest ! To run nose directly and have
its full set of options available, read on...
SETUP
-----
To run unit tests (assuming unix-style commandline, adjust as needed for windows):
Python 2.4 or greater is required since the unit tests use decorators.
All that's required is for SQLAlchemy to be installed via setuptools.
For example, to create a local install in a source distribution directory:
cd into the SQLAlchemy distribution directory.
$ export PYTHONPATH=.
$ python setup.py develop -d .
Set up the PYTHONPATH. In bash:
export PYTHONPATH=./test/
On windows:
set PYTHONPATH=test\
The unittest framework will automatically prepend the lib directory to
sys.path. This forces the local version of SQLAlchemy to be used,
bypassing any setuptools-installed installations (setuptools places
.egg files ahead of plain directories, even if on PYTHONPATH,
unfortunately).
The above will create a setuptools "development" distribution in the local
path, which allows the Nose plugin to be available when nosetests is run.
The plugin is enabled using the "with-sqlalchemy=True" configuration
in setup.cfg.
RUNNING ALL TESTS
-----------------
To run all tests:
python test/alltests.py
$ nosetests
COMMAND LINE OPTIONS
--------------------
Help is available via:
Assuming all tests pass, this is a very unexciting output. To make it more
intersesting:
python test/alltests.py --help
usage: alltests.py [options] [tests...]
options:
-h, --help show this help message and exit
--dburi=DBURI database uri (overrides --db)
--db=DB prefab database uri (sqlite, sqlite_file, postgres,
mysql, oracle, oracle8, mssql)
--mockpool use mock pool
--verbose enable stdout echoing/printing
--log-info=LOG_INFO turn on info logging for <LOG> (multiple OK)
--log-debug=LOG_DEBUG
turn on debug logging for <LOG> (multiple OK)
--quiet suppress unittest output
--nothreadlocal dont use thread-local mod
--enginestrategy=ENGINESTRATEGY
engine strategy (plain or threadlocal, defaults to SA
default)
--coverage Dump a full coverage report after running
NON-SQLITE DATABASES
--------------------
The prefab database connections expect to log in to localhost on the
default port as user "scott", password "tiger", database "test" (where
applicable). E.g. for postgresql the this translates to
"postgres://scott:tiger@127.0.0.1:5432/test".
$ nosetests -v
RUNNING INDIVIDUAL TESTS
-------------------------
Any unittest module can be run directly from the module file (same commandline options):
Any directory of test modules can be run at once by specifying the directory
path:
python test/orm/mapper.py
$ nosetest test/dialect
Additionally, to run a speciic test within the module, specify it as ClassName.methodname:
Any test module can be run directly by specifying its module name:
python test/orm/mapper.py MapperTest.testget
$ nosetests test.orm.test_mapper
To run a specific test within the module, specify it as module:ClassName.methodname:
$ nosetests test.orm.test_mapper:MapperTest.test_utils
COMMAND LINE OPTIONS
--------------------
Help is available via --help:
$ nosetests --help
The --help screen is a combination of common nose options and options which
the SQLAlchemy nose plugin adds. The most commonly SQLAlchemy-specific
options used are '--db' and '--dburi'.
DATABASE TARGETS
----------------
Tests will target an in-memory SQLite database by default. To test against
another database, use the --dburi option with any standard SQLAlchemy URL:
--dburi=postgresql://user:password@localhost/test
Use an empty database and a database user with general DBA privileges.
The test suite will be creating and dropping many tables and other DDL, and
preexisting tables will interfere with the tests.
Several tests require alternate schemas to be present. This requirement
applies to all backends except SQLite and Firebird. These schemas are:
test_schema
test_schema_2
Please refer to your vendor documentation for the proper syntax to create
these schemas - the database user must have permission to create and drop
tables within these schemas. Its perfectly fine to run the test suite
without these schemas present, it only means that a handful of tests which
expect them to be present will fail.
Additional steps specific to individual databases are as follows:
ORACLE: the test_schema and test_schema_2 schemas are created as
users, as the "owner" in Oracle is considered like a "schema" in
SQLAlchemy.
The primary database user needs to be able to create and drop tables,
synonyms, and constraints in these schemas. Unfortunately, many hours of
googling and experimentation cannot find a GRANT option that allows the
primary user the "REFERENCES" role in a remote schema for tables not yet
defined (REFERENCES is per-table) - the only thing that works is to put
the user in the "DBA" role:
grant dba to scott;
Any ideas on what specific privileges within "DBA" allow an open-ended
REFERENCES grant would be appreciated, or if in fact "DBA" has some kind
of "magic" flag not accessible otherwise. So, running SQLA tests on oracle
requires access to a completely open Oracle database - Oracle XE is
obviously a terrific choice since its just a local engine. As always,
leaving the schemas out means those few dozen tests will fail and is
otherwise harmless.
MSSQL: Tests that involve multiple connections require Snapshot Isolation
ability implented on the test database in order to prevent deadlocks that
will occur with record locking isolation. This feature is only available
with MSSQL 2005 and greater. You must enable snapshot isolation at the
database level and set the default cursor isolation with two SQL commands:
ALTER DATABASE MyDatabase SET ALLOW_SNAPSHOT_ISOLATION ON
ALTER DATABASE MyDatabase SET READ_COMMITTED_SNAPSHOT ON
MSSQL+zxJDBC: Trying to run the unit tests on Windows against SQL Server
requires using a test.cfg configuration file as the cmd.exe shell won't
properly pass the URL arguments into the nose test runner.
If you'll be running the tests frequently, database aliases can save a lot of
typing. The --dbs option lists the built-in aliases and their matching URLs:
$ nosetests --dbs
Available --db options (use --dburi to override)
mysql mysql://scott:tiger@127.0.0.1:3306/test
oracle oracle://scott:tiger@127.0.0.1:1521
postgresql postgresql://scott:tiger@127.0.0.1:5432/test
[...]
To run tests against an aliased database:
$ nosetests --db=postgresql
To customize the URLs with your own users or hostnames, make a simple .ini
file called `test.cfg` at the top level of the SQLAlchemy source distribution
or a `.satest.cfg` in your home directory:
[db]
postgresql=postgresql://myuser:mypass@localhost/mydb
Your custom entries will override the defaults and you'll see them reflected
in the output of --dbs.
CONFIGURING LOGGING
---------------------
Logging is now available via Python's logging package. Any area of SQLAlchemy can be logged
through the unittest interface, such as:
-------------------
SQLAlchemy logs its activity and debugging through Python's logging package.
Any log target can be directed to the console with command line options, such
as:
Log mapper configuration, connection pool checkouts, and SQL statement execution:
$ nosetests test.orm.unitofwork --log-info=sqlalchemy.orm.mapper \
--log-debug=sqlalchemy.pool --log-info=sqlalchemy.engine
This would log mapper configuration, connection pool checkouts, and SQL
statement execution.
python test/orm/unitofwork.py --log-info=sqlalchemy.orm.mapper --log-debug=sqlalchemy.pool --log-info=sqlalchemy.engine
BUILT-IN COVERAGE REPORTING
------------------------------
Coverage is now integrated through the coverage.py module, included in the './test/' directory. Running the test suite with
the --coverage switch will generate a local file ".coverage" containing coverage details, and a report will be printed
to standard output with an overview of the coverage gathered from the last unittest run (the file is deleted between runs).
Coverage is tracked using Nose's coverage plugin. See the nose
documentation for details. Basic usage is:
After the suite has been run with --coverage, an annotated version of any source file can be generated
marking statements that are executed with > and statements that are missed with !, by running the coverage.py
utility with the "-a" (annotate) option, such as:
$ nosetests test.sql.test_query --with-coverage
python ./test/coverage.py -a ./lib/sqlalchemy/sql.py
BIG COVERAGE TIP !!! There is an issue where existing .pyc files may
store the incorrect filepaths, which will break the coverage system. If
coverage numbers are coming out as low/zero, try deleting all .pyc files.
TESTING NEW DIALECTS
--------------------
You can use the SQLAlchemy test suite to test any new database dialect in
development. All possible database features will be exercised by default.
Test decorators are provided that can exclude unsupported tests for a
particular dialect. You'll see them all over the source, feel free to add
your dialect to them or apply new decorations to existing tests as required.
It's fine to start out with very broad exclusions, e.g. "2-phase commit is not
supported on this database" and later refine that as needed "2-phase commit is
not available until server version 8".
To be considered for inclusion in the SQLAlchemy distribution, a dialect must
be integrated with the standard test suite. Dialect-specific tests can be
placed in the 'dialects/' directory. Comprehensive testing of
database-specific column types and their proper reflection are a very good
place to start.
When working through the tests, start with 'engine' and 'sql' tests. 'engine'
performs a wide range of transaction tests that might deadlock on a brand-new
dialect- try disabling those if you're having problems and revisit them later.
Once the 'sql' tests are passing, the 'orm' tests should pass as well, modulo
any adjustments needed for SQL features the ORM uses that might not be
available in your database. But if an 'orm' test requires changes to your
dialect or the SQLAlchemy core to pass, there's a test missing in 'sql'! Any
time you can spend boiling down the problem to it's essential sql roots and
adding a 'sql' test will be much appreciated.
The test suite is very effective at illuminating bugs and inconsistencies in
an underlying DB-API (or database!) implementation. Workarounds are almost
always possible. If you hit a wall, join us on the mailing list or, better,
IRC!
which will create a new annotated file ./lib/sqlalchemy/sql.py,cover . Pretty cool !
TIPS
----
When running the tests on postgres, postgres gets slower and slower each time you run the tests.
This seems to be related to the constant creation/dropping of tables. Running a "VACUUM FULL"
on the database will speed it up again.
-1
View File
@@ -1 +0,0 @@
0.4.2
-27
View File
@@ -1,27 +0,0 @@
<html>
<head>
<link href="style.css" rel="stylesheet" type="text/css"></link>
<link href="docs.css" rel="stylesheet" type="text/css"></link>
<script src="scripts.js"></script>
<title>SQLAlchemy Documentation</title>
</head>
<body>
<h3>What is an Alpha API Feature?</h3>
<p><b>Alpha API</b> indicates that the best way for a particular feature to be presented hasn't been firmly settled on as of yet, and the current way is being introduced on a trial basis. Its spirit is not as much a warning that "this API might change", its more an invitation to the users saying, "heres a new idea I had. I'm not sure if this is the best way to do it. Do you like it ? Should we do this differently? Or is it good the way it is ?". Alpha API features are always small in scope and are presented in releases so that the greatest number of users get some hands-on experience with it; large-scoped API or architectural changes will always be discussed on the mailing list/Wiki first.</p>
<p>Reasons why a feature might want to change include:
<ul>
<li>The API for the feature is too difficult to use for the typical task, and needs to be more "convenient"</li>
<li>The feature only implements a subsection of what it really should be doing</li>
<li>The feature's interface is inconsistent with that of other features which operate at a similar level</li>
<li>The feature is confusing and is often misunderstood, and would be better replaced by a more manual feature that makes the task clearer</li>
<li>The feature overlaps with another feature and effectively provides too many ways to do the same thing</li>
<li>The feature made some assumptions about the total field of use cases which is not really true, and it breaks in other scenarios</li>
</ul>
</p>
<p>A good example of what was essentially an "alpha feature" is the <code>private=True</code> flag. This flag on a <code>relation()</code> indicates that child objects should be deleted along with the parent. After this flag experienced some usage by the SA userbase, some users remarked that a more generic and configurable way was Hibernates <code>cascade="all, delete-orphan"</code>, and also that the term <code>cascade</code> was clearer in purpose than the more ambiguous <code>private</code> keyword, which could be construed as a "private variable".</p>
<center><input type="button" value="close window" onclick="window.close()"></center>
</body>
</html>
-16
View File
@@ -1,16 +0,0 @@
<html>
<head>
<link href="style.css" rel="stylesheet" type="text/css"></link>
<link href="docs.css" rel="stylesheet" type="text/css"></link>
<script src="scripts.js"></script>
<title>SQLAlchemy Documentation</title>
</head>
<body>
<h3>What is an Alpha Implementation Feature?</h3>
<p><b>Alpha Implementation</b> indicates a feature where developer confidence in its functionality has not yet been firmly established. This typically includes brand new features for which adequate unit tests have not been completed, and/or features whose scope is broad enough that its not clear what additional unit tests might be needed.</p>
<p>Alpha implementation is not meant to discourage the usage of a feature, it is only meant to indicate that some difficulties in getting full functionality from the feature may occur, and to encourage the reporting of these difficulties either via the mailing list or through <a href="http://www.sqlalchemy.org/trac/newticket" target="_blank">submitting a ticket</a>.</p>
<center><input type="button" value="close window" onclick="window.close()"></center>
</body>
</html>
+51
View File
@@ -0,0 +1,51 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d output/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html latex site-mako
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dist-html same as html, but places files in /doc"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
clean:
-rm -rf output/*
html:
mkdir -p output/html output/doctrees
$(SPHINXBUILD) -b html -A mako_layout=html $(ALLSPHINXOPTS) output/html
@echo
@echo "Build finished. The HTML pages are in output/html."
dist-html:
$(SPHINXBUILD) -b html -A mako_layout=html $(ALLSPHINXOPTS) ..
@echo
@echo "Build finished. The HTML pages are in ../."
site-mako:
mkdir -p output/site output/doctrees
$(SPHINXBUILD) -b html -A mako_layout=site $(ALLSPHINXOPTS) output/site
@echo
@echo "Build finished. The Mako pages are in output/site."
latex:
mkdir -p output/latex output/doctrees
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) output/latex
cp texinputs/* output/latex/
@echo
@echo "Build finished; the LaTeX files are in output/latex."
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
"run these through (pdf)latex."
doctest:
$(SPHINXBUILD) -b doctest -d output/doctrees . .
-10
View File
@@ -1,10 +0,0 @@
Documentation exists in its original format as Markdown files in the ./content directory.
To generate documentation:
python genhtml.py
This generates the Markdown files into Myghty templates as an interim step and then into HTML. It also
creates two pickled datafiles corresponding to the table of contents and all the generated docstrings
for the SQLAlchemy sourcecode.
+164
View File
@@ -0,0 +1,164 @@
from sphinx.application import TemplateBridge
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.highlighting import PygmentsBridge
from pygments import highlight
from pygments.lexer import RegexLexer, bygroups, using
from pygments.token import *
from pygments.filter import Filter, apply_filters
from pygments.lexers import PythonLexer, PythonConsoleLexer
from pygments.formatters import HtmlFormatter, LatexFormatter
import re
from mako.lookup import TemplateLookup
from mako.template import Template
class MakoBridge(TemplateBridge):
def init(self, builder, *args, **kw):
self.layout = builder.config.html_context.get('mako_layout', 'html')
self.lookup = TemplateLookup(directories=builder.config.templates_path,
format_exceptions=True,
imports=[
"from builder import util"
]
)
def render(self, template, context):
template = template.replace(".html", ".mako")
context['prevtopic'] = context.pop('prev', None)
context['nexttopic'] = context.pop('next', None)
context['mako_layout'] = self.layout == 'html' and 'static_base.mako' or 'site_base.mako'
return self.lookup.get_template(template).render_unicode(**context)
def render_string(self, template, context):
context['prevtopic'] = context.pop('prev', None)
context['nexttopic'] = context.pop('next', None)
context['mako_layout'] = self.layout == 'html' and 'static_base.mako' or 'site_base.mako'
return Template(template, lookup=self.lookup,
format_exceptions=True,
imports=[
"from builder import util"
]
).render_unicode(**context)
class StripDocTestFilter(Filter):
def filter(self, lexer, stream):
for ttype, value in stream:
if ttype is Token.Comment and re.match(r'#\s*doctest:', value):
continue
yield ttype, value
class PyConWithSQLLexer(RegexLexer):
name = 'PyCon+SQL'
aliases = ['pycon+sql']
flags = re.IGNORECASE | re.DOTALL
tokens = {
'root': [
(r'{sql}', Token.Sql.Link, 'sqlpopup'),
(r'{opensql}', Token.Sql.Open, 'opensqlpopup'),
(r'.*?\n', using(PythonConsoleLexer))
],
'sqlpopup':[
(
r'(.*?\n)((?:PRAGMA|BEGIN|SELECT|INSERT|DELETE|ROLLBACK|COMMIT|ALTER|UPDATE|CREATE|DROP|PRAGMA|DESCRIBE).*?(?:{stop}\n?|$))',
bygroups(using(PythonConsoleLexer), Token.Sql.Popup),
"#pop"
)
],
'opensqlpopup':[
(
r'.*?(?:{stop}\n*|$)',
Token.Sql,
"#pop"
)
]
}
class PythonWithSQLLexer(RegexLexer):
name = 'Python+SQL'
aliases = ['pycon+sql']
flags = re.IGNORECASE | re.DOTALL
tokens = {
'root': [
(r'{sql}', Token.Sql.Link, 'sqlpopup'),
(r'{opensql}', Token.Sql.Open, 'opensqlpopup'),
(r'.*?\n', using(PythonLexer))
],
'sqlpopup':[
(
r'(.*?\n)((?:PRAGMA|BEGIN|SELECT|INSERT|DELETE|ROLLBACK|COMMIT|ALTER|UPDATE|CREATE|DROP|PRAGMA|DESCRIBE).*?(?:{stop}\n?|$))',
bygroups(using(PythonLexer), Token.Sql.Popup),
"#pop"
)
],
'opensqlpopup':[
(
r'.*?(?:{stop}\n*|$)',
Token.Sql,
"#pop"
)
]
}
def _strip_trailing_whitespace(iter_):
buf = list(iter_)
if buf:
buf[-1] = (buf[-1][0], buf[-1][1].rstrip())
for t, v in buf:
yield t, v
class PopupSQLFormatter(HtmlFormatter):
def _format_lines(self, tokensource):
buf = []
for ttype, value in apply_filters(tokensource, [StripDocTestFilter()]):
if ttype in Token.Sql:
for t, v in HtmlFormatter._format_lines(self, iter(buf)):
yield t, v
buf = []
if ttype is Token.Sql:
yield 1, "<div class='show_sql'>%s</div>" % re.sub(r'(?:[{stop}|\n]*)$', '', value)
elif ttype is Token.Sql.Link:
yield 1, "<a href='#' class='sql_link'>sql</a>"
elif ttype is Token.Sql.Popup:
yield 1, "<div class='popup_sql'>%s</div>" % re.sub(r'(?:[{stop}|\n]*)$', '', value)
else:
buf.append((ttype, value))
for t, v in _strip_trailing_whitespace(HtmlFormatter._format_lines(self, iter(buf))):
yield t, v
class PopupLatexFormatter(LatexFormatter):
def _filter_tokens(self, tokensource):
for ttype, value in apply_filters(tokensource, [StripDocTestFilter()]):
if ttype in Token.Sql:
if ttype is not Token.Sql.Link and ttype is not Token.Sql.Open:
yield Token.Literal, re.sub(r'(?:[{stop}|\n]*)$', '', value)
else:
continue
else:
yield ttype, value
def format(self, tokensource, outfile):
LatexFormatter.format(self, self._filter_tokens(tokensource), outfile)
def autodoc_skip_member(app, what, name, obj, skip, options):
if what == 'class' and skip and name == '__init__':
return False
else:
return skip
def setup(app):
app.add_lexer('pycon+sql', PyConWithSQLLexer())
app.add_lexer('python+sql', PythonWithSQLLexer())
app.connect('autodoc-skip-member', autodoc_skip_member)
PygmentsBridge.html_formatter = PopupSQLFormatter
PygmentsBridge.latex_formatter = PopupLatexFormatter
+8
View File
@@ -0,0 +1,8 @@
import re
def striptags(text):
return re.compile(r'<[^>]*>').sub('', text)
def strip_toplevel_anchors(text):
return re.compile(r'\.html#.*-toplevel').sub('.html', text)
+195
View File
@@ -0,0 +1,195 @@
# -*- coding: utf-8 -*-
#
# Foo Bar documentation build configuration file, created by
# sphinx-quickstart on Wed Nov 26 19:50:10 2008.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# The contents of this file are pickled, so don't put values in the namespace
# that aren't pickleable (module imports are okay, they're removed automatically).
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If your extensions are in another directory, add it here. If the directory
# is relative to the documentation root, use os.path.abspath to make it
# absolute, like shown here.
sys.path.insert(0, os.path.abspath('../../lib'))
sys.path.insert(0, os.path.abspath('../../examples'))
sys.path.insert(0, os.path.abspath('.'))
import sqlalchemy
# General configuration
# ---------------------
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'builder.builders']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['templates']
# The suffix of source filenames.
source_suffix = '.rst'
template_bridge = "builder.builders.MakoBridge"
# The encoding of source files.
#source_encoding = 'utf-8'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'SQLAlchemy'
copyright = u'2007, 2008, 2009, 2010, the SQLAlchemy authors and contributors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = sqlalchemy.__version__
# The full version, including alpha/beta/rc tags.
release = sqlalchemy.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
unused_docs = ['output.txt']
# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = ['build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# Options for HTML output
# -----------------------
# The style sheet to use for HTML and HTML Help pages. A file of that name
# must exist either in Sphinx' static/ path, or in one of the custom paths
# given in html_static_path.
html_style = 'default.css'
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
html_title = "%s %s Documentation" % (project, release)
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = '%m/%d/%Y %H:%M:%S'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
html_use_modindex = False
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, the reST sources are included in the HTML build as _sources/<name>.
#html_copy_source = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''
# Output file base name for HTML help builder.
htmlhelp_basename = 'FooBardoc'
#autoclass_content = 'both'
# Options for LaTeX output
# ------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, document class [howto/manual]).
latex_documents = [
('index', 'sqlalchemy_%s.tex' % release.replace('.', '_'), ur'SQLAlchemy Documentation',
ur'Mike Bayer', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# Additional stuff for the LaTeX preamble.
# sets TOC depth to 2.
latex_preamble = '\setcounter{tocdepth}{3}'
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_use_modindex = True
-438
View File
@@ -1,438 +0,0 @@
Database Engines {@name=dbengine}
============================
The **Engine** is the starting point for any SQLAlchemy application. It's "home base" for the actual database and its DBAPI, delivered to the SQLAlchemy application through a connection pool and a **Dialect**, which describes how to talk to a specific kind of database and DBAPI combination.
The general structure is this:
{diagram}
+-----------+ __________
/---| Pool |---\ (__________)
+-------------+ / +-----------+ \ +--------+ | |
connect() <--| Engine |---x x----| DBAPI |---| database |
+-------------+ \ +-----------+ / +--------+ | |
\---| Dialect |---/ |__________|
+-----------+ (__________)
Where above, a [sqlalchemy.engine.Engine](rel:docstrings_sqlalchemy.engine_Engine) references both a [sqlalchemy.engine.Dialect](rel:docstrings_sqlalchemy.engine_Dialect) and [sqlalchemy.pool.Pool](rel:docstrings_sqlalchemy.pool_Pool), which together interpret the DBAPI's module functions as well as the behavior of the database.
Creating an engine is just a matter of issuing a single call, `create_engine()`:
{python}
engine = create_engine('postgres://scott:tiger@localhost:5432/mydatabase')
The above engine invokes the `postgres` dialect and a connection pool which references `localhost:5432`.
The engine can be used directly to issue SQL to the database. The most generic way is to use connections, which you get via the `connect()` method:
{python}
connection = engine.connect()
result = connection.execute("select username from users")
for row in result:
print "username:", row['username']
connection.close()
The connection is an instance of [sqlalchemy.engine.Connection](rel:docstrings_sqlalchemy.engine_Connection), which is a **proxy** object for an actual DBAPI connection. The returned result is an instance of [sqlalchemy.engine.ResultProxy](rel:docstrings_sqlalchemy.engine_ResultProxy), which acts very much like a DBAPI cursor.
When you say `engine.connect()`, a new `Connection` object is created, and a DBAPI connection is retrieved from the connection pool. Later, when you call `connection.close()`, the DBAPI connection is returned to the pool; nothing is actually "closed" from the perspective of the database.
To execute some SQL more quickly, you can skip the `Connection` part and just say:
{python}
result = engine.execute("select username from users")
for row in result:
print "username:", row['username']
result.close()
Where above, the `execute()` method on the `Engine` does the `connect()` part for you, and returns the `ResultProxy` directly. The actual `Connection` is *inside* the `ResultProxy`, waiting for you to finish reading the result. In this case, when you `close()` the `ResultProxy`, the underlying `Connection` is closed, which returns the DBAPI connection to the pool.
To summarize the above two examples, when you use a `Connection` object, its known as **explicit execution**. When you don't see the `Connection` object, but you still use the `execute()` method on the `Engine`, its called **explicit, connectionless execution**. A third variant of execution also exists called **implicit execution**; this will be described later.
The `Engine` and `Connection` can do a lot more than what we illustrated above; SQL strings are only its most rudimental function. Later chapters will describe how "constructed SQL" expressions can be used with engines; in many cases, you don't have to deal with the `Engine` at all after it's created. The Object Relational Mapper (ORM), an optional feature of SQLAlchemy, also uses the `Engine` in order to get at connections; that's also a case where you can often create the engine once, and then forget about it.
### Supported Databases {@name=supported}
Recall that the `Dialect` is used to describe how to talk to a specific kind of database. Dialects are included with SQLAlchemy for SQLite, Postgres, MySQL, MS-SQL, Firebird, Informix, and Oracle; these can each be seen as a Python module present in the `sqlalchemy.databases` package. Each dialect requires the appropriate DBAPI drivers to be installed separately.
Downloads for each DBAPI at the time of this writing are as follows:
* Postgres: [psycopg2](http://www.initd.org/tracker/psycopg/wiki/PsycopgTwo)
* SQLite: [pysqlite](http://initd.org/tracker/pysqlite)
* MySQL: [MySQLDB](http://sourceforge.net/projects/mysql-python)
* Oracle: [cx_Oracle](http://www.cxtools.net/default.aspx?nav=home)
* MS-SQL: [pyodbc](http://pyodbc.sourceforge.net/) (recommended) [adodbapi](http://adodbapi.sourceforge.net/) [pymssql](http://pymssql.sourceforge.net/)
* Firebird: [kinterbasdb](http://kinterbasdb.sourceforge.net/)
* Informix: [informixdb](http://informixdb.sourceforge.net/)
The SQLAlchemy Wiki contains a page of database notes, describing whatever quirks and behaviors have been observed. Its a good place to check for issues with specific databases. [Database Notes](http://www.sqlalchemy.org/trac/wiki/DatabaseNotes)
### create_engine() URL Arguments {@name=establishing}
SQLAlchemy indicates the source of an Engine strictly via [RFC-1738](http://rfc.net/rfc1738.html) style URLs, combined with optional keyword arguments to specify options for the Engine. The form of the URL is:
driver://username:password@host:port/database
Available drivernames are `sqlite`, `mysql`, `postgres`, `oracle`, `mssql`, and `firebird`. For sqlite, the database name is the filename to connect to, or the special name ":memory:" which indicates an in-memory database. The URL is typically sent as a string to the `create_engine()` function:
{python}
# postgres
pg_db = create_engine('postgres://scott:tiger@localhost:5432/mydatabase')
# sqlite (note the four slashes for an absolute path)
sqlite_db = create_engine('sqlite:////absolute/path/to/database.txt')
sqlite_db = create_engine('sqlite:///relative/path/to/database.txt')
sqlite_db = create_engine('sqlite://') # in-memory database
sqlite_db = create_engine('sqlite://:memory:') # the same
# mysql
mysql_db = create_engine('mysql://localhost/foo')
# oracle via TNS name
oracle_db = create_engine('oracle://scott:tiger@dsn')
# oracle will feed host/port/SID into cx_oracle.makedsn
oracle_db = create_engine('oracle://scott:tiger@127.0.0.1:1521/sidname')
The `Engine` will ask the connection pool for a connection when the `connect()` or `execute()` methods are called. The default connection pool, `QueuePool`, as well as the default connection pool used with SQLite, `SingletonThreadPool`, will open connections to the database on an as-needed basis. As concurrent statements are executed, `QueuePool` will grow its pool of connections to a default size of five, and will allow a default "overflow" of ten. Since the `Engine` is essentially "home base" for the connection pool, it follows that you should keep a single `Engine` per database established within an application, rather than creating a new one for each connection.
#### Custom DBAPI connect() arguments
Custom arguments used when issuing the `connect()` call to the underlying DBAPI may be issued in three distinct ways. String-based arguments can be passed directly from the URL string as query arguments:
{python}
db = create_engine('postgres://scott:tiger@localhost/test?argument1=foo&argument2=bar')
If SQLAlchemy's database connector is aware of a particular query argument, it may convert its type from string to its proper type.
`create_engine` also takes an argument `connect_args` which is an additional dictionary that will be passed to `connect()`. This can be used when arguments of a type other than string are required, and SQLAlchemy's database connector has no type conversion logic present for that parameter:
{python}
db = create_engine('postgres://scott:tiger@localhost/test', connect_args = {'argument1':17, 'argument2':'bar'})
The most customizable connection method of all is to pass a `creator` argument, which specifies a callable that returns a DBAPI connection:
{python}
def connect():
return psycopg.connect(user='scott', host='localhost')
db = create_engine('postgres://', creator=connect)
### Database Engine Options {@name=options}
Keyword options can also be specified to `create_engine()`, following the string URL as follows:
{python}
db = create_engine('postgres://...', encoding='latin1', echo=True)
A list of all standard options, as well as several that are used by particular database dialects, is as follows:
* **assert_unicode=False** - When set to `True` alongside convert_unicode=`True`, asserts that incoming string bind parameters are instances of `unicode`, otherwise raises an error. Only takes effect when `convert_unicode==True`. This flag is also available on the `String` type and its decsendants. New in 0.4.2.
* **connect_args** - a dictionary of options which will be passed directly to the DBAPI's `connect()` method as additional keyword arguments.
* **convert_unicode=False** - if set to True, all String/character based types will convert Unicode values to raw byte values going into the database, and all raw byte values to Python Unicode coming out in result sets. This is an engine-wide method to provide unicode conversion across the board. For unicode conversion on a column-by-column level, use the `Unicode` column type instead, described in [types](rel:types).
* **creator** - a callable which returns a DBAPI connection. This creation function will be passed to the underlying connection pool and will be used to create all new database connections. Usage of this function causes connection parameters specified in the URL argument to be bypassed.
* **echo=False** - if True, the Engine will log all statements as well as a repr() of their parameter lists to the engines logger, which defaults to sys.stdout. The `echo` attribute of `Engine` can be modified at any time to turn logging on and off. If set to the string `"debug"`, result rows will be printed to the standard output as well. This flag ultimately controls a Python logger; see [dbengine_logging](rel:dbengine_logging) at the end of this chapter for information on how to configure logging directly.
* **echo_pool=False** - if True, the connection pool will log all checkouts/checkins to the logging stream, which defaults to sys.stdout. This flag ultimately controls a Python logger; see [dbengine_logging](rel:dbengine_logging) for information on how to configure logging directly.
* **encoding='utf-8'** - the encoding to use for all Unicode translations, both by engine-wide unicode conversion as well as the `Unicode` type object.
* **module=None** - used by database implementations which support multiple DBAPI modules, this is a reference to a DBAPI2 module to be used instead of the engine's default module. For Postgres, the default is psycopg2. For Oracle, its cx_Oracle.
* **pool=None** - an already-constructed instance of `sqlalchemy.pool.Pool`, such as a `QueuePool` instance. If non-None, this pool will be used directly as the underlying connection pool for the engine, bypassing whatever connection parameters are present in the URL argument. For information on constructing connection pools manually, see [pooling](rel:pooling).
* **poolclass=None** - a `sqlalchemy.pool.Pool` subclass, which will be used to create a connection pool instance using the connection parameters given in the URL. Note this differs from `pool` in that you don't actually instantiate the pool in this case, you just indicate what type of pool to be used.
* **max_overflow=10** - the number of connections to allow in connection pool "overflow", that is connections that can be opened above and beyond the pool_size setting, which defaults to five. this is only used with `QueuePool`.
* **pool_size=5** - the number of connections to keep open inside the connection pool. This used with `QueuePool` as well as `SingletonThreadPool`.
* **pool_recycle=-1** - this setting causes the pool to recycle connections after the given number of seconds has passed. It defaults to -1, or no timeout. For example, setting to 3600 means connections will be recycled after one hour. Note that MySQL in particular will **disconnect automatically** if no activity is detected on a connection for eight hours (although this is configurable with the MySQLDB connection itself and the server configuration as well).
* **pool_timeout=30** - number of seconds to wait before giving up on getting a connection from the pool. This is only used with `QueuePool`.
* **strategy='plain'** - the Strategy argument is used to select alternate implementations of the underlying Engine object, which coordinates operations between dialects, compilers, connections, and so on. Currently, the only alternate strategy besides the default value of "plain" is the "threadlocal" strategy, which selects the usage of the `TLEngine` class that provides a modified connection scope for connectionless executions. Connectionless execution as well as further detail on this setting are described in [dbengine_implicit](rel:dbengine_implicit).
* **threaded=True** - used by cx_Oracle; sets the `threaded` parameter of the connection indicating thread-safe usage. cx_Oracle docs indicate setting this flag to `False` will speed performance by 10-15%. While this defaults to `False` in cx_Oracle, SQLAlchemy defaults it to `True`, preferring stability over early optimization.
* **use_ansi=True** - used only by Oracle; when False, the Oracle driver attempts to support a particular "quirk" of Oracle versions 8 and previous, that the LEFT OUTER JOIN SQL syntax is not supported, and the "Oracle join" syntax of using `column1(+)=column2` must be used in order to achieve a LEFT OUTER JOIN.
* **use_oids=False** - used only by Postgres, will enable the column name "oid" as the object ID column, which is also used for the default sort order of tables. Postgres as of 8.1 has object IDs disabled by default.
### More On Connections {@name=connections}
Recall from the beginning of this section that the Engine provides a `connect()` method which returns a `Connection` object. `Connection` is a *proxy* object which maintains a reference to a DBAPI connection instance. The `close()` method on `Connection` does not actually close the DBAPI connection, but instead returns it to the connection pool referenced by the `Engine`. `Connection` will also automatically return its resources to the connection pool when the object is garbage collected, i.e. its `__del__()` method is called. When using the standard C implementation of Python, this method is usually called immediately as soon as the object is dereferenced. With other Python implementations such as Jython, this is not so guaranteed.
The `execute()` methods on both `Engine` and `Connection` can also receive SQL clause constructs as well:
{python}
connection = engine.connect()
result = connection.execute(select([table1], table1.c.col1==5))
for row in result:
print row['col1'], row['col2']
connection.close()
The above SQL construct is known as a `select()`. The full range of SQL constructs available are described in [sql](rel:sql).
Both `Connection` and `Engine` fulfill an interface known as `Connectable` which specifies common functionality between the two objects, namely being able to call `connect()` to return a `Connection` object (`Connection` just returns itself), and being able to call `execute()` to get a result set. Following this, most SQLAlchemy functions and objects which accept an `Engine` as a parameter or attribute with which to execute SQL will also accept a `Connection`. As of SQLAlchemy 0.3.9, this argument is named `bind`.
{python title="Specify Engine or Connection"}
engine = create_engine('sqlite:///:memory:')
# specify some Table metadata
metadata = MetaData()
table = Table('sometable', metadata, Column('col1', Integer))
# create the table with the Engine
table.create(bind=engine)
# drop the table with a Connection off the Engine
connection = engine.connect()
table.drop(bind=connection)
Connection facts:
* the Connection object is **not threadsafe**. While a Connection can be shared among threads using properly synchronized access, this is also not recommended as many DBAPIs have issues with, if not outright disallow, sharing of connection state between threads.
* The Connection object represents a single dbapi connection checked out from the connection pool. In this state, the connection pool has no affect upon the connection, including its expiration or timeout state. For the connection pool to properly manage connections, **connections should be returned to the connection pool (i.e. `connection.close()`) whenever the connection is not in use**. If your application has a need for management of multiple connections or is otherwise long running (this includes all web applications, threaded or not), don't hold a single connection open at the module level.
### Using Transactions with Connection {@name=transactions}
The `Connection` object provides a `begin()` method which returns a `Transaction` object. This object is usually used within a try/except clause so that it is guaranteed to `rollback()` or `commit()`:
{python}
trans = connection.begin()
try:
r1 = connection.execute(table1.select())
connection.execute(table1.insert(), col1=7, col2='this is some data')
trans.commit()
except:
trans.rollback()
raise
The `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.
{python}
# method_a starts a transaction and calls method_b
def method_a(connection):
trans = connection.begin() # open a transaction
try:
method_b(connection)
trans.commit() # transaction is committed here
except:
trans.rollback() # this rolls back the transaction unconditionally
raise
# method_b also starts a transaction
def method_b(connection):
trans = connection.begin() # open a transaction - this runs in the context of method_a's transaction
try:
connection.execute("insert into mytable values ('bat', 'lala')")
connection.execute(mytable.insert(), col1='bat', col2='lala')
trans.commit() # transaction is not committed yet
except:
trans.rollback() # this rolls back the transaction unconditionally
raise
# open a Connection and call method_a
conn = engine.connect()
method_a(conn)
conn.close()
Above, `method_a` is called first, which calls `connection.begin()`. Then it calls `method_b`. When `method_b` calls `connection.begin()`, it just increments a counter that is decremented when it calls `commit()`. If either `method_a` or `method_b` calls `rollback()`, the whole transaction is rolled back. The transaction is not committed until `method_a` calls the `commit()` method. This "nesting" behavior allows the creation of functions which "guarantee" that a transaction will be used if one was not already available, but will automatically participate in an enclosing transaction if one exists.
Note that SQLAlchemy's Object Relational Mapper also provides a way to control transaction scope at a higher level; this is described in [unitofwork_transaction](rel:unitofwork_transaction).
Transaction Facts:
* the Transaction object, just like its parent Connection, is **not threadsafe**.
* SQLAlchemy 0.4 will feature transactions with two-phase commit capability as well as SAVEPOINT capability.
#### Understanding Autocommit
The above transaction example illustrates how to use `Transaction` so that several executions can take part in the same transaction. What happens when we issue an INSERT, UPDATE or DELETE call without using `Transaction`? The answer is **autocommit**. While many DBAPIs implement a flag called `autocommit`, the current SQLAlchemy behavior is such that it implements its own autocommit. This is achieved by searching the statement for strings like INSERT, UPDATE, DELETE, etc. and then issuing a COMMIT automatically if no transaction is in progress.
{python}
conn = engine.connect()
conn.execute("INSERT INTO users VALUES (1, 'john')") # autocommits
### Connectionless Execution, Implicit Execution {@name=implicit}
Recall from the first section we mentioned executing with and without a `Connection`. `Connectionless` execution refers to calling the `execute()` method on an object which is not a `Connection`, which could be on the the `Engine` itself, or could be a constructed SQL object. When we say "implicit", we mean that we are calling the `execute()` method on an object which is neither a `Connection` nor an `Engine` object; this can only be used with constructed SQL objects which have their own `execute()` method, and can be "bound" to an `Engine`. A description of "constructed SQL objects" may be found in [sql](rel:sql).
A summary of all three methods follows below. First, assume the usage of the following `MetaData` and `Table` objects; while we haven't yet introduced these concepts, for now you only need to know that we are representing a database table, and are creating an "executeable" SQL construct which issues a statement to the database. These objects are described in [metadata](rel:metadata).
{python}
meta = MetaData()
users_table = Table('users', meta,
Column('id', Integer, primary_key=True),
Column('name', String(50))
)
Explicit execution delivers the SQL text or constructed SQL expression to the `execute()` method of `Connection`:
{python}
engine = create_engine('sqlite:///file.db')
connection = engine.connect()
result = connection.execute(users_table.select())
for row in result:
# ....
connection.close()
Explicit, connectionless execution delivers the expression to the `execute()` method of `Engine`:
{python}
engine = create_engine('sqlite:///file.db')
result = engine.execute(users_table.select())
for row in result:
# ....
result.close()
Implicit execution is also connectionless, and calls the `execute()` method on the expression itself, utilizing the fact that either an `Engine` or `Connection` has been *bound* to the expression object (binding is discussed further in the next section, [metadata](rel:metadata)):
{python}
engine = create_engine('sqlite:///file.db')
meta.bind = engine
result = users_table.select().execute()
for row in result:
# ....
result.close()
In both "connectionless" examples, the `Connection` is created behind the scenes; the `ResultProxy` returned by the `execute()` call references the `Connection` used to issue the SQL statement. When we issue `close()` on the `ResultProxy`, or if the result set object falls out of scope and is garbage collected, the underlying `Connection` is closed for us, resulting in the DBAPI connection being returned to the pool.
#### Using the Threadlocal Execution Strategy {@name=strategies}
With connectionless execution, each returned `ResultProxy` object references its own distinct DBAPI connection object. This means that multiple executions will result in multiple DBAPI connections being used at the same time; the example below illustrates this:
{python}
db = create_engine('mysql://localhost/test')
# execute one statement and receive results. r1 now references a DBAPI connection resource.
r1 = db.execute("select * from table1")
# execute a second statement and receive results. r2 now references a *second* DBAPI connection resource.
r2 = db.execute("select * from table2")
for row in r1:
...
for row in r2:
...
# release connection 1
r1.close()
# release connection 2
r2.close()
Where above, we have two result sets in scope at the same time, therefore we have two distinct DBAPI connections, both separately checked out from the connection pool, in scope at the same time.
An option exists to `create_engine()` called `strategy="threadlocal"`, which changes this behavior. When this option is used, the `Engine` which is returned by `create_engine()` is a special subclass of engine called `TLEngine`. This engine, when it creates the `Connection` used by a connectionless execution, checks a **threadlocal variable** for an existing DBAPI connection that was already checked out from the pool, within the current thread. If one exists, it uses that one.
The usage of "threadlocal" modifies the underlying behavior of our example above, as follows:
{python title="Threadlocal Strategy"}
db = create_engine('mysql://localhost/test', strategy='threadlocal')
# execute one statement and receive results. r1 now references a DBAPI connection resource.
r1 = db.execute("select * from table1")
# execute a second statement and receive results. r2 now references the *same* resource as r1
r2 = db.execute("select * from table2")
for row in r1:
...
for row in r2:
...
# close r1. the connection is still held by r2.
r1.close()
# close r2. with no more references to the underlying connection resources, they
# are returned to the pool.
r2.close()
Where above, we again have two result sets in scope at the same time, but because they are present in the same thread, there is only **one DBAPI connection in use**.
While the above distinction may not seem like much, it has several potentially desireable effects. One is that you can in some cases reduce the number of concurrent connections checked out from the connection pool, in the case that a `ResultProxy` is still opened and a second statement is issued. A second advantage is that by limiting the number of checked out connections in a thread to just one, you eliminate the issue of deadlocks within a single thread, such as when connection A locks a table, and connection B attempts to read from the same table in the same thread, it will "deadlock" on waiting for connection A to release its lock; the `threadlocal` strategy eliminates this possibility.
A third advantage to the `threadlocal` strategy is that it allows the `Transaction` object to be used in combination with connectionless execution. Recall from the section on transactions, that the `Transaction` is returned by the `begin()` method on a `Connection`; all statements which wish to participate in this transaction must be executed by the same `Connection`, thereby forcing the usage of an explicit connection. However, the `TLEngine` provides a `Transaction` that is local to the current thread; using it, one can issue many "connectionless" statements within a thread and they will all automatically partake in the current transaction, as in the example below:
{python title="threadlocal connection sharing"}
# get a TLEngine
engine = create_engine('mysql://localhost/test', strategy='threadlocal')
engine.begin()
try:
engine.execute("insert into users values (?, ?)", 1, "john")
users.update(users.c.user_id==5).execute(name='ed')
engine.commit()
except:
engine.rollback()
Notice that no `Connection` needed to be used; the `begin()` method on `TLEngine` (which note is not available on the regular `Engine`) created a `Transaction` as well as a `Connection`, and held onto both in a context corresponding to the current thread. Each `execute()` call made use of the same connection, allowing them all to participate in the same transaction.
Complex application flows can take advantage of the "threadlocal" strategy in order to allow many disparate parts of an application to take place in the same transaction automatically. The example below demonstrates several forms of "connectionless execution" as well as some specialized explicit ones:
{python title="threadlocal connection sharing"}
engine = create_engine('mysql://localhost/test', strategy='threadlocal')
def dosomethingimplicit():
table1.execute("some sql")
table1.execute("some other sql")
def dosomethingelse():
table2.execute("some sql")
conn = engine.contextual_connect()
# do stuff with conn
conn.execute("some other sql")
conn.close()
def dosomethingtransactional():
conn = engine.contextual_connect()
trans = conn.begin()
# do stuff
trans.commit()
engine.begin()
try:
dosomethingimplicit()
dosomethingelse()
dosomethingtransactional()
engine.commit()
except:
engine.rollback()
In the above example, the program calls three functions `dosomethingimplicit()`, `dosomethingelse()` and `dosomethingtransactional()`. All three functions use either connectionless execution, or a special function `contextual_connect()` which we will describe in a moment. These two styles of execution both indicate that all executions will use the same connection object. Additionally, the method `dosomethingtransactional()` begins and commits its own `Transaction`. But only one transaction is used, too; it's controlled completely by the `engine.begin()`/`engine.commit()` calls at the bottom. Recall that `Transaction` supports "nesting" behavior, whereby transactions begun on a `Connection` which already has a tranasaction open, will "nest" into the enclosing transaction. Since the transaction opened in `dosomethingtransactional()` occurs using the same connection which already has a transaction begun, it "nests" into that transaction and therefore has no effect on the actual transaction scope (unless it calls `rollback()`).
Some of the functions in the above example make use of a method called `engine.contextual_connect()`. This method is available on both `Engine` as well as `TLEngine`, and returns the `Connection` that applies to the current **connection context**. When using the `TLEngine`, this is just another term for the "thread local connection" that is being used for all connectionless executions. When using just the regular `Engine` (i.e. the "default" strategy), `contextual_connect()` is synonymous with `connect()`. Below we illustrate that two connections opened via `contextual_connect()` at the same time, both reference the same underlying DBAPI connection:
{python title="Contextual Connection"}
# threadlocal strategy
db = create_engine('mysql://localhost/test', strategy='threadlocal')
conn1 = db.contextual_connect()
conn2 = db.contextual_connect()
>>> conn1.connection is conn2.connection
True
The basic idea of `contextual_connect()` is that its the "connection used by connectionless execution". It's different from the `connect()` method in that `connect()` is always used when handling an explicit `Connection`, which will always reference distinct DBAPI connection. Using `connect()` in combination with `TLEngine` allows one to "circumvent" the current thread local context, as in this example where a single statement issues data to the database externally to the current transaction:
{python}
engine.begin()
engine.execute("insert into users values (?, ?)", 1, "john")
connection = engine.connect()
connection.execute(users.update(users.c.user_id==5).execute(name='ed'))
engine.rollback()
In the above example, a thread-local transaction is begun, but is later rolled back. The statement `insert into users values (?, ?)` is executed without using a connection, therefore uses the thread-local transaction. So its data is rolled back when the transaction is rolled back. However, the `users.update()` statement is executed using a distinct `Connection` returned by the `engine.connect()` method, so it therefore is not part of the threadlocal transaction; it autocommits immediately.
### Configuring Logging {@name=logging}
As of the 0.3 series of SQLAlchemy, Python's standard [logging](http://www.python.org/doc/lib/module-logging.html) module is used to implement informational and debug log output. This allows SQLAlchemy's logging to integrate in a standard way with other applications and libraries. The `echo` and `echo_pool` flags that are present on `create_engine()`, as well as the `echo_uow` flag used on `Session`, all interact with regular loggers.
This section assumes familiarity with the above linked logging module. All logging performed by SQLAlchemy exists underneath the `sqlalchemy` namespace, as used by `logging.getLogger('sqlalchemy')`. When logging has been configured (i.e. such as via `logging.basicConfig()`), the general namespace of SA loggers that can be turned on is as follows:
* `sqlalchemy.engine` - controls SQL echoing. set to `logging.INFO` for SQL query output, `logging.DEBUG` for query + result set output.
* `sqlalchemy.pool` - controls connection pool logging. set to `logging.INFO` or lower to log connection pool checkouts/checkins.
* `sqlalchemy.orm` - controls logging of various ORM functions. set to `logging.INFO` for configurational logging as well as unit of work dumps, `logging.DEBUG` for extensive logging during query and flush() operations. Subcategories of `sqlalchemy.orm` include:
* `sqlalchemy.orm.attributes` - logs certain instrumented attribute operations, such as triggered callables
* `sqlalchemy.orm.mapper` - logs Mapper configuration and operations
* `sqlalchemy.orm.unitofwork` - logs flush() operations, including dependency sort graphs and other operations
* `sqlalchemy.orm.strategies` - logs relation loader operations (i.e. lazy and eager loads)
* `sqlalchemy.orm.sync` - logs synchronization of attributes from parent to child instances during a flush()
For example, to log SQL queries as well as unit of work debugging:
{python}
import logging
logging.basicConfig()
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
logging.getLogger('sqlalchemy.orm.unitofwork').setLevel(logging.DEBUG)
By default, the log level is set to `logging.ERROR` within the entire `sqlalchemy` namespace so that no log operations occur, even within an application that has logging enabled otherwise.
The `echo` flags present as keyword arguments to `create_engine()` and others as well as the `echo` property on `Engine`, when set to `True`, will first attempt to ensure that logging is enabled. Unfortunately, the `logging` module provides no way of determining if output has already been configured (note we are referring to if a logging configuration has been set up, not just that the logging level is set). For this reason, any `echo=True` flags will result in a call to `logging.basicConfig()` using sys.stdout as the destination. It also sets up a default format using the level name, timestamp, and logger name. Note that this configuration has the affect of being configured **in addition** to any existing logger configurations. Therefore, **when using Python logging, ensure all echo flags are set to False at all times**, to avoid getting duplicate log lines.
-12
View File
@@ -1,12 +0,0 @@
# -*- coding: utf-8 -*-
<%inherit file="content_layout.html"/>
<%page args="toc, extension, paged"/>
<%namespace name="formatting" file="formatting.html"/>
<%namespace name="nav" file="nav.html"/>
<%namespace name="pydoc" file="pydoc.html"/>
<%def name="title()">SQLAlchemy 0.4 Documentation - Modules and Classes</%def>
<%!
filename = 'docstrings'
%>
-25
View File
@@ -1,25 +0,0 @@
<%inherit file="base.html"/>
<%namespace name="tocns" file="toc.html"/>
<%namespace name="nav" file="nav.html"/>
<%page args="toc, extension"/>
${tocns.toc(toc, extension=extension, paged=False)}
<%def name="title()">
SQLAlchemy Documentation
</%def>
% for file in toc.filenames:
<%
item = toc.get_by_file(file)
%>
<A name="${item.path}"></a>
% if not item.requires_paged:
${nav.pagenav(item=item, paged=False, extension=extension)}
${self.get_namespace(file + '.html').body(toc=toc, extension=extension, paged=False)}
% endif
% endfor
-6
View File
@@ -1,6 +0,0 @@
<%inherit file="base.html"/>
<%page args="toc, extension"/>
<%namespace name="tocns" file="toc.html"/>
${tocns.toc(toc, paged=True, extension=extension)}
-162
View File
@@ -1,162 +0,0 @@
Overview / Installation
============
## Overview
The SQLAlchemy SQL Toolkit and Object Relational Mapper is a comprehensive set of tools for working with databases and Python. It has several distinct areas of functionality which can be used individually or combined together. Its major API components, all public-facing, are illustrated below:
{diagram}
+-----------------------------------------------------------+
| Object Relational Mapper (ORM) |
| [[tutorial]](rel:datamapping) [[docs]](rel:advdatamapping) |
+-----------------------------------------------------------+
+---------+ +------------------------------------+ +--------+
| | | SQL Expression Language | | |
| | | [[tutorial]](rel:sql) [[docs]](rel:docstrings_sqlalchemy.sql.expression) | | |
| | +------------------------------------+ | |
| +-----------------------+ +--------------+ |
| Dialect/Execution | | Schema Management |
| [[docs]](rel:dbengine) | | [[docs]](rel:metadata) |
+---------------------------------+ +-----------------------+
+----------------------+ +----------------------------------+
| Connection Pooling | | Types |
| [[docs]](rel:pooling) | | [[docs]](rel:types) |
+----------------------+ +----------------------------------+
Above, the two most significant front-facing portions of SQLAlchemy are the **Object Relational Mapper** and the **SQL Expression Language**. These are two separate toolkits, one building off the other. SQL Expressions can be used independently of the ORM. When using the ORM, the SQL Expression language is used to establish object-relational configurations as well as in querying.
## Tutorials
* [Object Relational Tutorial](rel:datamapping) - This describes the richest feature of SQLAlchemy, its object relational mapper. If you want to work with higher-level SQL which is constructed automatically for you, as well as management of Python objects, proceed to this tutorial.
* [SQL Expression Tutorial](rel:sql) - The core of SQLAlchemy is its SQL expression language. The SQL Expression Language is a toolkit all its own, independent of the ORM package, which can be used to construct manipulable SQL expressions which can be programmatically constructed, modified, and executed, returning cursor-like result sets. It's a lot more lightweight than the ORM and is appropriate for higher scaling SQL operations. It's also heavily present within the ORM's public facing API, so advanced ORM users will want to master this language as well.
## Reference Documentation
* [Datamapping](rel:advdatamapping) - A comprehensive walkthrough of major ORM patterns and techniques.
* [Session](rel:unitofwork) - A detailed description of SQLAlchemy's Session object
* [Engines](rel:dbengine) - Describes SQLAlchemy's database-connection facilities, including connection documentation and working with connections and transactions.
* [Connection Pools](rel:pooling) - Further detail about SQLAlchemy's connection pool library.
* [Metadata](rel:metadata) - All about schema management using `MetaData` and `Table` objects; reading database schemas into your application, creating and dropping tables, constraints, defaults, sequences, indexes.
* [Types](rel:types) - Datatypes included with SQLAlchemy, their functions, as well as how to create your own types.
* [Plugins](rel:plugins) - Included addons for SQLAlchemy
## Installing SQLAlchemy {@name=sqlalchemy}
Installing SQLAlchemy from scratch is most easily achieved with [setuptools][]. ([setuptools installation][install setuptools]). Just run this from the command-line:
# easy_install SQLAlchemy
This command will download the latest version of SQLAlchemy from the [Python Cheese Shop][pypi] and install it to your system.
[setuptools]: http://peak.telecommunity.com/DevCenter/setuptools
[install setuptools]: http://peak.telecommunity.com/DevCenter/EasyInstall#installation-instructions
[pypi]: http://pypi.python.org/pypi/SQLAlchemy
Otherwise, you can install from the distribution using the `setup.py` script:
# python setup.py install
### Installing a Database API {@name=dbms}
SQLAlchemy is designed to operate with a [DB-API](http://www.python.org/doc/peps/pep-0249/) implementation built for a particular database, and includes support for the most popular databases:
* Postgres: [psycopg2](http://www.initd.org/tracker/psycopg/wiki/PsycopgTwo)
* SQLite: [pysqlite](http://initd.org/tracker/pysqlite), [sqlite3](http://docs.python.org/lib/module-sqlite3.html) (included with Python 2.5 or greater)
* MySQL: [MySQLdb](http://sourceforge.net/projects/mysql-python)
* Oracle: [cx_Oracle](http://www.cxtools.net/default.aspx?nav=home)
* MS-SQL: [pyodbc](http://pyodbc.sourceforge.net/) (recommended), [adodbapi](http://adodbapi.sourceforge.net/) or [pymssql](http://pymssql.sourceforge.net/)
* Firebird: [kinterbasdb](http://kinterbasdb.sourceforge.net/)
* Informix: [informixdb](http://informixdb.sourceforge.net/)
### Checking the Installed SQLAlchemy Version
This documentation covers SQLAlchemy version 0.4. If you're working on a system that already has SQLAlchemy installed, check the version from your Python prompt like this:
{python}
>>> import sqlalchemy
>>> sqlalchemy.__version__ # doctest: +SKIP
0.4.0
## 0.3 to 0.4 Migration {@name=migration}
From version 0.3 to version 0.4 of SQLAlchemy, some conventions have changed. Most of these conventions are available in the most recent releases of the 0.3 series starting with version 0.3.9, so that you can make a 0.3 application compatible with 0.4 in most cases.
This section will detail only those things that have changed in a backwards-incompatible manner. For a full overview of everything that's new and changed, see [WhatsNewIn04](http://www.sqlalchemy.org/trac/wiki/WhatsNewIn04).
### ORM Package is now sqlalchemy.orm {@name=imports}
All symbols related to the SQLAlchemy Object Relational Mapper, i.e. names like `mapper()`, `relation()`, `backref()`, `create_session()` `synonym()`, `eagerload()`, etc. are now only in the `sqlalchemy.orm` package, and **not** in `sqlalchemy`. So if you were previously importing everything on an asterisk:
{python}
from sqlalchemy import *
You should now import separately from orm:
{python}
from sqlalchemy import *
from sqlalchemy.orm import *
Or more commonly, just pull in the names you'll need:
{python}
from sqlalchemy import create_engine, MetaData, Table, Column, types
from sqlalchemy.orm import mapper, relation, backref, create_session
### BoundMetaData is now MetaData {@name=metadata}
The `BoundMetaData` name is removed. Now, you just use `MetaData`. Additionally, the `engine` parameter/attribute is now called `bind`, and `connect()` is deprecated:
{python}
# plain metadata
meta = MetaData()
# metadata bound to an engine
meta = MetaData(engine)
# bind metadata to an engine later
meta.bind = engine
Additionally, `DynamicMetaData` is now known as `ThreadLocalMetaData`.
### Some existing select() methods become generative {@name=generative}
The methods `correlate()`, `order_by()`, and `group_by()` on the `select()` construct now return a **new** select object, and do not change the original one. Additionally, the generative methods `where()`, `column()`, `distinct()`, and several others have been added:
{python}
s = table.select().order_by(table.c.id).where(table.c.x==7)
result = engine.execute(s)
### collection_class behavior is changed {@name=collection}
If you've been using the `collection_class` option on `mapper()`, the requirements for instrumented collections have changed. For an overview, see [advdatamapping_relation_collections](rel:advdatamapping_relation_collections).
### All "engine", "bind_to", "connectable" Keyword Arguments Changed to "bind" {@name=bind}
This is for create/drop statements, sessions, SQL constructs, metadatas:
{python}
myengine = create_engine('sqlite://')
meta = MetaData(myengine)
meta2 = MetaData()
meta2.bind = myengine
session = create_session(bind=myengine)
statement = select([table], bind=myengine)
meta.create_all(bind=myengine)
### All "type" Keyword Arguments Changed to "type_" {@name=type}
This mostly applies to SQL constructs where you pass a type in:
{python}
s = select([mytable], mytable.c.x=bindparam(y, type_=DateTime))
func.now(type_=DateTime)
### Mapper Extensions must return EXT_CONTINUE to continue execution to the next mapper
If you extend the mapper, the methods in your mapper extension must return EXT_CONTINUE to continue executing additional mappers.
-1414
View File
File diff suppressed because it is too large Load Diff
-500
View File
@@ -1,500 +0,0 @@
[alpha_api]: javascript:alphaApi()
[alpha_implementation]: javascript:alphaImplementation()
Database Meta Data {@name=metadata}
==================
### Describing Databases with MetaData {@name=tables}
The core of SQLAlchemy's query and object mapping operations are supported by **database metadata**, which is comprised of Python objects that describe tables and other schema-level objects. These objects can be created by explicitly naming the various components and their properties, using the Table, Column, ForeignKey, Index, and Sequence objects imported from `sqlalchemy.schema`. There is also support for **reflection** of some entities, which means you only specify the *name* of the entities and they are recreated from the database automatically.
A collection of metadata entities is stored in an object aptly named `MetaData`:
{python}
from sqlalchemy import *
metadata = MetaData()
To represent a Table, use the `Table` class:
{python}
users = Table('users', metadata,
Column('user_id', Integer, primary_key = True),
Column('user_name', String(16), nullable = False),
Column('email_address', String(60), key='email'),
Column('password', String(20), nullable = False)
)
user_prefs = Table('user_prefs', metadata,
Column('pref_id', Integer, primary_key=True),
Column('user_id', Integer, ForeignKey("users.user_id"), nullable=False),
Column('pref_name', String(40), nullable=False),
Column('pref_value', String(100))
)
The specific datatypes for each Column, such as Integer, String, etc. are described in [types](rel:types), and exist within the module `sqlalchemy.types` as well as the global `sqlalchemy` namespace.
Foreign keys are most easily specified by the `ForeignKey` object within a `Column` object. For a composite foreign key, i.e. a foreign key that contains multiple columns referencing multiple columns to a composite primary key, an explicit syntax is provided which allows the correct table CREATE statements to be generated:
{python}
# a table with a composite primary key
invoices = Table('invoices', metadata,
Column('invoice_id', Integer, primary_key=True),
Column('ref_num', Integer, primary_key=True),
Column('description', String(60), nullable=False)
)
# a table with a composite foreign key referencing the parent table
invoice_items = Table('invoice_items', metadata,
Column('item_id', Integer, primary_key=True),
Column('item_name', String(60), nullable=False),
Column('invoice_id', Integer, nullable=False),
Column('ref_num', Integer, nullable=False),
ForeignKeyConstraint(['invoice_id', 'ref_num'], ['invoices.invoice_id', 'invoices.ref_num'])
)
Above, the `invoice_items` table will have `ForeignKey` objects automatically added to the `invoice_id` and `ref_num` `Column` objects as a result of the additional `ForeignKeyConstraint` object.
The `MetaData` object supports some handy methods, such as getting a list of Tables in the order (or reverse) of their dependency:
{python}
>>> for t in metadata.table_iterator(reverse=False):
... print t.name
users
user_prefs
And `Table` provides an interface to the table's properties as well as that of its columns:
{python}
employees = Table('employees', metadata,
Column('employee_id', Integer, primary_key=True),
Column('employee_name', String(60), nullable=False, key='name'),
Column('employee_dept', Integer, ForeignKey("departments.department_id"))
)
# access the column "EMPLOYEE_ID":
employees.columns.employee_id
# or just
employees.c.employee_id
# via string
employees.c['employee_id']
# iterate through all columns
for c in employees.c:
# ...
# get the table's primary key columns
for primary_key in employees.primary_key:
# ...
# get the table's foreign key objects:
for fkey in employees.foreign_keys:
# ...
# access the table's MetaData:
employees.metadata
# access the table's bound Engine or Connection, if its MetaData is bound:
employees.bind
# access a column's name, type, nullable, primary key, foreign key
employees.c.employee_id.name
employees.c.employee_id.type
employees.c.employee_id.nullable
employees.c.employee_id.primary_key
employees.c.employee_dept.foreign_key
# get the "key" of a column, which defaults to its name, but can
# be any user-defined string:
employees.c.name.key
# access a column's table:
employees.c.employee_id.table is employees
>>> True
# get the table related by a foreign key
fcolumn = employees.c.employee_dept.foreign_key.column.table
#### Binding MetaData to an Engine or Connection {@name=binding}
A `MetaData` object can be associated with an `Engine` or an individual `Connection`; this process is called **binding**. The term used to describe "an engine or a connection" is often referred to as a **connectable**. Binding allows the `MetaData` and the elements which it contains to perform operations against the database directly, using the connection resources to which it's bound. Common operations which are made more convenient through binding include being able to generate SQL constructs which know how to execute themselves, creating `Table` objects which query the database for their column and constraint information, and issuing CREATE or DROP statements.
To bind `MetaData` to an `Engine`, use the `bind` attribute:
{python}
engine = create_engine('sqlite://', **kwargs)
# create MetaData
meta = MetaData()
# bind to an engine
meta.bind = engine
Once this is done, the `MetaData` and its contained `Table` objects can access the database directly:
{python}
meta.create_all() # issue CREATE statements for all tables
# describe a table called 'users', query the database for its columns
users_table = Table('users', meta, autoload=True)
# generate a SELECT statement and execute
result = users_table.select().execute()
Note that the feature of binding engines is **completely optional**. All of the operations which take advantage of "bound" `MetaData` also can be given an `Engine` or `Connection` explicitly with which to perform the operation. The equivalent "non-bound" of the above would be:
{python}
meta.create_all(engine) # issue CREATE statements for all tables
# 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())
#### Reflecting Tables
A `Table` object can be created without specifying any of its contained attributes, using the argument `autoload=True` in conjunction with the table's name and possibly its schema (if not the databases "default" schema). (You can also specify a list or set of column names to autoload as the kwarg include_columns, if you only want to load a subset of the columns in the actual database.) This will issue the appropriate queries to the database in order to locate all properties of the table required for SQLAlchemy to use it effectively, including its column names and datatypes, foreign and primary key constraints, and in some cases its default-value generating attributes. To use `autoload=True`, the table's `MetaData` object need be bound to an `Engine` or `Connection`, or alternatively the `autoload_with=<some connectable>` argument can be passed. Below we illustrate autoloading a table and then iterating through the names of its columns:
{python}
>>> messages = Table('messages', meta, autoload=True)
>>> [c.name for c in messages.columns]
['message_id', 'message_name', 'date']
Note that if a reflected table has a foreign key referencing another table, the related `Table` object will be automatically created within the `MetaData` object if it does not exist already. Below, suppose table `shopping_cart_items` references a table `shopping_carts`. After reflecting, the `shopping carts` table is present:
{python}
>>> shopping_cart_items = Table('shopping_cart_items', meta, autoload=True)
>>> 'shopping_carts' in meta.tables:
True
To get direct access to 'shopping_carts', simply instantiate it via the `Table` constructor. `Table` uses a special contructor that will return the already created `Table` instance if its already present:
{python}
shopping_carts = Table('shopping_carts', meta)
Of course, its a good idea to use `autoload=True` with the above table regardless. This is so that if it hadn't been loaded already, the operation will load the table. The autoload operation only occurs for the table if it hasn't already been loaded; once loaded, new calls to `Table` will not re-issue any reflection queries.
##### Overriding Reflected Columns {@name=overriding}
Individual columns can be overridden with explicit values when reflecting tables; this is handy for specifying custom datatypes, constraints such as primary keys that may not be configured within the database, etc.
{python}
>>> mytable = Table('mytable', meta,
... Column('id', Integer, primary_key=True), # override reflected 'id' to have primary key
... Column('mydata', Unicode(50)), # override reflected 'mydata' to be Unicode
... autoload=True)
#### Specifying the Schema Name {@name=schema}
Some databases support the concept of multiple schemas. A `Table` can reference this by specifying the `schema` keyword argument:
{python}
financial_info = Table('financial_info', meta,
Column('id', Integer, primary_key=True),
Column('value', String(100), nullable=False),
schema='remote_banks'
)
Within the `MetaData` collection, this table will be identified by the combination of `financial_info` and `remote_banks`. If another table called `financial_info` is referenced without the `remote_banks` schema, it will refer to a different `Table`. `ForeignKey` objects can reference columns in this table using the form `remote_banks.financial_info.id`.
#### ON UPDATE and ON DELETE {@name=onupdate}
`ON UPDATE` and `ON DELETE` clauses to a table create are specified within the `ForeignKeyConstraint` object, using the `onupdate` and `ondelete` keyword arguments:
{python}
foobar = Table('foobar', meta,
Column('id', Integer, primary_key=True),
Column('lala', String(40)),
ForeignKeyConstraint(['lala'],['hoho.lala'], onupdate="CASCADE", ondelete="CASCADE"))
Note that these clauses are not supported on SQLite, and require `InnoDB` tables when used with MySQL. They may also not be supported on other databases.
#### Other Options {@name=options}
`Tables` may support database-specific options, such as MySQL's `engine` option that can specify "MyISAM", "InnoDB", and other backends for the table:
{python}
addresses = Table('engine_email_addresses', meta,
Column('address_id', Integer, primary_key = True),
Column('remote_user_id', Integer, ForeignKey(users.c.user_id)),
Column('email_address', String(20)),
mysql_engine='InnoDB'
)
### Creating and Dropping Database Tables {@name=creating}
Creating and dropping individual tables can be done via the `create()` and `drop()` methods of `Table`; these methods take an optional `bind` parameter which references an `Engine` or a `Connection`. If not supplied, the `Engine` bound to the `MetaData` will be used, else an error is raised:
{python}
meta = MetaData()
meta.bind = 'sqlite:///:memory:'
employees = Table('employees', meta,
Column('employee_id', Integer, primary_key=True),
Column('employee_name', String(60), nullable=False, key='name'),
Column('employee_dept', Integer, ForeignKey("departments.department_id"))
)
{sql}employees.create()
CREATE TABLE employees(
employee_id SERIAL NOT NULL PRIMARY KEY,
employee_name VARCHAR(60) NOT NULL,
employee_dept INTEGER REFERENCES departments(department_id)
)
{}
`drop()` method:
{python}
{sql}employees.drop(bind=e)
DROP TABLE employees
{}
The `create()` and `drop()` methods also support an optional keyword argument `checkfirst` which will issue the database's appropriate pragma statements to check if the table exists before creating or dropping:
{python}
employees.create(bind=e, checkfirst=True)
employees.drop(checkfirst=False)
Entire groups of Tables can be created and dropped directly from the `MetaData` object with `create_all()` and `drop_all()`. These methods always check for the existence of each table before creating or dropping. Each method takes an optional `bind` keyword argument which can reference an `Engine` or a `Connection`. If no engine is specified, the underlying bound `Engine`, if any, is used:
{python}
engine = create_engine('sqlite:///:memory:')
metadata = MetaData()
users = Table('users', metadata,
Column('user_id', Integer, primary_key = True),
Column('user_name', String(16), nullable = False),
Column('email_address', String(60), key='email'),
Column('password', String(20), nullable = False)
)
user_prefs = Table('user_prefs', metadata,
Column('pref_id', Integer, primary_key=True),
Column('user_id', Integer, ForeignKey("users.user_id"), nullable=False),
Column('pref_name', String(40), nullable=False),
Column('pref_value', String(100))
)
{sql}metadata.create_all(bind=engine)
PRAGMA table_info(users){}
CREATE TABLE users(
user_id INTEGER NOT NULL PRIMARY KEY,
user_name VARCHAR(16) NOT NULL,
email_address VARCHAR(60),
password VARCHAR(20) NOT NULL
)
PRAGMA table_info(user_prefs){}
CREATE TABLE user_prefs(
pref_id INTEGER NOT NULL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(user_id),
pref_name VARCHAR(40) NOT NULL,
pref_value VARCHAR(100)
)
### Column Insert/Update Defaults {@name=defaults}
SQLAlchemy includes several constructs which provide default values provided during INSERT and UPDATE statements. The defaults may be provided as Python constants, Python functions, or SQL expressions, and the SQL expressions themselves may be "pre-executed", executed inline within the insert/update statement itself, or can be created as a SQL level "default" placed on the table definition itself. A "default" value by definition is only invoked if no explicit value is passed into the INSERT or UPDATE statement.
#### Pre-Executed Python Functions {@name=preexecute_functions}
The "default" keyword argument on Column can reference a Python value or callable which is invoked at the time of an insert:
{python}
# a function which counts upwards
i = 0
def mydefault():
global i
i += 1
return i
t = Table("mytable", meta,
# function-based default
Column('id', Integer, primary_key=True, default=mydefault),
# a scalar default
Column('key', String(10), default="default")
)
Similarly, the "onupdate" keyword does the same thing for update statements:
{python}
import datetime
t = Table("mytable", meta,
Column('id', Integer, primary_key=True),
# define 'last_updated' to be populated with datetime.now()
Column('last_updated', DateTime, onupdate=datetime.now),
)
#### Pre-executed and Inline SQL Expressions {@name=sqlexpression}
The "default" and "onupdate" keywords may also be passed SQL expressions, including select statements or direct function calls:
{python}
t = Table("mytable", meta,
Column('id', Integer, primary_key=True),
# define 'create_date' to default to now()
Column('create_date', DateTime, default=func.now()),
# define 'key' to pull its default from the 'keyvalues' table
Column('key', String(20), default=keyvalues.select(keyvalues.c.type='type1', limit=1))
# define 'last_modified' to use the current_timestamp SQL function on update
Column('last_modified', DateTime, onupdate=func.current_timestamp())
)
The above SQL functions are usually executed "inline" with the INSERT or UPDATE statement being executed. In some cases, the function is "pre-executed" and its result pre-fetched explicitly. This happens under the following circumstances:
* the column is a primary key column
* the database dialect does not support a usable `cursor.lastrowid` accessor (or equivalent); this currently includes Postgres, Oracle, and Firebird.
* the statement is a single execution, i.e. only supplies one set of parameters and doesn't use "executemany" behavior
* the `inline=True` flag is not set on the `Insert()` or `Update()` construct.
For a statement execution which is not an executemany, the returned `ResultProxy` will contain a collection accessible via `result.postfetch_cols()` which contains a list of all `Column` objects which had an inline-executed default. Similarly, all parameters which were bound to the statement, including all Python and SQL expressions which were pre-executed, are present in the `last_inserted_params()` or `last_updated_params()` collections on `ResultProxy`. The `last_inserted_ids()` collection contains a list of primary key values for the row inserted.
#### DDL-Level Defaults {@name=passive}
A variant on a SQL expression default is the `PassiveDefault`, which gets placed in the CREATE TABLE statement during a `create()` operation:
{python}
t = Table('test', meta,
Column('mycolumn', DateTime, PassiveDefault(text("sysdate")))
)
A create call for the above table will produce:
{code}
CREATE TABLE test (
mycolumn datetime default sysdate
)
The behavior of `PassiveDefault` is similar to that of a regular SQL default; if it's placed on a primary key column for a database which doesn't have a way to "postfetch" the ID, and the statement is not "inlined", the SQL expression is pre-executed; otherwise, SQLAlchemy lets the default fire off on the database side normally.
#### Defining Sequences {@name=sequences}
A table with a sequence looks like:
{python}
table = Table("cartitems", meta,
Column("cart_id", Integer, Sequence('cart_id_seq'), primary_key=True),
Column("description", String(40)),
Column("createdate", DateTime())
)
The `Sequence` object works a lot like the `default` keyword on `Column`, except that it only takes effect on a database which supports sequences. When used with a database that does not support sequences, the `Sequence` object has no effect; therefore it's safe to place on a table which is used against multiple database backends. The same rules for pre- and inline execution apply.
When the `Sequence` is associated with a table, CREATE and DROP statements issued for that table will also issue CREATE/DROP for the sequence object as well, thus "bundling" the sequence object with its parent table.
The flag `optional=True` on `Sequence` will produce a sequence that is only used on databases which have no "autoincrementing" capability. For example, Postgres supports primary key generation using the SERIAL keyword, whereas Oracle has no such capability. Therefore, a `Sequence` placed on a primary key column with `optional=True` will only be used with an Oracle backend but not Postgres.
A sequence can also be executed standalone, using an `Engine` or `Connection`, returning its next value in a database-independent fashion:
{python}
seq = Sequence('some_sequence')
nextid = connection.execute(seq)
### Defining Constraints and Indexes {@name=constraints}
#### UNIQUE Constraint
Unique constraints can be created anonymously on a single column using the `unique` keyword on `Column`. Explicitly named unique constraints and/or those with multiple columns are created via the `UniqueConstraint` table-level construct.
{python}
meta = MetaData()
mytable = Table('mytable', meta,
# per-column anonymous unique constraint
Column('col1', Integer, unique=True),
Column('col2', Integer),
Column('col3', Integer),
# explicit/composite unique constraint. 'name' is optional.
UniqueConstraint('col2', 'col3', name='uix_1')
)
#### CHECK Constraint
Check constraints can be named or unnamed and can be created at the Column or Table level, using the `CheckConstraint` construct. The text of the check constraint is passed directly through to the database, so there is limited "database independent" behavior. Column level check constraints generally should only refer to the column to which they are placed, while table level constraints can refer to any columns in the table.
Note that some databases do not actively support check constraints such as MySQL and sqlite.
{python}
meta = MetaData()
mytable = Table('mytable', meta,
# per-column CHECK constraint
Column('col1', Integer, CheckConstraint('col1>5')),
Column('col2', Integer),
Column('col3', Integer),
# table level CHECK constraint. 'name' is optional.
CheckConstraint('col2 > col3 + 5', name='check1')
)
#### Indexes
Indexes can be created anonymously (using an auto-generated name "ix_&lt;column label&gt;") for a single column using the inline `index` keyword on `Column`, which also modifies the usage of `unique` to apply the uniqueness to the index itself, instead of adding a separate UNIQUE constraint. For indexes with specific names or which encompass more than one column, use the `Index` construct, which requires a name.
Note that the `Index` construct is created **externally** to the table which it corresponds, using `Column` objects and not strings.
{python}
meta = MetaData()
mytable = Table('mytable', meta,
# an indexed column, with index "ix_mytable_col1"
Column('col1', Integer, index=True),
# a uniquely indexed column with index "ix_mytable_col2"
Column('col2', Integer, index=True, unique=True),
Column('col3', Integer),
Column('col4', Integer),
Column('col5', Integer),
Column('col6', Integer),
)
# place an index on col3, col4
Index('idx_col34', mytable.c.col3, mytable.c.col4)
# place a unique index on col5, col6
Index('myindex', mytable.c.col5, mytable.c.col6, unique=True)
The `Index` objects will be created along with the CREATE statements for the table itself. An index can also be created on its own independently of the table:
{python}
# create a table
sometable.create()
# define an index
i = Index('someindex', sometable.c.col5)
# create the index, will use the table's bound connectable if the `bind` keyword argument not specified
i.create()
### Adapting Tables to Alternate Metadata {@name=adapting}
A `Table` object created against a specific `MetaData` object can be re-created against a new MetaData using the `tometadata` method:
{python}
# create two metadata
meta1 = MetaData('sqlite:///querytest.db')
meta2 = MetaData()
# load 'users' from the sqlite engine
users_table = Table('users', meta1, autoload=True)
# create the same Table object for the plain metadata
users_table_2 = users_table.tometadata(meta2)
-1133
View File
File diff suppressed because it is too large Load Diff
-499
View File
@@ -1,499 +0,0 @@
Plugins {@name=plugins}
======================
SQLAlchemy has a variety of extensions available which provide extra functionality to SA, either via explicit usage or by augmenting the core behavior. Several of these extensions are designed to work together.
### associationproxy
**Author:** Mike Bayer and Jason Kirtland<br/>
**Version:** 0.3.1 or greater
`associationproxy` is used to create a simplified, read/write view of a relationship. It can be used to cherry-pick fields from a collection of related objects or to greatly simplify access to associated objects in an association relationship.
#### Simplifying Relations
{python}
users_table = Table('users', metadata,
Column('id', Integer, primary_key=True),
Column('name', String(64)),
)
keywords_table = Table('keywords', metadata,
Column('id', Integer, primary_key=True),
Column('keyword', String(64))
)
userkeywords_table = Table('userkeywords', metadata,
Column('user_id', Integer, ForeignKey("users.id"),
primary_key=True),
Column('keyword_id', Integer, ForeignKey("keywords.id"),
primary_key=True)
)
class User(object):
def __init__(self, name):
self.name = name
class Keyword(object):
def __init__(self, keyword):
self.keyword = keyword
mapper(User, users_table, properties={
'kw': relation(Keyword, secondary=userkeywords_table)
})
mapper(Keyword, keywords_table)
Above are three simple tables, modeling users, keywords and a many-to-many relationship between the two. These ``Keyword`` objects are little more than a container for a name, and accessing them via the relation is awkward:
{python}
user = User('jek')
user.kw.append(Keyword('cheese inspector'))
print user.kw
# [<__main__.Keyword object at 0xb791ea0c>]
print user.kw[0].keyword
# 'cheese inspector'
print [keyword.keyword for keyword in u._keywords]
# ['cheese inspector']
With ``association_proxy`` you have a "view" of the relation that contains just the `.keyword` of the related objects. The proxy is a Python property, and unlike the mapper relation, is defined in your class:
{python}
from sqlalchemy.ext.associationproxy import association_proxy
class User(object):
def __init__(self, name):
self.name = name
# proxy the 'keyword' attribute from the 'kw' relation
keywords = association_proxy('kw', 'keyword')
# ...
>>> user.kw
[<__main__.Keyword object at 0xb791ea0c>]
>>> user.keywords
['cheese inspector']
>>> user.keywords.append('snack ninja')
>>> user.keywords
['cheese inspector', 'snack ninja']
>>> user.kw
[<__main__.Keyword object at 0x9272a4c>, <__main__.Keyword object at 0xb7b396ec>]
The proxy is read/write. New associated objects are created on demand when values are added to the proxy, and modifying or removing an entry through the proxy also affects the underlying collection.
- The association proxy property is backed by a mapper-defined relation, either a collection or scalar.
- You can access and modify both the proxy and the backing relation. Changes in one are immediate in the other.
- The proxy acts like the type of the underlying collection. A list gets a list-like proxy, a dict a dict-like proxy, and so on.
- Multiple proxies for the same relation are fine.
- Proxies are lazy, and won't triger a load of the backing relation until they are accessed.
- The relation is inspected to determine the type of the related objects.
- To construct new instances, the type is called with the value being assigned, or key and value for dicts.
- A ``creator`` function can be used to create instances instead.
Above, the ``Keyword.__init__`` takes a single argument ``keyword``, which maps conveniently to the value being set through the proxy. A ``creator`` function could have been used instead if more flexiblity was required.
Because the proxies are backed a regular relation collection, all of the usual hooks and patterns for using collections are still in effect. The most convenient behavior is the automatic setting of "parent"-type relationships on assignment. In the example above, nothing special had to be done to associate the Keyword to the User. Simply adding it to the collection is sufficient.
#### Simplifying Association Object Relations
Association proxies are also useful for keeping [association objects](rel:datamapping_association) out the way during regular use. For example, the ``userkeywords`` table might have a bunch of auditing columns that need to get updated when changes are made- columns that are updated but seldom, if ever, accessed in your application. A proxy can provide a very natural access pattern for the relation.
{python}
from sqlalchemy.ext.associationproxy import association_proxy
# users_table and keywords_table tables as above, then:
userkeywords_table = Table('userkeywords', metadata,
Column('user_id', Integer, ForeignKey("users.id"), primary_key=True),
Column('keyword_id', Integer, ForeignKey("keywords.id"), primary_key=True),
# add some auditing columns
Column('updated_at', DateTime, default=datetime.now),
Column('updated_by', Integer, default=get_current_uid, onupdate=get_current_uid),
)
def _create_uk_by_keyword(keyword):
"""A creator function."""
return UserKeyword(keyword=keyword)
class User(object):
def __init__(self, name):
self.name = name
keywords = association_proxy('user_keywords', 'keyword', creator=_create_uk_by_keyword)
class Keyword(object):
def __init__(self, keyword):
self.keyword = keyword
def __repr__(self):
return 'Keyword(%s)' % repr(self.keyword)
class UserKeyword(object):
def __init__(self, user=None, keyword=None):
self.user = user
self.keyword = keyword
mapper(User, users_table, properties={
'user_keywords': relation(UserKeyword)
})
mapper(Keyword, keywords_table)
mapper(UserKeyword, userkeywords_table, properties={
'user': relation(User),
'keyword': relation(Keyword),
})
user = User('log')
kw1 = Keyword('new_from_blammo')
# Adding a Keyword requires creating a UserKeyword association object
user.user_keywords.append(UserKeyword(user, kw1))
# And accessing Keywords requires traverrsing UserKeywords
print user.user_keywords[0]
# <__main__.UserKeyword object at 0xb79bbbec>
print user.user_keywords[0].keyword
# Keyword('new_from_blammo')
# Lots of work.
# It's much easier to go through the association proxy!
for kw in (Keyword('its_big'), Keyword('its_heavy'), Keyword('its_wood')):
user.keywords.append(kw)
print user.keywords
# [Keyword('new_from_blammo'), Keyword('its_big'), Keyword('its_heavy'), Keyword('its_wood')]
#### Building Complex Views
{python}
stocks = Table("stocks", meta,
Column('symbol', String(10), primary_key=True),
Column('description', String(100), nullable=False),
Column('last_price', Numeric)
)
brokers = Table("brokers", meta,
Column('id', Integer,primary_key=True),
Column('name', String(100), nullable=False)
)
holdings = Table("holdings", meta,
Column('broker_id', Integer, ForeignKey('brokers.id'), primary_key=True),
Column('symbol', String(10), ForeignKey('stocks.symbol'), primary_key=True),
Column('shares', Integer)
)
Above are three tables, modeling stocks, their brokers and the number of shares of a stock held by each broker. This situation is quite different from the association example above. `shares` is a _property of the relation_, an important one that we need to use all the time.
For this example, it would be very convenient if `Broker` objects had a dictionary collection that mapped `Stock` instances to the shares held for each. That's easy.
{python}
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm.collections import attribute_mapped_collection
def _create_holding(stock, shares):
"""A creator function, constructs Holdings from Stock and share quantity."""
return Holding(stock=stock, shares=shares)
class Broker(object):
def __init__(self, name):
self.name = name
holdings = association_proxy('by_stock', 'shares', creator=_create_holding)
class Stock(object):
def __init__(self, symbol, description=None):
self.symbol = symbol
self.description = description
self.last_price = 0
class Holding(object):
def __init__(self, broker=None, stock=None, shares=0):
self.broker = broker
self.stock = stock
self.shares = shares
mapper(Stock, stocks_table)
mapper(Broker, brokers_table, properties={
'by_stock': relation(Holding,
collection_class=attribute_mapped_collection('stock'))
})
mapper(Holding, holdings_table, properties={
'stock': relation(Stock),
'broker': relation(Broker)
})
Above, we've set up the 'by_stock' relation collection to act as a dictionary, using the `.stock` property of each Holding as a key.
Populating and accessing that dictionary manually is slightly inconvenient because of the complexity of the Holdings association object:
{python}
stock = Stock('ZZK')
broker = Broker('paj')
broker.holdings[stock] = Holding(broker, stock, 10)
print broker.holdings[stock].shares
# 10
The `by_stock` proxy we've added to the `Broker` class hides the details of the `Holding` while also giving access to `.shares`:
{python}
for stock in (Stock('JEK'), Stock('STPZ')):
broker.holdings[stock] = 123
for stock, shares in broker.holdings.items():
print stock, shares
# lets take a peek at that holdings_table after committing changes to the db
print list(holdings_table.select().execute())
# [(1, 'ZZK', 10), (1, 'JEK', 123), (1, 'STEPZ', 123)]
Further examples can be found in the `examples/` directory in the SQLAlchemy distribution.
The `association_proxy` convenience function is not present in SQLAlchemy versions 0.3.1 through 0.3.7, instead instantiate the class directly:
{python}
from sqlalchemy.ext.associationproxy import AssociationProxy
class Article(object):
keywords = AssociationProxy('keyword_associations', 'keyword')
### orderinglist
**Author:** Jason Kirtland
`orderinglist` is a helper for mutable ordered relations. It will intercept
list operations performed on a relation collection and automatically
synchronize changes in list position with an attribute on the related objects.
(See [advdatamapping_properties_entitycollections](rel:advdatamapping_properties_customcollections) for more information on the general pattern.)
Example: Two tables that store slides in a presentation. Each slide
has a number of bullet points, displayed in order by the 'position'
column on the bullets table. These bullets can be inserted and re-ordered
by your end users, and you need to update the 'position' column of all
affected rows when changes are made.
{python}
slides_table = Table('Slides', metadata,
Column('id', Integer, primary_key=True),
Column('name', String))
bullets_table = Table('Bullets', metadata,
Column('id', Integer, primary_key=True),
Column('slide_id', Integer, ForeignKey('Slides.id')),
Column('position', Integer),
Column('text', String))
class Slide(object):
pass
class Bullet(object):
pass
mapper(Slide, slides_table, properties={
'bullets': relation(Bullet, order_by=[bullets_table.c.position])
})
mapper(Bullet, bullets_table)
The standard relation mapping will produce a list-like attribute on each Slide
containing all related Bullets, but coping with changes in ordering is totally
your responsibility. If you insert a Bullet into that list, there is no
magic- it won't have a position attribute unless you assign it it one, and
you'll need to manually renumber all the subsequent Bullets in the list to
accommodate the insert.
An `orderinglist` can automate this and manage the 'position' attribute on all
related bullets for you.
{python}
mapper(Slide, slides_table, properties={
'bullets': relation(Bullet,
collection_class=ordering_list('position'),
order_by=[bullets_table.c.position])
})
mapper(Bullet, bullets_table)
s = Slide()
s.bullets.append(Bullet())
s.bullets.append(Bullet())
s.bullets[1].position
>>> 1
s.bullets.insert(1, Bullet())
s.bullets[2].position
>>> 2
Use the `ordering_list` function to set up the `collection_class` on relations
(as in the mapper example above). This implementation depends on the list
starting in the proper order, so be SURE to put an order_by on your relation.
`ordering_list` takes the name of the related object's ordering attribute as
an argument. By default, the zero-based integer index of the object's
position in the `ordering_list` is synchronized with the ordering attribute:
index 0 will get position 0, index 1 position 1, etc. To start numbering at 1
or some other integer, provide `count_from=1`.
Ordering values are not limited to incrementing integers. Almost any scheme
can implemented by supplying a custom `ordering_func` that maps a Python list
index to any value you require. See the [module
documentation](rel:docstrings_sqlalchemy.ext.orderinglist) for more
information, and also check out the unit tests for examples of stepped
numbering, alphabetical and Fibonacci numbering.
### SqlSoup
**Author:** Jonathan Ellis
SqlSoup creates mapped classes on the fly from tables, which are automatically reflected from the database based on name. It is essentially a nicer version of the "row data gateway" pattern.
{python}
>>> from sqlalchemy.ext.sqlsoup import SqlSoup
>>> soup = SqlSoup('sqlite:///')
>>> db.users.select(order_by=[db.users.c.name])
[MappedUsers(name='Bhargan Basepair',email='basepair@example.edu',password='basepair',classname=None,admin=1),
MappedUsers(name='Joe Student',email='student@example.edu',password='student',classname=None,admin=0)]
Full SqlSoup documentation is on the [SQLAlchemy Wiki](http://www.sqlalchemy.org/trac/wiki/SqlSoup).
### Deprecated Extensions
A lot of our extensions are deprecated. But this is a good thing. Why ? Because all of them have been refined and focused, and rolled into the core of SQLAlchemy (or in the case of `ActiveMapper`, it's become **Elixir**). So they aren't removed, they've just graduated into fully integrated features. Below we describe a set of extensions which are present in 0.4 but are deprecated.
#### SelectResults
**Author:** Jonas Borgström
*NOTE:* As of verison 0.3.6 of SQLAlchemy, most behavior of `SelectResults` has been rolled into the base `Query` object. Explicit usage of `SelectResults` is therefore no longer needed.
`SelectResults` gives transformative behavior to the results returned from the `select` and `select_by` methods of `Query`.
{python}
from sqlalchemy.ext.selectresults import SelectResults
query = session.query(MyClass)
res = SelectResults(query)
res = res.filter(table.c.column == "something") # adds a WHERE clause (or appends to the existing via "and")
res = res.order_by([table.c.column]) # adds an ORDER BY clause
for x in res[:10]: # Fetch and print the top ten instances - adds OFFSET 0 LIMIT 10 or equivalent
print x.column2
# evaluate as a list, which executes the query
x = list(res)
# Count how many instances that have column2 > 42
# and column == "something"
print res.filter(table.c.column2 > 42).count()
# select() is a synonym for filter()
session.query(MyClass).select(mytable.c.column=="something").order_by([mytable.c.column])[2:7]
An important facet of SelectResults is that the actual SQL execution does not occur until the object is used in a list or iterator context. This means you can call any number of transformative methods (including `filter`, `order_by`, list range expressions, etc) before any SQL is actually issued.
Configuration of SelectResults may be per-Query, per Mapper, or per application:
{python}
from sqlalchemy.ext.selectresults import SelectResults, SelectResultsExt
# construct a SelectResults for an individual Query
sel = SelectResults(session.query(MyClass))
# construct a Mapper where the Query.select()/select_by() methods will return a SelectResults:
mapper(MyClass, mytable, extension=SelectResultsExt())
# globally configure all Mappers to return SelectResults, using the "selectresults" mod
import sqlalchemy.mods.selectresults
SelectResults greatly enhances querying and is highly recommended. For example, heres an example of constructing a query using a combination of joins and outerjoins:
{python}
mapper(User, users_table, properties={
'orders':relation(mapper(Order, orders_table, properties={
'items':relation(mapper(Item, items_table))
}))
})
session = create_session()
query = SelectResults(session.query(User))
result = query.outerjoin_to('orders').outerjoin_to('items').select(or_(Order.c.order_id==None,Item.c.item_id==2))
For a full listing of methods, see the [generated documentation](rel:docstrings_sqlalchemy.ext.selectresults).
#### SessionContext
**Author:** Daniel Miller
The `SessionContext` extension is still available in the 0.4 release of SQLAlchemy, but has been deprecated in favor of the [scoped_session()](rel:unitofwork_contextual) function, which provides a class-like object that constructs a `Session` on demand which references a thread-local scope.
For docs on `SessionContext`, see the SQLAlchemy 0.3 documentation.
#### assignmapper
**Author:** Mike Bayer
The `assignmapper` extension is still available in the 0.4 release of SQLAlchemy, but has been deprecated in favor of the [scoped_session()](rel:unitofwork_contextual) function, which provides a `mapper` callable that works similarly to `assignmapper`.
For docs on `assignmapper`, see the SQLAlchemy 0.3 documentation.
#### ActiveMapper
**Author:** Jonathan LaCour
Please note that ActiveMapper has been deprecated in favor of [Elixir](http://elixir.ematia.de/), a more comprehensive solution to declarative mapping, of which Jonathan is a co-author.
ActiveMapper is a so-called "declarative layer" which allows the construction of a class, a `Table`, and a `Mapper` all in one step:
{python}
class Person(ActiveMapper):
class mapping:
id = column(Integer, primary_key=True)
full_name = column(String)
first_name = column(String)
middle_name = column(String)
last_name = column(String)
birth_date = column(DateTime)
ssn = column(String)
gender = column(String)
home_phone = column(String)
cell_phone = column(String)
work_phone = column(String)
prefs_id = column(Integer, foreign_key=ForeignKey('preferences.id'))
addresses = one_to_many('Address', colname='person_id', backref='person')
preferences = one_to_one('Preferences', colname='pref_id', backref='person')
def __str__(self):
s = '%s\n' % self.full_name
s += ' * birthdate: %s\n' % (self.birth_date or 'not provided')
s += ' * fave color: %s\n' % (self.preferences.favorite_color or 'Unknown')
s += ' * personality: %s\n' % (self.preferences.personality_type or 'Unknown')
for address in self.addresses:
s += ' * address: %s\n' % address.address_1
s += ' %s, %s %s\n' % (address.city, address.state, address.postal_code)
return s
class Preferences(ActiveMapper):
class mapping:
__table__ = 'preferences'
id = column(Integer, primary_key=True)
favorite_color = column(String)
personality_type = column(String)
class Address(ActiveMapper):
class mapping:
id = column(Integer, primary_key=True)
type = column(String)
address_1 = column(String)
city = column(String)
state = column(String)
postal_code = column(String)
person_id = column(Integer, foreign_key=ForeignKey('person.id'))
More discussion on ActiveMapper can be found at [Jonathan LaCour's Blog](http://cleverdevil.org/computing/35/declarative-mapping-with-sqlalchemy) as well as the [SQLAlchemy Wiki](http://www.sqlalchemy.org/trac/wiki/ActiveMapper).
-95
View File
@@ -1,95 +0,0 @@
Connection Pooling {@name=pooling}
======================
This section describes the connection pool module of SQLAlchemy. The `Pool` object it provides is normally embedded within an `Engine` instance. For most cases, explicit access to the pool module is not required. However, the `Pool` object can be used on its own, without the rest of SA, to manage DBAPI connections; this section describes that usage. Also, this section will describe in more detail how to customize the pooling strategy used by an `Engine`.
At the base of any database helper library is a system of efficiently acquiring connections to the database. Since the establishment of a database connection is typically a somewhat expensive operation, an application needs a way to get at database connections repeatedly without incurring the full overhead each time. Particularly for server-side web applications, a connection pool is the standard way to maintain a "pool" of database connections which are used over and over again among many requests. Connection pools typically are configured to maintain a certain "size", which represents how many connections can be used simultaneously without resorting to creating more newly-established connections.
### Establishing a Transparent Connection Pool {@name=establishing}
Any DBAPI module can be "proxied" through the connection pool using the following technique (note that the usage of 'psycopg2' is **just an example**; substitute whatever DBAPI module you'd like):
{python}
import sqlalchemy.pool as pool
import psycopg2 as psycopg
psycopg = pool.manage(psycopg)
# then connect normally
connection = psycopg.connect(database='test', username='scott', password='tiger')
This produces a `sqlalchemy.pool.DBProxy` object which supports the same `connect()` function as the original DBAPI module. Upon connection, a connection proxy object is returned, which delegates its calls to a real DBAPI connection object. This connection object is stored persistently within a connection pool (an instance of `sqlalchemy.pool.Pool`) that corresponds to the exact connection arguments sent to the `connect()` function.
The connection proxy supports all of the methods on the original connection object, most of which are proxied via `__getattr__()`. The `close()` method will return the connection to the pool, and the `cursor()` method will return a proxied cursor object. Both the connection proxy and the cursor proxy will also return the underlying connection to the pool after they have both been garbage collected, which is detected via the `__del__()` method.
Additionally, when connections are returned to the pool, a `rollback()` is issued on the connection unconditionally. This is to release any locks still held by the connection that may have resulted from normal activity.
By default, the `connect()` method will return the same connection that is already checked out in the current thread. This allows a particular connection to be used in a given thread without needing to pass it around between functions. To disable this behavior, specify `use_threadlocal=False` to the `manage()` function.
### Connection Pool Configuration {@name=configuration}
For all types of Pool construction, which includes the "transparent proxy" described in the previous section, using an `Engine` via `create_engine()`, or constructing a pool through direct class instantiation, the options are generally the same. Additional options may be available based on the specific subclass of `Pool` being used.
For a description of all pool classes, see the [generated documentation](rel:docstrings_sqlalchemy.pool).
Common options include:
* echo=False : if set to True, connections being pulled and retrieved from/to the pool will
be logged to the standard output, as well as pool sizing information. Echoing can also
be achieved by enabling logging for the "sqlalchemy.pool" namespace. When using create_engine(),
this option is specified as `echo_pool`.
* use_threadlocal=False : if set to True, repeated calls to connect() within the same
application thread will be guaranteed to return the same connection object, if one has
already been retrieved from the pool and has not been returned yet. This allows code to
retrieve a connection from the pool, and then while still holding on to that connection,
to call other functions which also ask the pool for a connection of the same arguments;
those functions will act upon the same connection that the calling method is using.
This option is overridden during `create_engine()`, corresponding to the "plain" or
"threadlocal" connection strategy.
* recycle=-1 : if set to non -1, a number of seconds between connection recycling, which
means upon checkout, if this timeout is surpassed the connection will be closed and replaced
with a newly opened connection.
QueuePool options include:
* pool_size=5 : the size of the pool to be maintained. This is the
largest number of connections that will be kept persistently in the pool. Note that the
pool begins with no connections; once this number of connections is requested, that
number of connections will remain.
* max_overflow=10 : the maximum overflow size of the pool. When the number of checked-out
connections reaches the size set in pool_size, additional connections will be returned up
to this limit. When those additional connections are returned to the pool, they are
disconnected and discarded. It follows then that the total number of simultaneous
connections the pool will allow is pool_size + max_overflow, and the total number of
"sleeping" connections the pool will allow is pool_size. max_overflow can be set to -1 to
indicate no overflow limit; no limit will be placed on the total number of concurrent
connections.
* timeout=30 : the number of seconds to wait before giving up on returning a connection
### Custom Pool Construction {@name=custom}
Besides using the transparent proxy, instances of `sqlalchemy.pool.Pool` can be created directly. Constructing your own pool involves passing a callable used to create a connection. Through this method, custom connection schemes can be made, such as a connection that automatically executes some initialization commands to start.
{python title="Constructing a QueuePool"}
import sqlalchemy.pool as pool
import psycopg2
def getconn():
c = psycopg2.connect(username='ed', host='127.0.0.1', dbname='test')
# execute an initialization function on the connection before returning
c.cursor.execute("setup_encodings()")
return c
p = pool.QueuePool(getconn, max_overflow=10, pool_size=5, use_threadlocal=True)
Or with SingletonThreadPool:
{python title="Constructing a SingletonThreadPool"}
import sqlalchemy.pool as pool
import sqlite
def getconn():
return sqlite.connect(filename='myfile.db')
# SQLite connections require the SingletonThreadPool
p = pool.SingletonThreadPool(getconn)
-793
View File
@@ -1,793 +0,0 @@
Using the Session {@name=unitofwork}
============
The [Mapper](rel:advdatamapping) is the entrypoint to the configurational API of the SQLAlchemy object relational mapper. But the primary object one works with when using the ORM is the [Session](rel:docstrings_sqlalchemy.orm.session_Session).
## What does the Session do ?
In the most general sense, the `Session` establishes all conversations with the database and represents a "holding zone" for all the mapped instances which you've loaded or created during its lifespan. It implements the [Unit of Work](http://martinfowler.com/eaaCatalog/unitOfWork.html) pattern, which means it keeps track of all changes which occur, and is capable of **flushing** those changes to the database as appropriate. Another important facet of the `Session` is that it's also maintaining **unique** copies of each instance, where "unique" means "only one object with a particular primary key" - this pattern is called the [Identity Map](http://martinfowler.com/eaaCatalog/identityMap.html).
Beyond that, the `Session` implements an interface which let's you move objects in or out of the session in a variety of ways, it provides the entryway to a `Query` object which is used to query the database for data, it is commonly used to provide transactional boundaries (though this is optional), and it also can serve as a configurational "home base" for one or more `Engine` objects, which allows various vertical and horizontal partitioning strategies to be achieved.
## Getting a Session
The `Session` object exists just as a regular Python object, which can be directly instantiated. However, it takes a fair amount of keyword options, several of which you probably want to set explicitly. It's fairly inconvenient to deal with the "configuration" of a session every time you want to create one. Therefore, SQLAlchemy recommends the usage of a helper function called `sessionmaker()`, which typically you call only once for the lifespan of an application. This function creates a customized `Session` subclass for you, with your desired configurational arguments pre-loaded. Then, whenever you need a new `Session`, you use your custom `Session` class with no arguments to create the session.
### Using a sessionmaker() Configuration {@name=sessionmaker}
The usage of `sessionmaker()` is illustrated below:
{python}
from sqlalchemy.orm import sessionmaker
# create a configured "Session" class
Session = sessionmaker(autoflush=True, transactional=True)
# create a Session
sess = Session()
# work with sess
sess.save(x)
sess.commit()
# close when finished
sess.close()
Above, the `sessionmaker` call creates a class for us, which we assign to the name `Session`. This class is a subclass of the actual `sqlalchemy.orm.session.Session` class, which will instantiate with the arguments of `autoflush=True` and `transactional=True`.
When you write your application, place the call to `sessionmaker()` somewhere global, and then make your new `Session` class available to the rest of your application.
### Binding Session to an Engine or Connection {@name=binding}
In our previous example regarding `sessionmaker()`, nowhere did we specify how our session would connect to our database. When the session is configured in this manner, it will look for a database engine to connect with via the `Table` objects that it works with - the chapter called [metadata_tables_binding](rel:metadata_tables_binding) describes how to associate `Table` objects directly with a source of database connections.
However, it is often more straightforward to explicitly tell the session what database engine (or engines) you'd like it to communicate with. This is particularly handy with multiple-database scenarios where the session can be used as the central point of configuration. To acheive this, the constructor keyword `bind` is used for a basic single-database configuration:
{python}
# create engine
engine = create_engine('postgres://...')
# bind custom Session class to the engine
Session = sessionmaker(bind=engine, autoflush=True, transactional=True)
# work with the session
sess = Session()
One common issue with the above scenario is that an application will often organize its global imports before it ever connects to a database. Since the `Session` class created by `sessionmaker()` is meant to be a global application object (note we are saying the session *class*, not a session *instance*), we may not have a `bind` argument available. For this, the `Session` class returned by `sessionmaker()` supports post-configuration of all options, through its method `configure()`:
{python}
# configure Session class with desired options
Session = sessionmaker(autoflush=True, transactional=True)
# later, we create the engine
engine = create_engine('postgres://...')
# associate it with our custom Session class
Session.configure(bind=engine)
# work with the session
sess = Session()
The `Session` also has the ability to be bound to multiple engines. Descriptions of these scenarios are described in [unitofwork_partitioning](rel:unitofwork_partitioning).
#### Binding Session to a Connection {@name=connection}
The examples involving `bind` so far are dealing with the `Engine` object, which is, like the `Session` class itself, a global configurational object. The `Session` can also be bound to an individual database `Connection`. The reason you might want to do this is if your application controls the boundaries of transactions using distinct `Transaction` objects (these objects are described in [dbengine_transactions](rel:dbengine_transactions)). You'd have a transactional `Connection`, and then you'd want to work with an ORM-level `Session` which participates in that transaction. Since `Connection` is definitely not a globally-scoped object in all but the most rudimental commandline applications, you can bind an individual `Session()` instance to a particular `Connection` not at class configuration time, but at session instance construction time:
{python}
# global application scope. create Session class, engine
Session = sessionmaker(autoflush=True, transactional=True)
engine = create_engine('postgres://...')
...
# local scope, such as within a controller function
# connect to the database
connection = engine.connect()
# bind an individual Session to the connection
sess = Session(bind=connection)
### Using create_session() {@name=createsession}
As an alternative to `sessionmaker()`, `create_session()` exists literally as a function which calls the normal `Session` constructor directly. All arguments are passed through and the new `Session` object is returned:
{python}
session = create_session(bind=myengine)
The `create_session()` function doesn't add any functionality to the regular `Session`, it just sets up a default argument set of `autoflush=False, transactional=False`. But also, by calling `create_session()` instead of instantiating `Session` directly, you leave room in your application to change the type of session which the function creates. For example, an application which is calling `create_session()` in many places, which is typical for a pre-0.4 application, can be changed to use a `sessionmaker()` by just assigning the return of `sessionmaker()` to the `create_session` name:
{python}
# change from:
from sqlalchemy.orm import create_session
# to:
create_session = sessionmaker()
## Using the Session
A typical session conversation starts with creating a new session, or acquiring one from an ongoing context. You save new objects and load existing ones, make changes, mark some as deleted, and then persist your changes to the database. If your session is transactional, you use `commit()` to persist any remaining changes and to commit the transaction. If not, you call `flush()` which will flush any remaining data to the database.
Below, we open a new `Session` using a configured `sessionmaker()`, make some changes, and commit:
{python}
# configured Session class
Session = sessionmaker(autoflush=True, transactional=True)
sess = Session()
d = Data(value=10)
sess.save(d)
d2 = sess.query(Data).filter(Data.value=15).one()
d2.value = 19
sess.commit()
### Quickie Intro to Object States {@name=states}
It's helpful to know the states which an instance can have within a session:
* *Transient* - an instance that's not in a session, and is not saved to the database; i.e. it has no database identity. The only relationship such an object has to the ORM is that its class has a `mapper()` associated with it.
* *Pending* - when you `save()` a transient instance, it becomes pending. It still wasn't actually flushed to the database yet, but it will be when the next flush occurs.
* *Persistent* - An instance which is present in the session and has a record in the database. You get persistent instances by either flushing so that the pending instances become persistent, or by querying the database for existing instances (or moving persistent instances from other sessions into your local session).
* *Detached* - an instance which has a record in the database, but is not in any session. Theres nothing wrong with this, and you can use objects normally when they're detached, **except** they will not be able to issue any SQL in order to load collections or attributes which are not yet loaded, or were marked as "expired".
Knowing these states is important, since the `Session` tries to be strict about ambiguous operations (such as trying to save the same object to two different sessions at the same time).
### Frequently Asked Questions {@name=faq}
* When do I make a `sessionmaker` ?
Just one time, somewhere in your application's global scope. It should be looked upon as part of your application's configuration. If your application has three .py files in a package, you could, for example, place the `sessionmaker` line in your `__init__.py` file; from that point on your other modules say "from mypackage import Session". That way, everyone else just uses `Session()`, and the configuration of that session is controlled by that central point.
If your application starts up, does imports, but does not know what database it's going to be connecting to, you can bind the `Session` at the "class" level to the engine later on, using `configure()`.
In the examples in this section, we will frequently show the `sessionmaker` being created right above the line where we actually invoke `Session()`. But that's just for example's sake ! In reality, the `sessionmaker` would be somewhere at the module level, and your individual `Session()` calls would be sprinkled all throughout your app, such as in a web application within each controller method.
* When do I make a `Session` ?
You typically invoke `Session()` when you first need to talk to your database, and want to save some objects or load some existing ones. Then, you work with it, save your changes, and then dispose of it....or at the very least `close()` it. It's not a "global" kind of object, and should be handled more like a "local variable", as it's generally **not** safe to use with concurrent threads. Sessions are very inexpensive to make, and don't use any resources whatsoever until they are first used...so create some !
There is also a pattern whereby you're using a **contextual session**, this is described later in [unitofwork_contextual](rel:unitofwork_contextual). In this pattern, a helper object is maintaining a `Session` for you, most commonly one that is local to the current thread (and sometimes also local to an application instance). SQLAlchemy 0.4 has worked this pattern out such that it still *looks* like you're creating a new session as you need one...so in that case, it's still a guaranteed win to just say `Session()` whenever you want a session.
* Is the Session a cache ?
Yeee...no. It's somewhat used as a cache, in that it implements the identity map pattern, and stores objects keyed to their primary key. However, it doesn't do any kind of query caching. This means, if you say `session.query(Foo).filter_by(name='bar')`, even if `Foo(name='bar')` is right there, in the identity map, the session has no idea about that. It has to issue SQL to the database, get the rows back, and then when it sees the primary key in the row, *then* it can look in the local identity map and see that the object is already there. It's only when you say `query.get({some primary key})` that the `Session` doesn't have to issue a query.
Additionally, the Session stores object instances using a weak reference by default. This also defeats the purpose of using the Session as a cache, unless the `weak_identity_map` flag is set to `False`.
The `Session` is not designed to be a global object from which everyone consults as a "registry" of objects. That is the job of a **second level cache**. A good library for implementing second level caching is [Memcached](http://www.danga.com/memcached/). It *is* possible to "sort of" use the `Session` in this manner, if you set it to be non-transactional and it never flushes any SQL, but it's not a terrific solution, since if concurrent threads load the same objects at the same time, you may have multiple copies of the same objects present in collections.
* How can I get the `Session` for a certain object ?
Use the `object_session()` classmethod available on `Session`:
{python}
session = Session.object_session(someobject)
* Is the session threadsafe ?
Nope. It has no thread synchronization of any kind built in, and particularly when you do a flush operation, it definitely is not open to concurrent threads accessing it, because it holds onto a single database connection at that point. If you use a session which is non-transactional for read operations only, it's still not thread-"safe", but you also wont get any catastrophic failures either, since it opens and closes connections on an as-needed basis; its just that different threads might load the same objects independently of each other, but only one will wind up in the identity map (however, the other one might still live in a collection somewhere).
But the bigger point here is, you should not *want* to use the session with multiple concurrent threads. That would be like having everyone at a restaurant all eat from the same plate. The session is a local "workspace" that you use for a specific set of tasks; you don't want to, or need to, share that session with other threads who are doing some other task. If, on the other hand, there are other threads participating in the same task you are, such as in a desktop graphical application, then you would be sharing the session with those threads, but you also will have implemented a proper locking scheme (or your graphical framework does) so that those threads do not collide.
### Session Attributes {@name=attributes}
The session provides a set of attributes and collection-oriented methods which allow you to view the current state of the session.
The **identity map** is accessed by the `identity_map` attribute, which provides a dictionary interface. The keys are "identity keys", which are attached to all persistent objects by the attribute `_instance_key`:
{python}
>>> myobject._instance_key
(<class 'test.tables.User'>, (7,))
>>> myobject._instance_key in session.identity_map
True
>>> session.identity_map.values()
[<__main__.User object at 0x712630>, <__main__.Address object at 0x712a70>]
The identity map is a weak-referencing dictionary by default. This means that objects which are dereferenced on the outside will be removed from the session automatically. Note that objects which are marked as "dirty" will not fall out of scope until after changes on them have been flushed; special logic kicks in at the point of auto-removal which ensures that no pending changes remain on the object, else a temporary strong reference is created to the object.
Some people prefer objects to stay in the session until explicitly removed in all cases; for this, you can specify the flag `weak_identity_map=False` to the `create_session` or `sessionmaker` functions so that the `Session` will use a regular dictionary.
While the `identity_map` accessor is currently the actual dictionary used by the `Session` to store instances, you should not add or remove items from this dictionary. Use the session methods `save_or_update()` and `expunge()` to add or remove items.
The Session also supports an iterator interface in order to see all objects in the identity map:
{python}
for obj in session:
print obj
As well as `__contains__()`:
{python}
if obj in session:
print "Object is present"
The session is also keeping track of all newly created (i.e. pending) objects, all objects which have had changes since they were last loaded or saved (i.e. "dirty"), and everything that's been marked as deleted.
{python}
# pending objects recently added to the Session
session.new
# persistent objects which currently have changes detected
# (this collection is now created on the fly each time the property is called)
session.dirty
# persistent objects that have been marked as deleted via session.delete(obj)
session.deleted
### Querying
The `query()` function takes one or more classes and/or mappers, along with an optional `entity_name` parameter, and returns a new `Query` object which will issue mapper queries within the context of this Session. For each mapper is passed, the Query uses that mapper. For each class, the Query will locate the primary mapper for the class using `class_mapper()`.
{python}
# query from a class
session.query(User).filter_by(name='ed').all()
# query with multiple classes, returns tuples
session.query(User).add_entity(Address).join('addresses').filter_by(name='ed').all()
# query from a mapper
query = session.query(usermapper)
x = query.get(1)
# query from a class mapped with entity name 'alt_users'
q = session.query(User, entity_name='alt_users')
y = q.options(eagerload('orders')).all()
`entity_name` is an optional keyword argument sent with a class object, in order to further qualify which primary mapper to be used; this only applies if there was a `Mapper` created with that particular class/entity name combination, else an exception is raised. All of the methods on Session which take a class or mapper argument also take the `entity_name` argument, so that a given class can be properly matched to the desired primary mapper.
All instances retrieved by the returned `Query` object will be stored as persistent instances within the originating `Session`.
### Saving New Instances
`save()` is called with a single transient instance as an argument, which is then added to the Session and becomes pending. When the session is next flushed, the instance will be saved to the database. If the given instance is not transient, meaning it is either attached to an existing Session or it has a database identity, an exception is raised.
{python}
user1 = User(name='user1')
user2 = User(name='user2')
session.save(user1)
session.save(user2)
session.commit() # write changes to the database
There's also other ways to have objects saved to the session automatically; one is by using cascade rules, and the other is by using a contextual session. Both of these are described later.
### Updating/Merging Existing Instances
The `update()` method is used when you have a detached instance, and you want to put it back into a `Session`. Recall that "detached" means the object has a database identity.
Since `update()` is a little picky that way, most people use `save_or_update()`, which checks for an `_instance_key` attribute, and based on whether it's there or not, calls either `save()` or `update()`:
{python}
# load user1 using session 1
user1 = sess1.query(User).get(5)
# remove it from session 1
sess1.expunge(user1)
# move it into session 2
sess2.save_or_update(user1)
`update()` is also an operation that can happen automatically using cascade rules, just like `save()`.
`merge()` on the other hand is a little like `update()`, except it creates a **copy** of the given instance in the session, and returns to you that instance; the instance you send it never goes into the session. `merge()` is much fancier than `update()`; it will actually look to see if an object with the same primary key is already present in the session, and if not will load it by primary key. Then, it will merge the attributes of the given object into the one which it just located.
This method is useful for bringing in objects which may have been restored from a serialization, such as those stored in an HTTP session, where the object may be present in the session already:
{python}
# deserialize an object
myobj = pickle.loads(mystring)
# "merge" it. if the session already had this object in the
# identity map, then you get back the one from the current session.
myobj = session.merge(myobj)
`merge()` includes an important option called `dont_load`. When this boolean flag is set to `True`, the merge of a detached object will not force a `get()` of that object from the database. Normally, `merge()` issues a `get()` for every existing object so that it can load the most recent state of the object, which is then modified according to the state of the given object. With `dont_load=True`, the `get()` is skipped and `merge()` places an exact copy of the given object in the session. This allows objects which were retrieved from a caching system to be copied back into a session without any SQL overhead being added.
### Deleting
The `delete` method places an instance into the Session's list of objects to be marked as deleted:
{python}
# mark two objects to be deleted
session.delete(obj1)
session.delete(obj2)
# commit (or flush)
session.commit()
The big gotcha with `delete()` is that **nothing is removed from collections**. Such as, if a `User` has a collection of three `Addresses`, deleting an `Address` will not remove it from `user.addresses`:
{python}
>>> address = user.addresses[1]
>>> session.delete(address)
>>> session.flush()
>>> address in user.addresses
True
The solution is to use proper cascading:
{python}
mapper(User, users_table, properties={
'addresses':relation(Address, cascade="all, delete")
})
del user.addresses[1]
session.flush()
### Flushing
This is the main gateway to what the `Session` does best, which is save everything ! It should be clear by now what a flush looks like:
{python}
session.flush()
It also can be called with a list of objects; in this form, the flush operation will be limited only to the objects specified in the list:
{python}
# saves only user1 and address2. all other modified
# objects remain present in the session.
session.flush([user1, address2])
This second form of flush should be used carefully as it will not necessarily locate other dependent objects within the session, whose database representation may have foreign constraint relationships with the objects being operated upon.
Theres also a way to have `flush()` called automatically before each query; this is called "autoflush" and is described below.
Note that flush **does not change** the state of any collections or entity relationships in memory; for example, if you set a foreign key attribute `b_id` on object `A` with the the identifier `B.id`, the change will be flushed to the database, but `A` will not have `B` added to its collection. If you want to manipulate foreign key attributes directly, `refresh()` or `expire()` the objects whose state needs to be refreshed subsequent to flushing.
### Autoflush
A session can be configured to issue `flush()` calls before each query. This allows you to immediately have DB access to whatever has been saved to the session. It's recommended to use autoflush with `transactional=True`, that way an unexpected flush call won't permanently save to the database:
{python}
Session = sessionmaker(autoflush=True, transactional=True)
sess = Session()
u1 = User(name='jack')
sess.save(u1)
# reload user1
u2 = sess.query(User).filter_by(name='jack').one()
assert u2 is u1
# commit session, flushes whatever is remaining
sess.commit()
Autoflush is particularly handy when using "dynamic" mapper relations, so that changes to the underlying collection are immediately available via its query interface.
### Expunge / Clear
Expunge removes an object from the Session, sending persistent instances to the detached state, and pending instances to the transient state:
{python}
session.expunge(obj1)
Use `expunge` when youd like to remove an object altogether from memory, such as before calling `del` on it, which will prevent any "ghost" operations occuring when the session is flushed.
This `clear()` method is equivalent to `expunge()`-ing everything from the Session:
{python}
session.clear()
However note that the `clear()` method does not reset any transactional state or connection resources; therefore what you usually want to call instead of `clear()` is `close()`.
### Closing
The `close()` method issues a `clear()`, and releases any transactional/connection resources. When connections are returned to the connection pool, whatever transactional state exists is rolled back.
When `close()` is called, the `Session` is in the same state as when it was first created, and is safe to be used again. `close()` is especially important when using a contextual session, which remains in memory after usage. By issuing `close()`, the session will be clean for the next request that makes use of it.
### Refreshing / Expiring
To assist with the Session's "sticky" behavior of instances which are present, individual objects can have all of their attributes immediately re-loaded from the database, or marked as "expired" which will cause a re-load to occur upon the next access of any of the object's mapped attributes. This includes all relationships, so lazy-loaders will be re-initialized, eager relationships will be repopulated. Any changes marked on the object are discarded:
{python}
# immediately re-load attributes on obj1, obj2
session.refresh(obj1)
session.refresh(obj2)
# expire objects obj1, obj2, attributes will be reloaded
# on the next access:
session.expire(obj1)
session.expire(obj2)
`refresh()` and `expire()` also support being passed a list of individual attribute names in which to be refreshed. These names can reference any attribute, column-based or relation based:
{python}
# immediately re-load the attributes 'hello', 'world' on obj1, obj2
session.refresh(obj1, ['hello', 'world'])
session.refresh(obj2, ['hello', 'world'])
# expire the attriibutes 'hello', 'world' objects obj1, obj2, attributes will be reloaded
# on the next access:
session.expire(obj1, ['hello', 'world'])
session.expire(obj2, ['hello', 'world'])
## Cascades
Mappers support the concept of configurable *cascade* behavior on `relation()`s. This behavior controls how the Session should treat the instances that have a parent-child relationship with another instance that is operated upon by the Session. Cascade is indicated as a comma-separated list of string keywords, with the possible values `all`, `delete`, `save-update`, `refresh-expire`, `merge`, `expunge`, and `delete-orphan`.
Cascading is configured by setting the `cascade` keyword argument on a `relation()`:
{python}
mapper(Order, order_table, properties={
'items' : relation(Item, items_table, cascade="all, delete-orphan"),
'customer' : relation(User, users_table, user_orders_table, cascade="save-update"),
})
The above mapper specifies two relations, `items` and `customer`. The `items` relationship specifies "all, delete-orphan" as its `cascade` value, indicating that all `save`, `update`, `merge`, `expunge`, `refresh` `delete` and `expire` operations performed on a parent `Order` instance should also be performed on the child `Item` instances attached to it (`save` and `update` are cascaded using the `save_or_update()` method, so that the database identity of the instance doesn't matter). The `delete-orphan` cascade value additionally indicates that if an `Item` instance is no longer associated with an `Order`, it should also be deleted. The "all, delete-orphan" cascade argument allows a so-called *lifecycle* relationship between an `Order` and an `Item` object.
The `customer` relationship specifies only the "save-update" cascade value, indicating most operations will not be cascaded from a parent `Order` instance to a child `User` instance, except for if the `Order` is attached with a particular session, either via the `save()`, `update()`, or `save-update()` method.
Additionally, when a child item is attached to a parent item that specifies the "save-update" cascade value on the relationship, the child is automatically passed to `save_or_update()` (and the operation is further cascaded to the child item).
Note that cascading doesn't do anything that isn't possible by manually calling Session methods on individual instances within a hierarchy, it merely automates common operations on a group of associated instances.
The default value for `cascade` on `relation()`s is `save-update, merge`.
## Managing Transactions
The Session can manage transactions automatically, including across multiple engines. When the Session is in a transaction, as it receives requests to execute SQL statements, it adds each indivdual Connection/Engine encountered to its transactional state. At commit time, all unflushed data is flushed, and each individual transaction is committed. If the underlying databases support two-phase semantics, this may be used by the Session as well if two-phase transactions are enabled.
The easiest way to use a Session with transactions is just to declare it as transactional. The session will remain in a transaction at all times:
{python}
# transactional session
Session = sessionmaker(transactional=True)
sess = Session()
item1 = sess.query(Item).get(1)
item2 = sess.query(Item).get(2)
item1.foo = 'bar'
item2.bar = 'foo'
# commit- will immediately go into a new transaction afterwards
sess.commit()
Alternatively, a transaction can be begun explicitly using `begin()`:
{python}
# non transactional session
Session = sessionmaker(transactional=False)
sess = Session()
sess.begin()
try:
item1 = sess.query(Item).get(1)
item2 = sess.query(Item).get(2)
item1.foo = 'bar'
item2.bar = 'foo'
except:
sess.rollback()
raise
sess.commit()
Session also supports Python 2.5's with statement so that the example above can be written as:
{python}
Session = sessionmaker(transactional=False)
sess = Session()
with sess.begin():
item1 = sess.query(Item).get(1)
item2 = sess.query(Item).get(2)
item1.foo = 'bar'
item2.bar = 'foo'
For MySQL and Postgres (and soon Oracle), "nested" transactions can be accomplished which use SAVEPOINT behavior, via the `begin_nested()` method:
{python}
Session = sessionmaker(transactional=False)
sess = Session()
sess.begin()
sess.save(u1)
sess.save(u2)
sess.flush()
sess.begin_nested() # establish a savepoint
sess.save(u3)
sess.rollback() # rolls back u3, keeps u1 and u2
sess.commit() # commits u1 and u2
Finally, for MySQL, Postgres, and soon Oracle as well, the session can be instructed to use two-phase commit semantics using the flag `twophase=True`, which coordinates transactions across multiple databases:
{python}
engine1 = create_engine('postgres://db1')
engine2 = create_engine('postgres://db2')
Session = sessionmaker(twophase=True, transactional=True)
# bind User operations to engine 1, Account operations to engine 2
Session.configure(binds={User:engine1, Account:engine2})
sess = Session()
# .... work with accounts and users
# commit. session will issue a flush to all DBs, and a prepare step to all DBs,
# before committing both transactions
sess.commit()
## Embedding SQL Insert/Update Expressions into a Flush {@name=flushsql}
This feature allows the value of a database column to be set to a SQL expression instead of a literal value. It's especially useful for atomic updates, calling stored procedures, etc. All you do is assign an expression to an attribute:
{python}
class SomeClass(object):
pass
mapper(SomeClass, some_table)
someobject = session.query(SomeClass).get(5)
# set 'value' attribute to a SQL expression adding one
someobject.value = some_table.c.value + 1
# issues "UPDATE some_table SET value=value+1"
session.commit()
This works both for INSERT and UPDATE statements. After the flush/commit operation, the `value` attribute on `someobject` gets "deferred", so that when you again access it the newly generated value will be loaded from the database. This is the same mechanism at work when database-side column defaults fire off.
## Using SQL Expressions with Sessions {@name=sql}
SQL constructs and string statements can be executed via the `Session`. You'd want to do this normally when your `Session` is transactional and youd like your free-standing SQL statements to participate in the same transaction.
The two ways to do this are to use the connection/execution services of the Session, or to have your Session participate in a regular SQL transaction.
First, a Session thats associated with an Engine or Connection can execute statements immediately (whether or not its transactional):
{python}
Session = sessionmaker(bind=engine, transactional=True)
sess = Session()
result = sess.execute("select * from table where id=:id", {'id':7})
result2 = sess.execute(select([mytable], mytable.c.id==7))
To get at the current connection used by the session, which will be part of the current transaction if one is in progress, use `connection()`:
{python}
connection = sess.connection()
A second scenario is that of a Session which is not directly bound to a connectable. This session executes statements relative to a particular `Mapper`, since the mappers are bound to tables which are in turn bound to connectables via their `MetaData` (either the session or the mapped tables need to be bound). In this case, the Session can conceivably be associated with multiple databases through different mappers; so it wants you to send along a `mapper` argument, which can be any mapped class or mapper instance:
{python}
# session is *not* bound to an engine or connection
Session = sessionmaker(transactional=True)
sess = Session()
# need to specify mapper or class when executing
result = sess.execute("select * from table where id=:id", {'id':7}, mapper=MyMappedClass)
result2 = sess.execute(select([mytable], mytable.c.id==7), mapper=MyMappedClass)
# need to specify mapper or class when you call connection()
connection = sess.connection(MyMappedClass)
The third scenario is when you are using `Connection` and `Transaction` yourself, and want the `Session` to participate. This is easy, as you just bind the `Session` to the connection:
{python}
# non-transactional session
Session = sessionmaker(transactional=False)
# non-ORM connection + transaction
conn = engine.connect()
trans = conn.begin()
# bind the Session *instance* to the connection
sess = Session(bind=conn)
# ... etc
trans.commit()
It's safe to use a `Session` which is transactional or autoflushing, as well as to call `begin()`/`commit()` on the session too; the outermost Transaction object, the one we declared explicitly, controls the scope of the transaction.
When using the `threadlocal` engine context, things are that much easier; the `Session` uses the same connection/transaction as everyone else in the current thread, whether or not you explicitly bind it:
{python}
engine = create_engine('postgres://mydb', strategy="threadlocal")
engine.begin()
sess = Session() # session takes place in the transaction like everyone else
# ... go nuts
engine.commit() # commit the transaction
## Contextual/Thread-local Sessions {@name=contextual}
A common need in applications, particularly those built around web frameworks, is the ability to "share" a `Session` object among disparate parts of an application, without needing to pass the object explicitly to all method and function calls. What you're really looking for is some kind of "global" session object, or at least "global" to all the parts of an application which are tasked with servicing the current request. For this pattern, SQLAlchemy provides the ability to enhance the `Session` class generated by `sessionmaker()` to provide auto-contextualizing support. This means that whenever you create a `Session` instance with its constructor, you get an *existing* `Session` object which is bound to some "context". By default, this context is the current thread. This feature is what previously was accomplished using the `sessioncontext` SQLAlchemy extension.
### Creating a Thread-local Context {@name=creating}
The `scoped_session()` function wraps around the `sessionmaker()` function, and produces an object which behaves the same as the `Session` subclass returned by `sessionmaker()`:
{python}
from sqlalchemy.orm import scoped_session, sessionmaker
Session = scoped_session(sessionmaker(autoflush=True, transactional=True))
However, when you instantiate this `Session` "class", in reality the object is pulled from a threadlocal variable, or if it doesn't exist yet, it's created using the underlying class generated by `sessionmaker()`:
{python}
>>> # call Session() the first time. the new Session instance is created.
>>> sess = Session()
>>> # later, in the same application thread, someone else calls Session()
>>> sess2 = Session()
>>> # the two Session objects are *the same* object
>>> sess is sess2
True
Since the `Session()` constructor now returns the same `Session` object every time within the current thread, the object returned by `scoped_session()` also implements most of the `Session` methods and properties at the "class" level, such that you don't even need to instantiate `Session()`:
{python}
# create some objects
u1 = User()
u2 = User()
# save to the contextual session, without instantiating
Session.save(u1)
Session.save(u2)
# view the "new" attribute
assert u1 in Session.new
# flush changes (if not using autoflush)
Session.flush()
# commit transaction (if using a transactional session)
Session.commit()
To "dispose" of the `Session`, theres two general approaches. One is to close out the current session, but to leave it assigned to the current context. This allows the same object to be re-used on another operation. This may be called from a current, instantiated `Session`:
{python}
sess.close()
Or, when using `scoped_session()`, the `close()` method may also be called as a classmethod on the `Session` "class":
{python}
Session.close()
When the `Session` is closed, it remains attached, but clears all of its contents and releases any ongoing transactional resources, including rolling back any remaining transactional state. The `Session` can then be used again.
The other method is to remove the current session from the current context altogether. This is accomplished using the classmethod `remove()`:
{python}
Session.remove()
After `remove()` is called, the next call to `Session()` will create a *new* `Session` object which then becomes the contextual session.
That, in a nutshell, is all there really is to it. Now for all the extra things one should know.
### Lifespan of a Contextual Session {@name=lifespan}
A (really, really) common question is when does the contextual session get created, when does it get disposed ? We'll consider a typical lifespan as used in a web application:
{diagram}
Web Server Web Framework User-defined Controller Call
-------------- -------------- ------------------------------
web request ->
call controller -> # call Session(). this establishes a new,
# contextual Session.
sess = Session()
# load some objects, save some changes
objects = sess.query(MyClass).all()
# some other code calls Session, its the
# same contextual session as "sess"
sess2 = Session()
sess2.save(foo)
sess2.commit()
# generate content to be returned
return generate_content()
Session.remove() <-
web response <-
Above, we illustrate a *typical* organization of duties, where the "Web Framework" layer has some integration built-in to manage the span of ORM sessions. Upon the initial handling of an incoming web request, the framework passes control to a controller. The controller then calls `Session()` when it wishes to work with the ORM; this method establishes the contextual Session which will remain until it's removed. Disparate parts of the controller code may all call `Session()` and will get the same session object. Then, when the controller has completed and the reponse is to be sent to the web server, the framework **closes out** the current contextual session, above using the `remove()` method which removes the session from the context altogether.
As an alternative, the "finalization" step can also call `Session.close()`, which will leave the same session object in place. Which one is better ? For a web framework which runs from a fixed pool of threads, it doesn't matter much. For a framework which runs a **variable** number of threads, or which **creates and disposes** of a thread for each request, `remove()` is better, since it leaves no resources associated with the thread which might not exist.
* Why close out the session at all ? Why not just leave it going so the next request doesn't have to do as many queries ?
There are some cases where you may actually want to do this. However, this is a special case where you are dealing with data which **does not change** very often, or you don't care about the "freshness" of the data. In reality, a single thread of a web server may, on a slow day, sit around for many minutes or even hours without being accessed. When it's next accessed, if data from the previous request still exists in the session, that data may be very stale indeed. So its generally better to have an empty session at the start of a web request.
### Associating Classes and Mappers with a Contextual Session {@name=associating}
Another luxury we gain, when we've established a `Session()` that can be globally accessed, is the ability for mapped classes and objects to provide us with session-oriented functionality automatically. When using the `scoped_session()` function, we access this feature using the `mapper` attribute on the object in place of the normal `sqlalchemy.orm.mapper` function:
{python}
# "contextual" mapper function
mapper = Session.mapper
# use normally
mapper(User, users_table, properties={
relation(Address)
})
mapper(Address, addresses_table)
When we use the contextual `mapper()` function, our `User` and `Address` now gain a new attribute `query`, which will create a `Query` object for us against the contextual session:
{python}
wendy = User.query.filter_by(name='wendy').one()
#### Auto-Save Behavior with Contextual Session's Mapper {@name=autosave}
By default, when using Session.mapper, **new instances are saved into the contextual session automatically upon construction;** there is no longer a need to call `save()`:
{python}
>>> newuser = User(name='ed')
>>> assert newuser in Session.new
True
The auto-save functionality can cause problems, namely that any `flush()` which occurs before a newly constructed object is fully populated will result in that object being INSERTed without all of its attributes completed. As a `flush()` is more frequent when using sessions with `autoflush=True`, **the auto-save behavior can be disabled**, using the `save_on_init=False` flag:
{python}
# "contextual" mapper function
mapper = Session.mapper
# use normally, specify no save on init:
mapper(User, users_table, properties={
relation(Address)
}, save_on_init=False)
mapper(Address, addresses_table, save_on_init=False)
# objects now again require expicit "save"
>>> newuser = User(name='ed')
>>> assert newuser in Session.new
False
>>> Session.save(newuser)
>>> assert newuser in Session.new
True
The functionality of `Session.mapper` is an updated version of what used to be accomplished by the `assignmapper()` SQLAlchemy extension.
[Generated docstrings for scoped_session()](rel:docstrings_sqlalchemy.orm_modfunc_scoped_session)
## Partitioning Strategies
this section is TODO
### Vertical Partitioning
Vertical partitioning places different kinds of objects, or different tables, across multiple databases.
{python}
engine1 = create_engine('postgres://db1')
engine2 = create_engine('postgres://db2')
Session = sessionmaker(twophase=True, transactional=True)
# bind User operations to engine 1, Account operations to engine 2
Session.configure(binds={User:engine1, Account:engine2})
sess = Session()
### Horizontal Partitioning
Horizontal partitioning partitions the rows of a single table (or a set of tables) across multiple databases.
See the "sharding" example in [attribute_shard.py](http://www.sqlalchemy.org/trac/browser/sqlalchemy/trunk/examples/sharding/attribute_shard.py)
## Extending Session
Extending the session can be achieved through subclassing as well as through a simple extension class, which resembles the style of [advdatamapping_mapper_extending](rel:advdatamapping_mapper_extending) called [SessionExtension](rel:docstrings_sqlalchemy.orm.session_SessionExtension). See the docstrings for more information on this class' methods.
Basic usage is similar to `MapperExtension`:
{python}
class MySessionExtension(SessionExtension):
def before_commit(self, session):
print "before commit!"
Session = sessionmaker(extension=MySessionExtension())
or with `create_session()`:
{python}
sess = create_session(extension=MySessionExtension())
The same `SessionExtension` instance can be used with any number of sessions.
-523
View File
@@ -1,523 +0,0 @@
Tutorial
========
This tutorial provides a relatively simple walking tour through the basic concepts of SQLAlchemy. You may wish to skip it and dive into the [main manual][manual] which is more reference-oriented. The examples in this tutorial comprise a fully working interactive Python session, and are guaranteed to be functioning courtesy of [doctest][].
[doctest]: http://www.python.org/doc/lib/module-doctest.html
[manual]: rel:metadata
Installation
------------
### Installing SQLAlchemy {@name=sqlalchemy}
Installing SQLAlchemy from scratch is most easily achieved with [setuptools][]. ([setuptools installation][install setuptools]). Just run this from the command-line:
# easy_install SQLAlchemy
This command will download the latest version of SQLAlchemy from the [Python Cheese Shop][cheese] and install it to your system.
[setuptools]: http://peak.telecommunity.com/DevCenter/setuptools
[install setuptools]: http://peak.telecommunity.com/DevCenter/EasyInstall#installation-instructions
[cheese]: http://cheeseshop.python.org/pypi/SQLAlchemy
Otherwise, you can install from the distribution using the `setup.py` script:
# python setup.py install
### Installing a Database API {@name=dbms}
SQLAlchemy is designed to operate with a [DBAPI](http://www.python.org/doc/peps/pep-0249/) implementation built for a particular database, and includes support for the most popular databases. If you have one of the [supported DBAPI implementations](rel:dbengine_supported), you can proceed to the following section. Otherwise [SQLite][] is an easy-to-use database to get started with, which works with plain files or in-memory databases.
SQLite is included with Python 2.5 and greater.
If you are working with Python 2.3 or 2.4, SQLite and the Python API for SQLite can be installed from the following packages:
* [pysqlite][] - Python interface for SQLite
* [SQLite library](http://sqlite.org)
Note that the SQLite library download is not required with Windows, as the Windows Pysqlite library already includes it linked in. Pysqlite and SQLite can also be installed on Linux or FreeBSD via pre-made [packages][pysqlite packages] or [from sources][pysqlite].
[sqlite]: http://sqlite.org/
[pysqlite]: http://pysqlite.org/
[pysqlite packages]: http://initd.org/tracker/pysqlite/wiki/PysqlitePackages
Getting Started {@name=gettingstarted}
--------------------------
### Checking the Version
**Note: This tutorial is oriented towards version 0.4 of SQLAlchemy. ** Check the version of SQLAlchemy you have installed via:
{python}
>>> import sqlalchemy
>>> sqlalchemy.__version__ # doctest: +SKIP
0.4.0
### Imports
To start connecting to databases and begin issuing queries, we want to import the base of SQLAlchemy's functionality, which is provided under the module name of `sqlalchemy`. For the purposes of this tutorial, we will import its full list of symbols into our own local namespace.
{python}
>>> from sqlalchemy import *
Note that importing using the `*` operator pulls all the names from `sqlalchemy` into the local module namespace, which in a real application can produce name conflicts. Therefore its recommended in practice to either import the individual symbols desired (i.e. `from sqlalchemy import Table, Column`) or to import under a distinct namespace (i.e. `import sqlalchemy as sa`).
### Connecting to the Database
After our imports, the next thing we need is a handle to the desired database, represented by an `Engine` object. This object handles the business of managing connections and dealing with the specifics of a particular database. Below, we will make a SQLite connection to a file-based database called "tutorial.db".
{python}
>>> db = create_engine('sqlite:///tutorial.db')
Technically, the above statement did not make an actual connection to the sqlite database just yet. As soon as we begine working with the engine, it will start creating connections. In the case of SQLite, the `tutorial.db` file will actually be created at the moment it is first used, if the file does not exist already.
For full information on creating database engines, including those for SQLite and others, see [dbengine](rel:dbengine).
SQLAlchemy is Two Libraries in One {@name=twoinone}
----------------------------------------------------
Now that the basics of installing SQLAlchemy and connecting to our database are established, we can start getting in to actually doing something. But first, a little bit of explanation is required.
A central concept of SQLAlchemy is that it actually contains two distinct areas of functionality, one of which builds upon the other. One is a **SQL Construction Language** and the other is an **Object Relational Mapper** ("ORM" for short). The SQL construction language allows you to construct objects called `ClauseElements` which represent SQL expressions. These ClauseElements can then be executed against any database, where they are **compiled** into strings that are appropriate for the target database, and return an object called a `ResultProxy`, which is essentially a result set object that acts very much like a deluxe version of the dbapi `cursor` object.
The Object Relational Mapper (ORM) is a set of tools completely distinct from the SQL Construction Language which serve the purpose of mapping Python object instances into database rows, providing a rich selection interface with which to retrieve instances from tables as well as a comprehensive solution to persisting changes on those instances back into the database. When working with the ORM, its underlying workings as well as its public API make extensive use of the SQL Construction Language, however the general theory of operation is slightly different. Instead of working with database rows directly, you work with your own user-defined classes and object instances. Additionally, the method of issuing queries to the database is different, as the ORM handles the job of generating most of the SQL required, and instead requires more information about what kind of class instances you'd like to load and where you'd like to put them.
Where SA is somewhat unique, more powerful, and slightly more complicated is that the two areas of functionality can be mixed together in many ways. A key strategy to working with SA effectively is to have a solid awareness of these two distinct toolsets, and which concepts of SA belong to each - even some publications have confused the SQL Construction Language with the ORM. The key difference between the two is that when you're working with cursor-like result sets its the SQL Construction Language, and when working with collections of your own class instances its the Object Relational Mapper.
This tutorial will first focus on the basic configuration that is common to using both the SQL Construction Language as well as the ORM, which is to declare information about your database called **table metadata**. This will be followed by some constructed SQL examples, and then into usage of the ORM utilizing the same data we established in the SQL construction examples.
Working with Database Objects {@name=schemasql}
-----------------------------------------------
### Defining Metadata, Binding to Engines {@name=metadata}
Configuring SQLAlchemy for your database consists of creating objects called `Tables`, each of which represent an actual table in the database. A collection of `Table` objects resides in a `MetaData` object which is essentially a table collection. We will create a `MetaData` and connect it to our `Engine` (connecting a schema object to an Engine is called *binding*):
{python}
>>> metadata = MetaData()
>>> metadata.bind = db
An equivalent operation is to create the `MetaData` object directly with the Engine:
{python}
>>> metadata = MetaData(db)
Now, when we tell "metadata" about the tables in our database, we can issue CREATE statements for those tables, as well as execute SQL statements derived from them, without needing to open or close any connections; that will be all done automatically.
Note that SQLALchemy allows us to use explicit connection objects for everything, if we wanted to, and there are reasons why you might want to do this. But for the purposes of this tutorial, using `bind` removes the need for us to deal with explicit connections.
### Creating a Table {@name=table_creating}
With `metadata` as our established home for tables, lets make a Table for it:
{python}
>>> users_table = Table('users', metadata,
... Column('user_id', Integer, primary_key=True),
... Column('user_name', String(40)),
... Column('password', String(15))
... )
As you might have guessed, we have just defined a table named `users` which has three columns: `user_id` (which is a primary key column), `user_name` and `password`. Currently it is just an object that doesn't necessarily correspond to an existing table in our database. To actually create the table, we use the `create()` method. To make it interesting, we will have SQLAlchemy echo the SQL statements it sends to the database, by setting the `echo` flag on the `Engine` associated with our `MetaData`:
{python}
>>> metadata.bind.echo = True
>>> users_table.create() # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE
CREATE TABLE users (
user_id INTEGER NOT NULL,
user_name VARCHAR(40),
password VARCHAR(15),
PRIMARY KEY (user_id)
)
...
Alternatively, the `users` table might already exist (such as, if you're running examples from this tutorial for the second time), in which case you can just skip the `create()` method call. You can even skip defining the individual columns in the `users` table and ask SQLAlchemy to load its definition from the database:
{python}
>>> users_table = Table('users', metadata, autoload=True)
>>> list(users_table.columns)[0].name
'user_id'
Loading a table's columns from the database is called **reflection**. Documentation on table metadata, including reflection, is available in [metadata](rel:metadata).
### Inserting Rows
Inserting is achieved via the `insert()` method, which defines a *clause object* (known as a `ClauseElement`) representing an INSERT statement:
{python}
>>> i = users_table.insert()
>>> i # doctest:+ELLIPSIS
<sqlalchemy.sql.Insert object at 0x...>
>>> # the string form of the Insert object is a generic SQL representation
>>> print i
INSERT INTO users (user_id, user_name, password) VALUES (?, ?, ?)
Since we created this insert statement object from the `users` table which is bound to our `Engine`, the statement itself is also bound to the `Engine`, and supports executing itself. The `execute()` method of the clause object will *compile* the object into a string according to the underlying *dialect* of the Engine to which the statement is bound, and will then execute the resulting statement.
{python}
>>> # insert a single row
>>> i.execute(user_name='Mary', password='secure') # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE
INSERT INTO users (user_name, password) VALUES (?, ?)
['Mary', 'secure']
COMMIT
<sqlalchemy.engine.base.ResultProxy object at 0x...>
>>> # insert multiple rows simultaneously
>>> i.execute({'user_name':'Tom'}, {'user_name':'Fred'}, {'user_name':'Harry'}) # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE
INSERT INTO users (user_name) VALUES (?)
[['Tom'], ['Fred'], ['Harry']]
COMMIT
<sqlalchemy.engine.base.ResultProxy object at 0x...>
Note that the `VALUES` clause of each `INSERT` statement was automatically adjusted to correspond to the parameters sent to the `execute()` method. This is because the compilation step of a `ClauseElement` takes into account not just the constructed SQL object and the specifics of the type of database being used, but the execution parameters sent along as well.
When constructing clause objects, SQLAlchemy will bind all literal values into bind parameters. On the construction side, bind parameters are always treated as named parameters. At compilation time, SQLAlchemy will convert them into their proper format, based on the paramstyle of the underlying DBAPI. This works equally well for all named and positional bind parameter formats described in the DBAPI specification.
Documentation on inserting: [sql_insert](rel:sql_insert).
### Selecting
Let's check that the data we have put into `users` table is actually there. The procedure is analogous to the insert example above, except you now call the `select()` method off the `users` table:
{python}
>>> s = users_table.select()
>>> print s
SELECT users.user_id, users.user_name, users.password
FROM users
>>> r = s.execute()
SELECT users.user_id, users.user_name, users.password
FROM users
[]
This time, we won't ignore the return value of `execute()`. Its an instance of `ResultProxy`, which is a result-holding object that behaves very similarly to the `cursor` object one deals with directly with a database API:
{python}
>>> r # doctest:+ELLIPSIS
<sqlalchemy.engine.base.ResultProxy object at 0x...>
>>> r.fetchone()
(1, u'Mary', u'secure')
>>> r.fetchall()
[(2, u'Tom', None), (3, u'Fred', None), (4, u'Harry', None)]
Query criterion for the select is specified using Python expressions, using the `Column` objects in the `Table` as a base. All expressions constructed from `Column` objects are themselves instances of `ClauseElements`, just like the `Select`, `Insert`, and `Table` objects themselves.
{python}
>>> r = users_table.select(users_table.c.user_name=='Harry').execute()
SELECT users.user_id, users.user_name, users.password
FROM users
WHERE users.user_name = ?
['Harry']
>>> row = r.fetchone()
>>> print row
(4, u'Harry', None)
Pretty much the full range of standard SQL operations are supported as constructed Python expressions, including joins, ordering, grouping, functions, correlated subqueries, unions, etc. Documentation on selecting: [sql_select](rel:sql_select).
### Working with Rows
You can see that when we print out the rows returned by an execution result, it prints the rows as tuples. These rows support both the list and dictionary interfaces. The dictionary interface allows the addressing of columns by string column name, or even the original `Column` object:
{python}
>>> row.keys()
[u'user_id', u'user_name', u'password']
>>> row['user_id'], row[1], row[users_table.c.password]
(4, u'Harry', None)
Addressing the columns in a row based on the original `Column` object is especially handy, as it eliminates the need to work with literal column names altogether.
Result sets also support iteration. We'll show this with a slightly different form of `select` that allows you to specify the specific columns to be selected:
{python}
>>> for row in select([users_table.c.user_id, users_table.c.user_name]).execute(): # doctest:+NORMALIZE_WHITESPACE
... print row
SELECT users.user_id, users.user_name
FROM users
[]
(1, u'Mary')
(2, u'Tom')
(3, u'Fred')
(4, u'Harry')
### Table Relationships {@name=table_relationships}
Lets create a second table, `email_addresses`, which references the `users` table. To define the relationship between the two tables, we will use the `ForeignKey` construct. We will also issue the `CREATE` statement for the table:
{python}
>>> email_addresses_table = Table('email_addresses', metadata,
... Column('address_id', Integer, primary_key=True),
... Column('email_address', String(100), nullable=False),
... Column('user_id', Integer, ForeignKey('users.user_id')))
>>> email_addresses_table.create() # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE
CREATE TABLE email_addresses (
address_id INTEGER NOT NULL,
email_address VARCHAR(100) NOT NULL,
user_id INTEGER,
PRIMARY KEY (address_id),
FOREIGN KEY(user_id) REFERENCES users (user_id)
)
...
Above, the `email_addresses` table is related to the `users` table via the `ForeignKey('users.user_id')`. The `ForeignKey` constructor can take a `Column` object or a string representing the table and column name. When using the string argument, the referenced table must exist within the same `MetaData` object; thats where it looks for the other table!
Next, lets put a few rows in:
{python}
>>> email_addresses_table.insert().execute(
... {'email_address':'tom@tom.com', 'user_id':2},
... {'email_address':'mary@mary.com', 'user_id':1}) #doctest:+ELLIPSIS
INSERT INTO email_addresses (email_address, user_id) VALUES (?, ?)
[['tom@tom.com', 2], ['mary@mary.com', 1]]
COMMIT
<sqlalchemy.engine.base.ResultProxy object at 0x...>
With two related tables, we can now construct a join amongst them using the `join` method:
{python}
>>> r = users_table.join(email_addresses_table).select(order_by=users_table.c.user_id).execute()
SELECT users.user_id, users.user_name, users.password, email_addresses.address_id, email_addresses.email_address, email_addresses.user_id
FROM users JOIN email_addresses ON users.user_id = email_addresses.user_id ORDER BY users.user_id
[]
>>> print [row for row in r]
[(1, u'Mary', u'secure', 2, u'mary@mary.com', 1), (2, u'Tom', None, 1, u'tom@tom.com', 2)]
The `join` method is also a standalone function in the `sqlalchemy` namespace. The join condition is figured out from the foreign keys of the Table objects given. The condition (also called the "ON clause") can be specified explicitly, such as in this example which creates a join representing all users that used their email address as their password:
{python}
>>> print join(users_table, email_addresses_table,
... and_(users_table.c.user_id==email_addresses_table.c.user_id,
... users_table.c.password==email_addresses_table.c.email_address)
... )
users JOIN email_addresses ON users.user_id = email_addresses.user_id AND users.password = email_addresses.email_address
Working with Object Mappers {@name=orm}
-----------------------------------------------
Now that we have a little bit of Table and SQL operations covered, lets look into SQLAlchemy's ORM (object relational mapper). With the ORM, you associate Tables (and other *Selectable* units, like queries and table aliases) with Python classes, into units called **Mappers**. Then you can execute queries that return lists of object instances, instead of result sets. The object instances themselves are associated with an object called a **Session**, which automatically tracks changes on each object and supports a "save all at once" operation called a **flush**.
To start, we will import the names necessary to use SQLAlchemy's ORM, again using `import *` for simplicities sake, even though we all know that in real life we should be importing individual names via "`from sqlalchemy.orm import symbol1, symbol2, ...`" or "`import sqlalchemy.orm as orm`":
{python}
>>> from sqlalchemy.orm import *
It should be noted that the above step is technically not needed when working with the 0.3 series of SQLAlchemy; all symbols from the `orm` package are also included in the `sqlalchemy` package. However, a future release (most likely the 0.4 series) will make the separate `orm` import required in order to use the object relational mapper, so its a good practice for now.
### Creating a Mapper {@name=mapper}
A Mapper is usually created once per Python class, and at its core primarily means to say, "objects of this class are to be stored as rows in this table". Lets create a class called `User`, which will represent a user object that is stored in our `users` table:
{python}
>>> class User(object):
... def __repr__(self):
... return "%s(%r,%r)" % (
... self.__class__.__name__, self.user_name, self.password)
The class is a new style class (i.e. it extends `object`) and does not require a constructor (although one may be provided if desired). We just have one `__repr__` method on it which will display basic information about the User. Note that the `__repr__` method references the instance variables `user_name` and `password` which otherwise aren't defined. While we are free to explicitly define these attributes and treat them normally, this is optional; as SQLAlchemy's `Mapper` construct will manage them for us, since their names correspond to the names of columns in the `users` table. Lets create a mapper, and observe that these attributes are now defined:
{python}
>>> mapper(User, users_table) # doctest: +ELLIPSIS
<sqlalchemy.orm.mapper.Mapper object at 0x...>
>>> u1 = User()
>>> print u1.user_name
None
>>> print u1.password
None
The `mapper` function returns a new instance of `Mapper`. As it is the first Mapper we have created for the `User` class, it is known as the classes' *primary mapper*. We generally don't need to hold onto the return value of the `mapper` function; SA can automatically locate this Mapper as needed when it deals with the `User` class.
### Obtaining a Session {@name=session}
After you create a Mapper, all operations with that Mapper require the usage of an important object called a `Session`. All objects loaded or saved by the Mapper must be *attached* to a `Session` object, which represents a kind of "workspace" of objects that are loaded into memory. A particular object instance can only be attached to one `Session` at a time (but of course can be moved around or detached altogether).
By default, you have to create a `Session` object explicitly before you can load or save objects. Theres several ways to manage sessions, but the most straightforward is to just create one, which we will do by saying, `create_session()`:
{python}
>>> session = create_session()
>>> session # doctest:+ELLIPSIS
<sqlalchemy.orm.session.Session object at 0x...>
### The Query Object {@name=query}
The Session has all kinds of methods on it to manage and inspect its collection of objects. The Session also provides an easy interface which can be used to query the database, by giving you an instance to a `Query` object corresponding to a particular Python class:
{python}
>>> query = session.query(User)
>>> print query.filter_by(user_name='Harry').all()
SELECT users.user_id AS users_user_id, users.user_name AS users_user_name, users.password AS users_password
FROM users
WHERE users.user_name = ? ORDER BY users.oid
['Harry']
[User(u'Harry',None)]
All querying for objects is performed via an instance of `Query`. The various `select` methods on an instance of `Mapper` also use an underlying `Query` object to perform the operation. A `Query` is always bound to a specific `Session`.
Lets turn off the database echoing for a moment, and try out a few methods on `Query`. The two methods used to narrow results are `filter()` and `filter_by()`, and the two most common methods used to load results are `all()` and `first()`. The `get()` method is used for a quick lookup by primary key. `filter_by()` works with keyword arguments, and `filter()` works with `ClauseElement` objects, which are constructed by using `Column` objects inside of Python expressions, in the same way as we did with our SQL select example in the previous section of this tutorial. Using `ClauseElement` structures to query objects is more verbose but more flexible:
{python}
>>> metadata.bind.echo = False
>>> print query.filter(User.c.user_id==3).all()
[User(u'Fred',None)]
>>> print query.get(2)
User(u'Tom',None)
>>> print query.filter_by(user_name='Mary').first()
User(u'Mary',u'secure')
>>> print query.filter(User.c.password==None).first()
User(u'Tom',None)
>>> print query.count()
4
Notice that our `User` class has a special attribute `c` attached to it. This 'c' represents the columns on the User's mapper's Table object. Saying `User.c.user_name` is synonymous with saying `users_table.c.user_name`, recalling that `User` is the Python class and `users_table` is our `Table` object.
### Making Changes {@name=changes}
With a little experience in loading objects, lets see what its like to make changes. First, lets create a new user "Ed". We do this by just constructing the new object. Then, we just add it to the session:
{python}
>>> ed = User()
>>> ed.user_name = 'Ed'
>>> ed.password = 'edspassword'
>>> session.save(ed)
>>> ed in session
True
Lets also make a few changes on some of the objects in the database. We will load them with our `Query` object, and then change some things.
{python}
>>> mary = query.filter_by(user_name='Mary').first()
>>> harry = query.filter_by(user_name='Harry').first()
>>> mary.password = 'marysnewpassword'
>>> harry.password = 'harrysnewpassword'
At the moment, nothing has been saved to the database; all of our changes are in memory only. What happens if some other part of the application also tries to load 'Mary' from the database and make some changes before we had a chance to save it ? Assuming that the same `Session` is used, loading 'Mary' from the database a second time will issue a second query in order locate the primary key of 'Mary', but will *return the same object instance as the one already loaded*. This behavior is due to an important property of the `Session` known as the **identity map**:
{python}
>>> mary2 = query.filter_by(user_name='Mary').first()
>>> mary is mary2
True
With the identity map, a single `Session` can be relied upon to keep all loaded instances straight.
As far as the issue of the same object being modified in two different Sessions, that's an issue of concurrency detection; SQLAlchemy does some basic concurrency checks when saving objects, with the option for a stronger check using version ids. See [advdatamapping_arguments](rel:advdatamapping_arguments) for more details.
### Saving {@name=saving}
With a new user "ed" and some changes made on "Mary" and "Harry", lets also mark "Fred" as deleted:
{python}
>>> fred = query.filter_by(user_name='Fred').first()
>>> session.delete(fred)
Then to send all of our changes to the database, we `flush()` the Session. Lets turn echo back on to see this happen!:
{python}
>>> metadata.bind.echo = True
>>> session.flush()
BEGIN
UPDATE users SET password=? WHERE users.user_id = ?
['marysnewpassword', 1]
UPDATE users SET password=? WHERE users.user_id = ?
['harrysnewpassword', 4]
INSERT INTO users (user_name, password) VALUES (?, ?)
['Ed', 'edspassword']
DELETE FROM users WHERE users.user_id = ?
[3]
COMMIT
### Relationships
When our User object contains relationships to other kinds of information, such as a list of email addresses, we can indicate this by using a function when creating the `Mapper` called `relation()`. While there is a lot you can do with relations, we'll cover a simple one here. First, recall that our `users` table has a foreign key relationship to another table called `email_addresses`. A single row in `email_addresses` has a column `user_id` that references a row in the `users` table; since many rows in the `email_addresses` table can reference a single row in `users`, this is called a *one to many* relationship.
To illustrate this relationship, we will start with a new mapper configuration. Since our `User` class has a mapper assigned to it, we want to discard it and start over again. So we issue the `clear_mappers()` function first, which removes all mapping associations from classes:
{python}
>>> clear_mappers()
When removing mappers, it is usually best to remove all mappings at the same time, since mappers usually have relationships to each other which will become invalid if only part of the mapper collection is removed. In practice, a particular mapping setup will usually remain throughout the lifetime of an application. Clearing out the mappers and making new ones is a practice that is generally limited to writing mapper unit tests and experimenting from the console.
Next, we want to create a class/mapping that corresponds to the `email_addresses` table. We will create a new class `Address` which represents a single row in the `email_addresses` table, and a corresponding `Mapper` which will associate the `Address` class with the `email_addresses` table:
{python}
>>> class Address(object):
... def __init__(self, email_address):
... self.email_address = email_address
... def __repr__(self):
... return "%s(%r)" % (
... self.__class__.__name__, self.email_address)
>>> mapper(Address, email_addresses_table) # doctest: +ELLIPSIS
<sqlalchemy.orm.mapper.Mapper object at 0x...>
We then create a mapper for the `User` class which contains a relationship to the `Address` class using the `relation()` function:
{python}
>>> mapper(User, users_table, properties={ # doctest: +ELLIPSIS
... 'addresses':relation(Address)
... })
<sqlalchemy.orm.mapper.Mapper object at 0x...>
Since we've made new mappers, we have to throw away the old `Query` object and get a new one:
>>> query = session.query(User)
The `relation()` function takes either a class or a Mapper as its first argument, and has many options to further control its behavior. When this mapping relationship is used, each new `User` instance will contain an attribute called `addresses`. SQLAlchemy will automatically determine that this relationship is a one-to-many relationship, and will subsequently create `addresses` as a list. When a new `User` is created, this list will begin as empty.
The order in which the mapping definitions for `User` and `Address` is created is *not significant*. When the `mapper()` function is called, it creates an *uncompiled* mapping record corresponding to the given class/table combination. When the mappers are first used, the entire collection of mappers created up until that point will be compiled, which involves the establishment of class instrumentation as well as the resolution of all mapping relationships.
Lets try out this new mapping configuration, and see what we get for the email addresses already in the database. Since we have made a new mapping configuration, its best that we clear out our `Session`, which is currently holding onto every `User` object we have already loaded:
{python}
>>> session.clear()
We can then treat the `addresses` attribute on each `User` object like a regular list:
{python}
>>> mary = query.filter_by(user_name='Mary').first() # doctest: +NORMALIZE_WHITESPACE
SELECT users.user_id AS users_user_id, users.user_name AS users_user_name, users.password AS users_password
FROM users
WHERE users.user_name = ? ORDER BY users.oid
LIMIT 1 OFFSET 0
['Mary']
>>> print [a for a in mary.addresses]
SELECT email_addresses.address_id AS email_addresses_address_id, email_addresses.email_address AS email_addresses_email_address, email_addresses.user_id AS email_addresses_user_id
FROM email_addresses
WHERE ? = email_addresses.user_id ORDER BY email_addresses.oid
[1]
[Address(u'mary@mary.com')]
Adding to the list is just as easy. New `Address` objects will be detected and saved when we `flush` the Session:
{python}
>>> mary.addresses.append(Address('mary2@gmail.com'))
>>> session.flush() # doctest: +NORMALIZE_WHITESPACE
BEGIN
INSERT INTO email_addresses (email_address, user_id) VALUES (?, ?)
['mary2@gmail.com', 1]
COMMIT
Main documentation for using mappers: [datamapping](rel:datamapping)
### Transactions
You may have noticed from the example above that when we say `session.flush()`, SQLAlchemy indicates the names `BEGIN` and `COMMIT` to indicate a transaction with the database. The `flush()` method, since it may execute many statements in a row, will automatically use a transaction in order to execute these instructions. But what if we want to use `flush()` inside of a larger transaction? The easiest way is to use a "transactional" session; that is, when the session is created, you're automatically in a transaction which you can commit or rollback at any time. As a bonus, it offers the ability to call `flush()` for you, whenever a query is issued; that way whatever changes you've made can be returned right back (and since its all in a transaction, nothing gets committed until you tell it so).
Below, we create a session with `autoflush=True`, which implies that it's transactional. We can query for things as soon as they are created without the need for calling `flush()`. At the end, we call `commit()` to persist everything permanently.
{python}
>>> metadata.bind.echo = False
>>> session = create_session(autoflush=True)
>>> (ed, harry, mary) = session.query(User).filter(
... User.c.user_name.in_(['Ed', 'Harry', 'Mary'])
... ).order_by(User.c.user_name).all() # doctest: +NORMALIZE_WHITESPACE
>>> del mary.addresses[1]
>>> harry_address = Address('harry2@gmail.com')
>>> harry.addresses.append(harry_address)
>>> session.query(User).join('addresses').filter_by(email_address='harry2@gmail.com').first() # doctest: +NORMALIZE_WHITESPACE
User(u'Harry',u'harrysnewpassword')
>>> session.commit()
Main documentation: [unitofwork](rel:unitofwork)
Next Steps
----------
That covers a quick tour through the basic idea of SQLAlchemy, in its simplest form. Beyond that, one should familiarize oneself with the basics of Sessions, the various patterns that can be used to define different kinds of Mappers and relations among them, the rudimentary SQL types that are available when constructing Tables, and the basics of Engines, SQL statements, and database Connections.
-155
View File
@@ -1,155 +0,0 @@
The Types System {@name=types}
================
The package `sqlalchemy.types` defines the datatype identifiers which may be used when defining [metadata](rel: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.
### Built-in Types {@name=standard}
SQLAlchemy comes with a set of standard generic datatypes, which are defined as classes. Types are usually used when defining tables, and can be left as a class or instantiated, for example:
{python}
mytable = Table('mytable', metadata,
Column('myid', Integer, primary_key=True),
Column('data', String(30)),
Column('info', Unicode(100)),
Column('value', Number(7,4))
)
Following is a rundown of the standard types.
#### String
This type is the base type for all string and character types, such as `Unicode`, `TEXT`, `CLOB`, etc. By default it generates a VARCHAR in DDL. It includes an argument `length`, which indicates the length in characters of the type, as well as `convert_unicode` and `assert_unicode`, which are booleans. `length` will be used as the length argument when generating DDL. If `length` is omitted, the `String` type resolves into the `TEXT` type.
`convert_unicode=True` indicates that incoming strings, if they are Python `unicode` strings, will be encoded into a raw bytestring using the `encoding` attribute of the dialect (defaults to `utf-8`). Similarly, raw bytestrings coming back from the database will be decoded into `unicode` objects on the way back.
`assert_unicode` is set to `None` by default. When `True`, it indicates that incoming bind parameters will be checked that they are in fact `unicode` objects, else an error is raised. A value of `'warn'` instead raises a warning. Setting it to `None` indicates that the dialect-level `convert_unicode` setting should take place, whereas setting it to `False` disables it unconditionally (this flag is new as of version 0.4.2).
Both `convert_unicode` and `assert_unicode` may be set at the engine level as flags to `create_engine()`.
#### Unicode
The `Unicode` type is shorthand for `String` with `convert_unicode=True` and `assert_unicode='warn'`. When writing a unicode-aware appication, it is strongly recommended that this type is used, and that only unicode strings are used in the application. By "unicode string" we mean a string with a u, i.e. `u'hello'`. Otherwise, particularly when using the ORM, data will be converted to unicode when it returns from the database, but local data which was generated locally will not be in unicode format, which can create confusion.
#### Numeric
TODO
#### Float
TODO
#### Datetime/Date/Time
TODO
#### Interval
TODO
#### Binary
TODO
#### Boolean
TODO
#### PickleType
TODO
#### SQL-Specific Types {@name=sqlspecific}
These are subclasses of the generic types and include:
{python}
class FLOAT(Numeric)
class TEXT(String)
class DECIMAL(Numeric)
class INT(Integer)
INTEGER = INT
class TIMESTAMP(DateTime)
class DATETIME(DateTime)
class CLOB(String)
class VARCHAR(String)
class CHAR(String)
class BLOB(Binary)
class BOOLEAN(Boolean)
### Dialect Specific Types {@name=dialect}
Each dialect has its own set of types, many of which are available only within that dialect. For example, MySQL has a `BigInteger` type and Postgres has an `Inet` type. To use these, import them from the module explicitly:
{python}
from sqlalchemy.databases.mysql import MSEnum, MSBigInteger
table = Table('foo', meta,
Column('enumerates', MSEnum('a', 'b', 'c')),
Column('id', MSBigInteger)
)
Or some postgres types:
{python}
from sqlalchemy.databases.postgres import PGInet, PGArray
table = Table('foo', meta,
Column('ipaddress', PGInet),
Column('elements', PGArray(str)) # PGArray is available in 0.4, and takes a type argument
)
### Creating your Own Types {@name=custom}
User-defined types can be created which can augment the bind parameter and result processing capabilities of the built in types. This is usually achieved using the `TypeDecorator` class, which "decorates" the behavior of any existing type. As of version 0.4.2, the new `process_bind_param()` and `process_result_value()` methods should be used:
{python}
import sqlalchemy.types as types
class MyType(types.TypeDecorator):
"""a type that decorates Unicode, prefixes values with "PREFIX:" on
the way in and strips it off on the way out."""
impl = types.Unicode
def process_bind_param(self, value, engine):
return "PREFIX:" + value
def process_result_value(self, value, engine):
return value[7:]
def copy(self):
return MyType(self.impl.length)
Note that the "old" way to process bind params and result values, the `convert_bind_param()` and `convert_result_value()` methods, are still available. The downside of these is that when using a type which already processes data such as the `Unicode` type, you need to call the superclass version of these methods directly. Using `process_bind_param()` and `process_result_value()`, user-defined code can return and receive the desired Python data directly.
As of version 0.4.2, `TypeDecorator` should generally be used for any user-defined type which redefines the behavior of another type, including other `TypeDecorator` subclasses such as `PickleType`, and the new `process_...()` methods described above should be used.
To build a type object from scratch, which will not have a corresponding database-specific implementation, subclass `TypeEngine`:
{python}
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, engine):
return value
def convert_result_value(self, value, engine):
return value
Once you make your type, its immediately useable:
{python}
table = Table('foo', meta,
Column('id', Integer, primary_key=True),
Column('data', MyType(16))
)
@@ -1,10 +1,11 @@
Appendix: Copyright {@name=copyright}
================
====================
Appendix: Copyright
====================
This is the MIT license: http://www.opensource.org/licenses/mit-license.php
This is the MIT license: `<http://www.opensource.org/licenses/mit-license.php>`_
Copyright (c) 2005, 2006, 2007, 2008 Michael Bayer and contributors. SQLAlchemy is a trademark of Michael
Bayer.
Copyright (c) 2005, 2006, 2007, 2008, 2009, 2010 Michael Bayer and contributors.
SQLAlchemy is a trademark of Michael Bayer.
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
+457
View File
@@ -0,0 +1,457 @@
.. _engines_toplevel:
================
Database Engines
================
The **Engine** is the starting point for any SQLAlchemy application. It's "home base" for the actual database and its DBAPI, delivered to the SQLAlchemy application through a connection pool and a **Dialect**, which describes how to talk to a specific kind of database/DBAPI combination.
The general structure is this::
+-----------+ __________
/---| Pool |---\ (__________)
+-------------+ / +-----------+ \ +--------+ | |
connect() <--| Engine |---x x----| DBAPI |---| database |
+-------------+ \ +-----------+ / +--------+ | |
\---| Dialect |---/ |__________|
+-----------+ (__________)
Where above, a :class:`~sqlalchemy.engine.Engine` references both a :class:`~sqlalchemy.engine.Dialect` and :class:`~sqlalchemy.pool.Pool`, which together interpret the DBAPI's module functions as well as the behavior of the database.
Creating an engine is just a matter of issuing a single call, :func:`create_engine()`::
engine = create_engine('postgresql://scott:tiger@localhost:5432/mydatabase')
The above engine invokes the ``postgresql`` dialect and a connection pool which references ``localhost:5432``.
The engine can be used directly to issue SQL to the database. The most generic way is to use connections, which you get via the ``connect()`` method::
connection = engine.connect()
result = connection.execute("select username from users")
for row in result:
print "username:", row['username']
connection.close()
The connection is an instance of :class:`~sqlalchemy.engine.Connection`, which is a **proxy** object for an actual DBAPI connection. The returned result is an instance of :class:`~sqlalchemy.engine.ResultProxy`, which acts very much like a DBAPI cursor.
When you say ``engine.connect()``, a new ``Connection`` object is created, and a DBAPI connection is retrieved from the connection pool. Later, when you call ``connection.close()``, the DBAPI connection is returned to the pool; nothing is actually "closed" from the perspective of the database.
To execute some SQL more quickly, you can skip the ``Connection`` part and just say::
result = engine.execute("select username from users")
for row in result:
print "username:", row['username']
result.close()
Where above, the ``execute()`` method on the ``Engine`` does the ``connect()`` part for you, and returns the ``ResultProxy`` directly. The actual ``Connection`` is *inside* the ``ResultProxy``, waiting for you to finish reading the result. In this case, when you ``close()`` the ``ResultProxy``, the underlying ``Connection`` is closed, which returns the DBAPI connection to the pool.
To summarize the above two examples, when you use a ``Connection`` object, it's known as **explicit execution**. When you don't see the ``Connection`` object, but you still use the ``execute()`` method on the ``Engine``, it's called **explicit, connectionless execution**. A third variant of execution also exists called **implicit execution**; this will be described later.
The ``Engine`` and ``Connection`` can do a lot more than what we illustrated above; SQL strings are only its most rudimentary function. Later chapters will describe how "constructed SQL" expressions can be used with engines; in many cases, you don't have to deal with the ``Engine`` at all after it's created. The Object Relational Mapper (ORM), an optional feature of SQLAlchemy, also uses the ``Engine`` in order to get at connections; that's also a case where you can often create the engine once, and then forget about it.
.. _supported_dbapis:
Supported Databases
====================
Recall that the ``Dialect`` is used to describe how to talk to a specific kind of database. Dialects are included with SQLAlchemy for many different backends; these can be seen as a Python package within the :mod:`~sqlalchemy.dialect` package. Each dialect requires the appropriate DBAPI drivers to be installed separately.
Dialects included with SQLAlchemy fall under one of three categories: supported, experimental, and third party. Supported drivers are those which work against the most common databases available in the open source world, including SQLite, PostgreSQL, MySQL, and Firebird. Very popular commercial databases which provide easy access to test platforms are also supported, these currently include MSSQL and Oracle. These dialects are tested frequently and the level of support should be close to 100% for each.
The experimental category is for drivers against less common database platforms, or commercial platforms for which no freely available and easily usable test platform is provided. These include Access, MaxDB, Informix, and Sybase at the time of this writing. These are not-yet-functioning
or partially-functioning dialects for which the SQLAlchemy project is not able to provide regular test support. If you're interested in supporting one of these backends, contact the mailing list.
There are also third-party dialects available - currently IBM offers a DB2/Informix IDS dialect for SQLAlchemy.
Downloads for each DBAPI at the time of this writing are as follows:
* Supported Dialects
- PostgreSQL: `psycopg2 <http://www.initd.org/tracker/psycopg/wiki/PsycopgTwo>`_ `pg8000 <http://pybrary.net/pg8000/>`_
- PostgreSQL on Jython: `PostgreSQL JDBC Driver <http://jdbc.postgresql.org/>`_
- SQLite: `sqlite3 <http://www.python.org/doc/2.5.2/lib/module-sqlite3.html>`_ (included in Python 2.5 or greater) `pysqlite <http://initd.org/tracker/pysqlite>`_
- MySQL: `MySQLdb (a.k.a. mysql-python) <http://sourceforge.net/projects/mysql-python>`_ `MySQL Connector/Python <https://launchpad.net/myconnpy>`_
- MySQL on Jython: `MySQL Connector/J JDBC driver <http://dev.mysql.com/downloads/connector/j/>`_
- Oracle: `cx_Oracle <http://cx-oracle.sourceforge.net/>`_
- Oracle on Jython: `Oracle JDBC Driver <http://www.oracle.com/technology/software/tech/java/sqlj_jdbc/index.html>`_
- Firebird: `kinterbasdb <http://kinterbasdb.sourceforge.net/>`_
- MS-SQL, MSAccess: `pyodbc <http://pyodbc.sourceforge.net/>`_ (recommended) `adodbapi <http://adodbapi.sourceforge.net/>`_ `pymssql <http://pymssql.sourceforge.net/>`_
- MS-SQL on Jython: `jTDS JDBC Driver <http://jtds.sourceforge.net/>`_
* Experimental Dialects
- MSAccess: `pyodbc <http://pyodbc.sourceforge.net/>`_
- Informix: `informixdb <http://informixdb.sourceforge.net/>`_
- Sybase: TODO
- MAXDB: `sapdb <http://www.sapdb.org/sapdbapi.html>`_
* Third Party Dialects
- DB2/Informix IDS: `ibm-db <http://code.google.com/p/ibm-db/>`_
The SQLAlchemy Wiki contains a page of database notes, describing whatever quirks and behaviors have been observed. Its a good place to check for issues with specific databases. `Database Notes <http://www.sqlalchemy.org/trac/wiki/DatabaseNotes>`_
create_engine() URL Arguments
==============================
SQLAlchemy indicates the source of an Engine strictly via `RFC-1738 <http://rfc.net/rfc1738.html>`_ style URLs, combined with optional keyword arguments to specify options for the Engine. The form of the URL is:
dialect+driver://username:password@host:port/database
Dialect names include the identifying name of the SQLAlchemy dialect which include ``sqlite``, ``mysql``, ``postgresql``, ``oracle``, ``mssql``, and ``firebird``. The drivername is the name of the DBAPI to be used to connect to the database using all lowercase letters. If not specified, a "default" DBAPI will be imported if available - this default is typically the most widely known driver available for that backend (i.e. cx_oracle, pysqlite/sqlite3, psycopg2, mysqldb). For Jython connections, specify the `zxjdbc` driver, which is the JDBC-DBAPI bridge included with Jython.
.. sourcecode:: python+sql
# postgresql - psycopg2 is the default driver.
pg_db = create_engine('postgresql://scott:tiger@localhost/mydatabase')
pg_db = create_engine('postgresql+psycopg2://scott:tiger@localhost/mydatabase')
pg_db = create_engine('postgresql+pg8000://scott:tiger@localhost/mydatabase')
# postgresql on Jython
pg_db = create_engine('postgresql+zxjdbc://scott:tiger@localhost/mydatabase')
# mysql - MySQLdb (mysql-python) is the default driver
mysql_db = create_engine('mysql://scott:tiger@localhost/foo')
mysql_db = create_engine('mysql+mysqldb://scott:tiger@localhost/foo')
# mysql on Jython
mysql_db = create_engine('mysql+zxjdbc://localhost/foo')
# mysql with pyodbc (buggy)
mysql_db = create_engine('mysql+pyodbc://scott:tiger@some_dsn')
# oracle - cx_oracle is the default driver
oracle_db = create_engine('oracle://scott:tiger@127.0.0.1:1521/sidname')
# oracle via TNS name
oracle_db = create_engine('oracle+cx_oracle://scott:tiger@tnsname')
# mssql using ODBC datasource names. PyODBC is the default driver.
mssql_db = create_engine('mssql://mydsn')
mssql_db = create_engine('mssql+pyodbc://mydsn')
mssql_db = create_engine('mssql+adodbapi://mydsn')
mssql_db = create_engine('mssql+pyodbc://username:password@mydsn')
SQLite connects to file based databases. The same URL format is used, omitting the hostname, and using the "file" portion as the filename of the database. This has the effect of four slashes being present for an absolute file path::
# sqlite://<nohostname>/<path>
# where <path> is relative:
sqlite_db = create_engine('sqlite:///foo.db')
# or absolute, starting with a slash:
sqlite_db = create_engine('sqlite:////absolute/path/to/foo.db')
To use a SQLite ``:memory:`` database, specify an empty URL::
sqlite_memory_db = create_engine('sqlite://')
The :class:`~sqlalchemy.engine.base.Engine` will ask the connection pool for a connection when the ``connect()`` or ``execute()`` methods are called. The default connection pool, :class:`~sqlalchemy.pool.QueuePool`, as well as the default connection pool used with SQLite, :class:`~sqlalchemy.pool.SingletonThreadPool`, will open connections to the database on an as-needed basis. As concurrent statements are executed, :class:`~sqlalchemy.pool.QueuePool` will grow its pool of connections to a default size of five, and will allow a default "overflow" of ten. Since the ``Engine`` is essentially "home base" for the connection pool, it follows that you should keep a single :class:`~sqlalchemy.engine.base.Engine` per database established within an application, rather than creating a new one for each connection.
Custom DBAPI connect() arguments
--------------------------------
Custom arguments used when issuing the ``connect()`` call to the underlying DBAPI may be issued in three distinct ways. String-based arguments can be passed directly from the URL string as query arguments:
.. sourcecode:: python+sql
db = create_engine('postgresql://scott:tiger@localhost/test?argument1=foo&argument2=bar')
If SQLAlchemy's database connector is aware of a particular query argument, it may convert its type from string to its proper type.
``create_engine`` also takes an argument ``connect_args`` which is an additional dictionary that will be passed to ``connect()``. This can be used when arguments of a type other than string are required, and SQLAlchemy's database connector has no type conversion logic present for that parameter:
.. sourcecode:: python+sql
db = create_engine('postgresql://scott:tiger@localhost/test', connect_args = {'argument1':17, 'argument2':'bar'})
The most customizable connection method of all is to pass a ``creator`` argument, which specifies a callable that returns a DBAPI connection:
.. sourcecode:: python+sql
def connect():
return psycopg.connect(user='scott', host='localhost')
db = create_engine('postgresql://', creator=connect)
.. _create_engine_args:
Database Engine Options
========================
Keyword options can also be specified to ``create_engine()``, following the string URL as follows:
.. sourcecode:: python+sql
db = create_engine('postgresql://...', encoding='latin1', echo=True)
Options common to all database dialects are described at :func:`~sqlalchemy.create_engine`.
More On Connections
====================
Recall from the beginning of this section that the Engine provides a ``connect()`` method which returns a ``Connection`` object. ``Connection`` is a *proxy* object which maintains a reference to a DBAPI connection instance. The ``close()`` method on ``Connection`` does not actually close the DBAPI connection, but instead returns it to the connection pool referenced by the ``Engine``. ``Connection`` will also automatically return its resources to the connection pool when the object is garbage collected, i.e. its ``__del__()`` method is called. When using the standard C implementation of Python, this method is usually called immediately as soon as the object is dereferenced. With other Python implementations such as Jython, this is not so guaranteed.
The ``execute()`` methods on both ``Engine`` and ``Connection`` can also receive SQL clause constructs as well::
connection = engine.connect()
result = connection.execute(select([table1], table1.c.col1==5))
for row in result:
print row['col1'], row['col2']
connection.close()
The above SQL construct is known as a ``select()``. The full range of SQL constructs available are described in :ref:`sqlexpression_toplevel`.
Both ``Connection`` and ``Engine`` fulfill an interface known as ``Connectable`` which specifies common functionality between the two objects, namely being able to call ``connect()`` to return a ``Connection`` object (``Connection`` just returns itself), and being able to call ``execute()`` to get a result set. Following this, most SQLAlchemy functions and objects which accept an ``Engine`` as a parameter or attribute with which to execute SQL will also accept a ``Connection``. This argument is named ``bind``::
engine = create_engine('sqlite:///:memory:')
# specify some Table metadata
metadata = MetaData()
table = Table('sometable', metadata, Column('col1', Integer))
# create the table with the Engine
table.create(bind=engine)
# drop the table with a Connection off the Engine
connection = engine.connect()
table.drop(bind=connection)
.. index::
single: thread safety; connections
Connection facts:
* the Connection object is **not thread-safe**. While a Connection can be shared among threads using properly synchronized access, this is also not recommended as many DBAPIs have issues with, if not outright disallow, sharing of connection state between threads.
* The Connection object represents a single dbapi connection checked out from the connection pool. In this state, the connection pool has no affect upon the connection, including its expiration or timeout state. For the connection pool to properly manage connections, **connections should be returned to the connection pool (i.e. ``connection.close()``) whenever the connection is not in use**. If your application has a need for management of multiple connections or is otherwise long running (this includes all web applications, threaded or not), don't hold a single connection open at the module level.
Using Transactions with Connection
===================================
The ``Connection`` object provides a ``begin()`` method which returns a ``Transaction`` object. This object is usually used within a try/except clause so that it is guaranteed to ``rollback()`` or ``commit()``::
trans = connection.begin()
try:
r1 = connection.execute(table1.select())
connection.execute(table1.insert(), col1=7, col2='this is some data')
trans.commit()
except:
trans.rollback()
raise
The ``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.
.. sourcecode:: python+sql
# method_a starts a transaction and calls method_b
def method_a(connection):
trans = connection.begin() # open a transaction
try:
method_b(connection)
trans.commit() # transaction is committed here
except:
trans.rollback() # this rolls back the transaction unconditionally
raise
# method_b also starts a transaction
def method_b(connection):
trans = connection.begin() # open a transaction - this runs in the context of method_a's transaction
try:
connection.execute("insert into mytable values ('bat', 'lala')")
connection.execute(mytable.insert(), col1='bat', col2='lala')
trans.commit() # transaction is not committed yet
except:
trans.rollback() # this rolls back the transaction unconditionally
raise
# open a Connection and call method_a
conn = engine.connect()
method_a(conn)
conn.close()
Above, ``method_a`` is called first, which calls ``connection.begin()``. Then it calls ``method_b``. When ``method_b`` calls ``connection.begin()``, it just increments a counter that is decremented when it calls ``commit()``. If either ``method_a`` or ``method_b`` calls ``rollback()``, the whole transaction is rolled back. The transaction is not committed until ``method_a`` calls the ``commit()`` method. This "nesting" behavior allows the creation of functions which "guarantee" that a transaction will be used if one was not already available, but will automatically participate in an enclosing transaction if one exists.
Note that SQLAlchemy's Object Relational Mapper also provides a way to control transaction scope at a higher level; this is described in :ref:`unitofwork_transaction`.
.. index::
single: thread safety; transactions
Transaction Facts:
* the Transaction object, just like its parent Connection, is **not thread-safe**.
Understanding Autocommit
------------------------
The above transaction example illustrates how to use ``Transaction`` so that several executions can take part in the same transaction. What happens when we issue an INSERT, UPDATE or DELETE call without using ``Transaction``? The answer is **autocommit**. While many DBAPIs implement a flag called ``autocommit``, the current SQLAlchemy behavior is such that it implements its own autocommit. This is achieved by detecting statements which represent data-changing operations, i.e. INSERT, UPDATE, DELETE, etc., and then issuing a COMMIT automatically if no transaction is in progress. The detection is based on compiled statement attributes, or in the case of a text-only statement via regular expressions.
.. sourcecode:: python+sql
conn = engine.connect()
conn.execute("INSERT INTO users VALUES (1, 'john')") # autocommits
.. _dbengine_implicit:
Connectionless Execution, Implicit Execution
=============================================
Recall from the first section we mentioned executing with and without a ``Connection``. ``Connectionless`` execution refers to calling the ``execute()`` method on an object which is not a ``Connection``, which could be on the ``Engine`` itself, or could be a constructed SQL object. When we say "implicit", we mean that we are calling the ``execute()`` method on an object which is neither a ``Connection`` nor an ``Engine`` object; this can only be used with constructed SQL objects which have their own ``execute()`` method, and can be "bound" to an ``Engine``. A description of "constructed SQL objects" may be found in :ref:`sqlexpression_toplevel`.
A summary of all three methods follows below. First, assume the usage of the following ``MetaData`` and ``Table`` objects; while we haven't yet introduced these concepts, for now you only need to know that we are representing a database table, and are creating an "executable" SQL construct which issues a statement to the database. These objects are described in :ref:`metadata_toplevel`.
.. sourcecode:: python+sql
meta = MetaData()
users_table = Table('users', meta,
Column('id', Integer, primary_key=True),
Column('name', String(50))
)
Explicit execution delivers the SQL text or constructed SQL expression to the ``execute()`` method of ``Connection``:
.. sourcecode:: python+sql
engine = create_engine('sqlite:///file.db')
connection = engine.connect()
result = connection.execute(users_table.select())
for row in result:
# ....
connection.close()
Explicit, connectionless execution delivers the expression to the ``execute()`` method of ``Engine``:
.. sourcecode:: python+sql
engine = create_engine('sqlite:///file.db')
result = engine.execute(users_table.select())
for row in result:
# ....
result.close()
Implicit execution is also connectionless, and calls the ``execute()`` method on the expression itself, utilizing the fact that either an ``Engine`` or ``Connection`` has been *bound* to the expression object (binding is discussed further in the next section, :ref:`metadata_toplevel`):
.. sourcecode:: python+sql
engine = create_engine('sqlite:///file.db')
meta.bind = engine
result = users_table.select().execute()
for row in result:
# ....
result.close()
In both "connectionless" examples, the ``Connection`` is created behind the scenes; the ``ResultProxy`` returned by the ``execute()`` call references the ``Connection`` used to issue the SQL statement. When we issue ``close()`` on the ``ResultProxy``, or if the result set object falls out of scope and is garbage collected, the underlying ``Connection`` is closed for us, resulting in the DBAPI connection being returned to the pool.
.. _threadlocal_strategy:
Using the Threadlocal Execution Strategy
-----------------------------------------
The "threadlocal" engine strategy is used by non-ORM applications which wish to bind a transaction to the current thread, such that all parts of the application can participate in that transaction implicitly without the need to explicitly reference a ``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 ``Session`` object, as the ``Session`` itself represents an ongoing transaction and itself handles the job of maintaining connection and transactional resources.
Enabling ``threadlocal`` is achieved as follows:
.. sourcecode:: python+sql
db = create_engine('mysql://localhost/test', strategy='threadlocal')
When the engine above is used in a "connectionless" style, meaning ``engine.execute()`` is called, a DBAPI connection is retrieved from the connection pool and then associated with the current thread. Subsequent operations on the ``Engine`` while the DBAPI connection remains checked out will make use of the *same* DBAPI connection object. The connection stays allocated until all returned ``ResultProxy`` objects are closed, which occurs for a particular ``ResultProxy`` after all pending results are fetched, or immediately for an operation which returns no rows (such as an INSERT).
.. sourcecode:: python+sql
# execute one statement and receive results. r1 now references a DBAPI connection resource.
r1 = db.execute("select * from table1")
# execute a second statement and receive results. r2 now references the *same* resource as r1
r2 = db.execute("select * from table2")
# fetch a row on r1 (assume more results are pending)
row1 = r1.fetchone()
# fetch a row on r2 (same)
row2 = r2.fetchone()
# close r1. the connection is still held by r2.
r1.close()
# close r2. with no more references to the underlying connection resources, they
# are returned to the pool.
r2.close()
The above example does not illustrate any pattern that is particularly useful, as it is not a frequent occurence that two execute/result fetching operations "leapfrog" one another. There is a slight savings of connection pool checkout overhead between the two operations, and an implicit sharing of the same transactional context, but since there is no explicitly declared transaction, this association is short lived.
The real usage of "threadlocal" comes when we want several operations to occur within the scope of a shared transaction. The ``Engine`` now has ``begin()``, ``commit()`` and ``rollback()`` methods which will retrieve a connection resource from the pool and establish a new transaction, maintaining the connection against the current thread until the transaction is committed or rolled back:
.. sourcecode:: python+sql
db.begin()
try:
call_operation1()
call_operation2()
db.commit()
except:
db.rollback()
``call_operation1()`` and ``call_operation2()`` can make use of the ``Engine`` as a global variable, using the "connectionless" execution style, and their operations will participate in the same transaction:
.. sourcecode:: python+sql
def call_operation1():
engine.execute("insert into users values (?, ?)", 1, "john")
def call_operation2():
users.update(users.c.user_id==5).execute(name='ed')
When using threadlocal, operations that do call upon the ``engine.connect()`` method will receive a ``Connection`` that is **outside** the scope of the transaction. This can be used for operations such as logging the status of an operation regardless of transaction success:
.. sourcecode:: python+sql
db.begin()
conn = db.connect()
try:
conn.execute(log_table.insert(), message="Operation started")
call_operation1()
call_operation2()
db.commit()
conn.execute(log_table.insert(), message="Operation succeeded")
except:
db.rollback()
conn.execute(log_table.insert(), message="Operation failed")
finally:
conn.close()
Functions which are written to use an explicit ``Connection`` object, but wish to participate in the threadlocal transaction, can receive their ``Connection`` object from the ``contextual_connect()`` method, which returns a ``Connection`` that is **inside** the scope of the transaction:
.. sourcecode:: python+sql
conn = db.contextual_connect()
call_operation3(conn)
conn.close()
Calling ``close()`` on the "contextual" connection does not release the connection resources to the pool if other resources are making use of it. A resource-counting mechanism is employed so that the connection is released back to the pool only when all users of that connection, including the transaction established by ``engine.begin()``, have been completed.
So remember - if you're not sure if you need to use ``strategy="threadlocal"`` or not, the answer is **no** ! It's driven by a specific programming pattern that is generally not the norm.
Configuring Logging
====================
Python's standard `logging <http://www.python.org/doc/lib/module-logging.html>`_ module is used to implement informational and debug log output with SQLAlchemy. This allows SQLAlchemy's logging to integrate in a standard way with other applications and libraries. The ``echo`` and ``echo_pool`` flags that are present on ``create_engine()``, as well as the ``echo_uow`` flag used on ``Session``, all interact with regular loggers.
This section assumes familiarity with the above linked logging module. All logging performed by SQLAlchemy exists underneath the ``sqlalchemy`` namespace, as used by ``logging.getLogger('sqlalchemy')``. When logging has been configured (i.e. such as via ``logging.basicConfig()``), the general namespace of SA loggers that can be turned on is as follows:
* ``sqlalchemy.engine`` - controls SQL echoing. set to ``logging.INFO`` for SQL query output, ``logging.DEBUG`` for query + result set output.
* ``sqlalchemy.pool`` - controls connection pool logging. set to ``logging.INFO`` or lower to log connection pool checkouts/checkins.
* ``sqlalchemy.orm`` - controls logging of various ORM functions. set to ``logging.INFO`` for configurational logging as well as unit of work dumps, ``logging.DEBUG`` for extensive logging during query and flush() operations. Subcategories of ``sqlalchemy.orm`` include:
* ``sqlalchemy.orm.attributes`` - logs certain instrumented attribute operations, such as triggered callables
* ``sqlalchemy.orm.mapper`` - logs Mapper configuration and operations
* ``sqlalchemy.orm.unitofwork`` - logs flush() operations, including dependency sort graphs and other operations
* ``sqlalchemy.orm.strategies`` - logs relation loader operations (i.e. lazy and eager loads)
* ``sqlalchemy.orm.sync`` - logs synchronization of attributes from parent to child instances during a flush()
For example, to log SQL queries as well as unit of work debugging:
.. sourcecode:: python+sql
import logging
logging.basicConfig()
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
logging.getLogger('sqlalchemy.orm.unitofwork').setLevel(logging.DEBUG)
By default, the log level is set to ``logging.ERROR`` within the entire ``sqlalchemy`` namespace so that no log operations occur, even within an application that has logging enabled otherwise.
The ``echo`` flags present as keyword arguments to ``create_engine()`` and others as well as the ``echo`` property on ``Engine``, when set to ``True``, will first attempt to ensure that logging is enabled. Unfortunately, the ``logging`` module provides no way of determining if output has already been configured (note we are referring to if a logging configuration has been set up, not just that the logging level is set). For this reason, any ``echo=True`` flags will result in a call to ``logging.basicConfig()`` using sys.stdout as the destination. It also sets up a default format using the level name, timestamp, and logger name. Note that this configuration has the affect of being configured **in addition** to any existing logger configurations. Therefore, **when using Python logging, ensure all echo flags are set to False at all times**, to avoid getting duplicate log lines.
+121
View File
@@ -0,0 +1,121 @@
.. _examples_toplevel:
Examples
========
The SQLAlchemy distribution includes a variety of code examples illustrating a select set of patterns, some typical and some not so typical. All are runnable and can be found in the ``/examples`` directory of the distribution. Each example contains a README in its ``__init__.py`` file, each of which are listed below.
Additional SQLAlchemy examples, some user contributed, are available on the wiki at `<http://www.sqlalchemy.org/trac/wiki/UsageRecipes>`_.
Adjacency List
--------------
Location: /examples/adjacency_list/
.. automodule:: adjacency_list
Associations
------------
Location: /examples/association/
.. automodule:: association
Attribute Instrumentation
-------------------------
Location: /examples/custom_attributes/
.. automodule:: custom_attributes
Beaker Caching
--------------
Location: /examples/beaker_caching/
.. automodule:: beaker_caching
Derived Attributes
------------------
Location: /examples/derived_attributes/
.. automodule:: derived_attributes
Directed Graphs
---------------
Location: /examples/graphs/
.. automodule:: graphs
Dynamic Relations as Dictionaries
----------------------------------
Location: /examples/dynamic_dict/
.. automodule:: dynamic_dict
Horizontal Sharding
-------------------
Location: /examples/sharding
.. automodule:: sharding
Inheritance Mappings
--------------------
Location: /examples/inheritance/
.. automodule:: inheritance
Large Collections
-----------------
Location: /examples/large_collection/
.. automodule:: large_collection
Nested Sets
-----------
Location: /examples/nested_sets/
.. automodule:: nested_sets
Polymorphic Associations
------------------------
Location: /examples/poly_assoc/
.. automodule:: poly_assoc
PostGIS Integration
-------------------
Location: /examples/postgis
.. automodule:: postgis
Versioned Objects
-----------------
Location: /examples/versioning
.. automodule:: versioning
Vertical Attribute Mapping
--------------------------
Location: /examples/vertical
.. automodule:: vertical
XML Persistence
---------------
Location: /examples/elementtree/
.. automodule:: elementtree
-111
View File
@@ -1,111 +0,0 @@
from toc import TOCElement
import docstring
import re
from sqlalchemy import schema, types, engine, sql, pool, orm, exceptions, databases, interfaces
from sqlalchemy.sql import compiler, expression
from sqlalchemy.engine import default, strategies, threadlocal, url
import sqlalchemy.orm.shard
import sqlalchemy.ext.sessioncontext as sessioncontext
import sqlalchemy.ext.selectresults as selectresults
import sqlalchemy.ext.orderinglist as orderinglist
import sqlalchemy.ext.associationproxy as associationproxy
import sqlalchemy.ext.assignmapper as assignmapper
import sqlalchemy.ext.sqlsoup as sqlsoup
def make_doc(obj, classes=None, functions=None, **kwargs):
"""generate a docstring.ObjectDoc structure for an individual module, list of classes, and list of functions."""
obj = docstring.ObjectDoc(obj, classes=classes, functions=functions, **kwargs)
return (obj.name, obj)
def make_all_docs():
"""generate a docstring.AbstractDoc structure."""
print "generating docstrings"
objects = [
make_doc(obj=engine),
make_doc(obj=default),
make_doc(obj=strategies),
make_doc(obj=threadlocal),
make_doc(obj=url),
make_doc(obj=exceptions),
make_doc(obj=interfaces),
make_doc(obj=pool),
make_doc(obj=schema),
#make_doc(obj=sql,include_all_classes=True),
make_doc(obj=compiler),
make_doc(obj=expression,include_all_classes=True),
make_doc(obj=types),
make_doc(obj=orm),
make_doc(obj=orm.collections, classes=[orm.collections.collection,
orm.collections.MappedCollection,
orm.collections.CollectionAdapter]),
make_doc(obj=orm.interfaces),
make_doc(obj=orm.mapperlib, classes=[orm.mapperlib.Mapper]),
make_doc(obj=orm.properties),
make_doc(obj=orm.query, classes=[orm.query.Query]),
make_doc(obj=orm.session, classes=[orm.session.Session, orm.session.SessionExtension]),
make_doc(obj=orm.shard),
make_doc(obj=associationproxy, classes=[associationproxy.AssociationProxy]),
make_doc(obj=orderinglist, classes=[orderinglist.OrderingList]),
make_doc(obj=sqlsoup),
] + [make_doc(getattr(__import__('sqlalchemy.databases.%s' % m).databases, m)) for m in databases.__all__]
return objects
def create_docstring_toc(data, root):
"""given a docstring.AbstractDoc structure, create new TOCElement nodes corresponding
to the elements and cross-reference them back to the doc structure."""
root = TOCElement("docstrings", name="docstrings", description="API Documentation", parent=root, requires_paged=True)
files = []
def create_obj_toc(obj, toc):
if obj.isclass:
s = []
for elem in obj.inherits:
if isinstance(elem, docstring.ObjectDoc):
s.append(elem.name)
else:
s.append(str(elem))
description = "class " + obj.classname + "(%s)" % (','.join(s))
filename = toc.filename
else:
description = obj.description
filename = re.sub(r'\W', '_', obj.name)
toc = TOCElement(filename, obj.name, description, parent=toc, requires_paged=True)
obj.toc_path = toc.path
if not obj.isclass:
create_module_file(obj, toc)
files.append(filename)
if not obj.isclass and obj.functions:
functoc = TOCElement(toc.filename, name="modfunc", description="Module Functions", parent=toc)
obj.mod_path = functoc.path
for func in obj.functions:
t = TOCElement(toc.filename, name=func.name, description=func.name + "()", parent=functoc)
func.toc_path = t.path
#elif obj.functions:
# for func in obj.functions:
# t = TOCElement(toc.filename, name=func.name, description=func.name, parent=toc)
# func.toc_path = t.path
if obj.classes:
for class_ in obj.classes:
create_obj_toc(class_, toc)
for key, obj in data:
create_obj_toc(obj, root)
return files
def create_module_file(obj, toc):
outname = 'output/%s.html' % toc.filename
print "->", outname
header = """# -*- coding: utf-8 -*-
<%%inherit file="module.html"/>
<%%def name="title()">%s - %s</%%def>
## This file is generated. Edit the .txt files instead of this one.
<%%!
filename = '%s'
docstring = '%s'
%%>
""" % (toc.root.doctitle, obj.description, toc.filename, obj.name)
file(outname, 'w').write(header)
return outname
-106
View File
@@ -1,106 +0,0 @@
#!/usr/bin/env python
import sys,re,os,shutil
from os import path
import cPickle as pickle
sys.path = ['../../lib', './lib'] + sys.path
import sqlalchemy
import gen_docstrings, read_markdown, toc
from mako.lookup import TemplateLookup
from mako import exceptions, runtime
import time
import optparse
files = [
'index',
'documentation',
'intro',
'ormtutorial',
'sqlexpression',
'mappers',
'session',
'dbengine',
'metadata',
'types',
'pooling',
'plugins',
'docstrings',
]
post_files = [
'copyright'
]
v = open(path.join(path.dirname(__file__), '..', '..', 'VERSION'))
VERSION = v.readline().strip()
v.close()
parser = optparse.OptionParser(usage = "usage: %prog [options] [tests...]")
parser.add_option("--file", action="store", dest="file", help="only generate file <file>")
parser.add_option("--docstrings", action="store_true", dest="docstrings", help="only generate docstrings")
parser.add_option("--version", action="store", dest="version", default=VERSION, help="version string")
(options, args) = parser.parse_args()
if options.file:
to_gen = [options.file]
else:
to_gen = files + post_files
title='SQLAlchemy 0.4 Documentation'
version = options.version
root = toc.TOCElement('', 'root', '', version=version, doctitle=title)
shutil.copy('./content/index.html', './output/index.html')
shutil.copy('./content/docstrings.html', './output/docstrings.html')
shutil.copy('./content/documentation.html', './output/documentation.html')
if not options.docstrings:
read_markdown.parse_markdown_files(root, [f for f in files if f in to_gen])
if not options.file or options.docstrings:
docstrings = gen_docstrings.make_all_docs()
doc_files = gen_docstrings.create_docstring_toc(docstrings, root)
pickle.dump(docstrings, file('./output/compiled_docstrings.pickle', 'w'))
if not options.docstrings:
read_markdown.parse_markdown_files(root, [f for f in post_files if f in to_gen])
if not options.file or options.docstrings:
pickle.dump(root, file('./output/table_of_contents.pickle', 'w'))
template_dirs = ['./templates', './output']
output = os.path.dirname(os.getcwd())
lookup = TemplateLookup(template_dirs, output_encoding='utf-8', module_directory='./modules')
def genfile(name, outname):
infile = name + ".html"
outfile = file(outname, 'w')
print infile, '->', outname
t = lookup.get_template(infile)
outfile.write(t.render(attributes={}))
if not options.docstrings:
for filename in to_gen:
try:
genfile(filename, os.path.join(os.getcwd(), '../', filename + ".html"))
except:
print exceptions.text_error_template().render()
if not options.file or options.docstrings:
for filename in doc_files:
try:
genfile(filename, os.path.join(os.getcwd(), '../', os.path.basename(filename) + ".html"))
except:
print exceptions.text_error_template().render()
+20
View File
@@ -0,0 +1,20 @@
Table of Contents
=================
.. toctree::
intro
ormtutorial
sqlexpression
mappers
session
dbengine
metadata
examples
reference/index
Indices and tables
------------------
* :ref:`genindex`
* :ref:`search`
+79
View File
@@ -0,0 +1,79 @@
.. _overview_toplevel:
=======================
Overview / Installation
=======================
Overview
========
The SQLAlchemy SQL Toolkit and Object Relational Mapper is a comprehensive set of tools for working with databases and Python. It has several distinct areas of functionality which can be used individually or combined together. Its major components are illustrated below. The arrows represent the general dependencies of components:
.. image:: sqla_arch_small.jpg
Above, the two most significant front-facing portions of SQLAlchemy are the **Object Relational Mapper** and the **SQL Expression Language**. SQL Expressions can be used independently of the ORM. When using the ORM, the SQL Expression language remains part of the public facing API as it is used within object-relational configurations and queries.
Tutorials
=========
* :ref:`ormtutorial_toplevel` - This describes the richest feature of SQLAlchemy, its object relational mapper. If you want to work with higher-level SQL which is constructed automatically for you, as well as management of Python objects, proceed to this tutorial.
* :ref:`sqlexpression_toplevel` - The core of SQLAlchemy is its SQL expression language. The SQL Expression Language is a toolkit all its own, independent of the ORM package, which can be used to construct manipulable SQL expressions which can be programmatically constructed, modified, and executed, returning cursor-like result sets. It's a lot more lightweight than the ORM and is appropriate for higher scaling SQL operations. It's also heavily present within the ORM's public facing API, so advanced ORM users will want to master this language as well.
Main Documentation
==================
* :ref:`datamapping_toplevel` - A comprehensive walkthrough of major ORM patterns and techniques.
* :ref:`session_toplevel` - A detailed description of SQLAlchemy's Session object
* :ref:`engines_toplevel` - Describes SQLAlchemy's database-connection facilities, including connection documentation and working with connections and transactions.
* :ref:`metadata_toplevel` - All about schema management using ``MetaData`` and ``Table`` objects; reading database schemas into your application, creating and dropping tables, constraints, defaults, sequences, indexes.
* :ref:`pooling_toplevel` - Further detail about SQLAlchemy's connection pool library.
* :ref:`types` - Datatypes included with SQLAlchemy, their functions, as well as how to create your own types.
* :ref:`plugins` - Included addons for SQLAlchemy
API Reference
=============
An organized section of all SQLAlchemy APIs is at :ref:`api_reference_toplevel`.
Installing SQLAlchemy
======================
Installing SQLAlchemy from scratch is most easily achieved with `setuptools <http://pypi.python.org/pypi/setuptools/>`_. Assuming it's installed, just run this from the command-line:
.. sourcecode:: none
# easy_install SQLAlchemy
This command will download the latest version of SQLAlchemy from the `Python Cheese Shop <http://pypi.python.org/pypi/SQLAlchemy>`_ and install it to your system.
* `setuptools <http://peak.telecommunity.com/DevCenter/setuptools>`_
* `install setuptools <http://peak.telecommunity.com/DevCenter/EasyInstall#installation-instructions>`_
* `pypi <http://pypi.python.org/pypi/SQLAlchemy>`_
Otherwise, you can install from the distribution using the ``setup.py`` script:
.. sourcecode:: none
# python setup.py install
Installing a Database API
==========================
SQLAlchemy is designed to operate with a `DB-API <http://www.python.org/doc/peps/pep-0249/>`_ implementation built for a particular database, and includes support for the most popular databases. The current list is at :ref:`supported_dbapis`.
Checking the Installed SQLAlchemy Version
=========================================
This documentation covers SQLAlchemy version 0.6. If you're working on a system that already has SQLAlchemy installed, check the version from your Python prompt like this:
.. sourcecode:: python+sql
>>> import sqlalchemy
>>> sqlalchemy.__version__ # doctest: +SKIP
0.6.0
0.5 to 0.6 Migration
=====================
Notes on what's changed from 0.5 to 0.6 is available on the SQLAlchemy wiki at `06Migration <http://www.sqlalchemy.org/trac/wiki/06Migration>`_.
-181
View File
@@ -1,181 +0,0 @@
"""
defines a pickleable, recursive "generated python documentation" datastructure.
"""
import re, types, string, inspect
allobjects = {}
class AbstractDoc(object):
def __init__(self, obj):
allobjects[id(obj)] = self
self.id = id(obj)
self.allobjects = allobjects
self.toc_path = None
class ObjectDoc(AbstractDoc):
def __init__(self, obj, functions=None, classes=None, include_all_classes=False):
super(ObjectDoc, self).__init__(obj)
self.isclass = isinstance(obj, types.ClassType) or isinstance(obj, types.TypeType)
self.name= obj.__name__
self.include_all_classes = include_all_classes
functions = functions
classes= classes
if not self.isclass:
if not include_all_classes and hasattr(obj, '__all__'):
objects = obj.__all__
sort = True
else:
objects = obj.__dict__.keys()
sort = True
if functions is None:
functions = [getattr(obj, x, None)
for x in objects
if getattr(obj,x,None) is not None and
(isinstance(getattr(obj,x), types.FunctionType))
and not self._is_private_name(getattr(obj,x).__name__)
]
if sort:
functions.sort(lambda a, b: cmp(a.__name__, b.__name__))
if classes is None:
classes = [getattr(obj, x, None) for x in objects
if getattr(obj,x,None) is not None and
(isinstance(getattr(obj,x), types.TypeType)
or isinstance(getattr(obj,x), types.ClassType))
and (self.include_all_classes or not self._is_private_name(getattr(obj,x).__name__))
]
classes = list(set(classes))
if sort:
classes.sort(lambda a, b: cmp(a.__name__.replace('_', ''), b.__name__.replace('_', '')))
else:
if functions is None:
functions = (
[getattr(obj, x).im_func for x in obj.__dict__.keys() if isinstance(getattr(obj,x), types.MethodType)
and
(getattr(obj, x).__name__ == '__init__' or not self._is_private_name(getattr(obj,x).__name__))
] +
[(x, getattr(obj, x)) for x in obj.__dict__.keys() if _is_property(getattr(obj,x))
and
not self._is_private_name(x)
]
)
functions.sort(_method_sort)
if classes is None:
classes = []
if self.isclass:
self.description = "class " + self.name
self.classname = self.name
if hasattr(obj, '__mro__'):
l = []
mro = list(obj.__mro__[1:])
mro.reverse()
for x in mro:
for y in x.__mro__[1:]:
if y in l:
del l[l.index(y)]
l.insert(0, x)
self.description += "(" + string.join([x.__name__ for x in l], ',') + ")"
self._inherits = [(id(x), x.__name__) for x in l]
else:
self._inherits = []
else:
self.description = "module " + self.name
self.doc = obj.__doc__
self.functions = []
if not self.isclass:
for func in functions:
self.functions.append(FunctionDoc(func))
else:
for func in functions:
if isinstance(func, types.FunctionType):
self.functions.append(MethodDoc(func, self))
elif isinstance(func, tuple):
self.functions.append(PropertyDoc(func[0], func[1]))
self.classes = []
for class_ in classes:
self.classes.append(ObjectDoc(class_))
def _is_private_name(self, name):
if name in ('__weakref__', '__repr__','__str__', '__unicode__',
'__getstate__', '__setstate__', '__reduce__',
'__reduce_ex__', '__hash__'):
return True
elif re.match(r'^__.*__$', name):
return False
elif name.startswith('_'):
return True
else:
return False
def _get_inherits(self):
for item in self._inherits:
if item[0] in self.allobjects:
yield self.allobjects[item[0]]
else:
yield item[1]
inherits = property(_get_inherits)
def accept_visitor(self, visitor):
visitor.visit_object(self)
def _is_property(elem):
return isinstance(elem, property) or (hasattr(elem, '__get__') and hasattr(elem, '__set__'))
class FunctionDoc(AbstractDoc):
def __init__(self, func):
super(FunctionDoc, self).__init__(func)
argspec = inspect.getargspec(func)
argnames = argspec[0]
varargs = argspec[1]
varkw = argspec[2]
defaults = argspec[3] or ()
argstrings = []
for i in range(0, len(argnames)):
if i >= len(argnames) - len(defaults):
argstrings.append("%s=%s" % (argnames[i], repr(defaults[i - (len(argnames) - len(defaults))])))
else:
argstrings.append(argnames[i])
if varargs is not None:
argstrings.append("*%s" % varargs)
if varkw is not None:
argstrings.append("**%s" % varkw)
self.argstrings = self.arglist = argstrings
self.name = func.__name__
self.link = func.__name__
self.doc = func.__doc__
def accept_visitor(self, visitor):
visitor.visit_function(self)
class MethodDoc(FunctionDoc):
def __init__(self, func, owner):
super(MethodDoc, self).__init__(func)
if self.name == '__init__' and not self.doc:
self.doc = "Construct a new ``%s``." % owner.name
class PropertyDoc(AbstractDoc):
def __init__(self, name, prop):
super(PropertyDoc, self).__init__(prop)
self.doc = prop.__doc__
self.name = name
self.link = name
def accept_visitor(self, visitor):
visitor.visit_property(self)
def _method_sort(fna, fnb):
a = getattr(fna, '__name__', None) or fna[0]
b = getattr(fnb, '__name__', None) or fnb[0]
if a == '__init__': return -1
if b == '__init__': return 1
a_u = a.startswith('__') and a.endswith('__')
b_u = b.startswith('__') and b.endswith('__')
if a_u and not b_u: return 1
if b_u and not a_u: return -1
return cmp(a, b)
-389
View File
@@ -1,389 +0,0 @@
# $Id$
# highlight.py - syntax highlighting functions for Myghty
# Copyright (C) 2004 Michael Bayer mike_mp@zzzcomputing.com
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
import re, StringIO, sys, string, os
import token, tokenize, keyword
# Highlighter - highlights Myghty and Python source code
__all__ = ['highlight', 'PythonHighlighter', 'MyghtyHighlighter']
pystyles = {
token.ENDMARKER : 'python_operator' ,
token.NAME : 'python_name' ,
token.NUMBER : 'python_number' ,
token.STRING : 'python_literal' ,
token.NEWLINE : 'python_operator' ,
token.INDENT : 'python_operator' ,
token.DEDENT : 'python_operator' ,
token.LPAR : 'python_enclosure' ,
token.RPAR : 'python_enclosure' ,
token.LSQB : 'python_enclosure' ,
token.RSQB : 'python_enclosure' ,
token.COLON : 'python_operator' ,
token.COMMA : 'python_operator' ,
token.SEMI : 'python_operator' ,
token.PLUS : 'python_operator' ,
token.MINUS : 'python_operator' ,
token.STAR : 'python_operator' ,
token.SLASH : 'python_operator' ,
token.VBAR : 'python_operator' ,
token.AMPER : 'python_operator' ,
token.LESS : 'python_operator' ,
token.GREATER : 'python_operator' ,
token.EQUAL : 'python_operator' ,
token.DOT : 'python_operator' ,
token.PERCENT : 'python_operator' ,
token.BACKQUOTE : 'python_operator' ,
token.LBRACE : 'python_enclosure',
token.RBRACE : 'python_enclosure' ,
token.EQEQUAL : 'python_operator' ,
token.NOTEQUAL : 'python_operator' ,
token.LESSEQUAL : 'python_operator' ,
token.GREATEREQUAL : 'python_operator' ,
token.TILDE : 'python_operator' ,
token.CIRCUMFLEX : 'python_operator' ,
token.LEFTSHIFT : 'python_operator' ,
token.RIGHTSHIFT : 'python_operator' ,
token.DOUBLESTAR : 'python_operator' ,
token.PLUSEQUAL : 'python_operator' ,
token.MINEQUAL : 'python_operator' ,
token.STAREQUAL : 'python_operator' ,
token.SLASHEQUAL : 'python_operator' ,
token.PERCENTEQUAL : 'python_operator' ,
token.AMPEREQUAL : 'python_operator' ,
token.VBAREQUAL : 'python_operator' ,
token.CIRCUMFLEXEQUAL : 'python_operator' ,
token.LEFTSHIFTEQUAL : 'python_operator' ,
token.RIGHTSHIFTEQUAL : 'python_operator' ,
token.DOUBLESTAREQUAL : 'python_operator' ,
token.DOUBLESLASH : 'python_operator' ,
token.DOUBLESLASHEQUAL : 'python_operator' ,
token.OP : 'python_operator' ,
token.ERRORTOKEN : 'python_operator' ,
token.N_TOKENS : 'python_operator' ,
token.NT_OFFSET : 'python_operator' ,
tokenize.COMMENT: 'python_comment',
}
html_escapes = {
'&' : '&amp;',
'>' : '&gt;',
'<' : '&lt;',
'"' : '&quot;'
}
def do_html_escape(string):
#return "@" + re.sub(r"([&<>])", lambda m: html_escapes[m.group()], string) + "+"
return re.sub(r"([&<>])", lambda m: html_escapes[m.group()], string)
def highlight(source, filename = None, syntaxtype = None, html_escape = True):
if syntaxtype is not None:
highlighter = highlighters.get(syntaxtype, None)
elif filename is not None:
(root, filename) = os.path.split(filename)
highlighter = highlighters.get(filename, None)
if highlighter is None:
(root, ext) = os.path.splitext(filename)
highlighter = highlighters.get(ext, None)
else:
highlighter = None
if highlighter is None:
if html_escape:
return do_html_escape(source)
else:
return source
else:
return highlighter(source, html_escape = html_escape).highlight()
class Highlighter:
def __init__(self, source, output = None, html_escape = True):
self.source = source
self.pos = 0
self.html_escape = html_escape
if output is None:
self.output = StringIO.StringIO()
else:
self.output = output
def content(self):
return self.output.getvalue()
def highlight(self):raise NotImplementedError()
def colorize(self, tokens):
for pair in tokens:
if pair[1] is None:
if self.html_escape:
self.output.write(do_html_escape(pair[0]))
else:
self.output.write(pair[0])
else:
if self.html_escape:
self.output.write('<span class="%s">%s</span>' % (pair[1], do_html_escape(pair[0])))
else:
self.output.write('<span class="%s">%s</span>' % (pair[1], pair[0]))
class PythonHighlighter(Highlighter):
def _line_grid(self, str, start, end):
lines = re.findall(re.compile(r'[^\n]*\n?', re.S), str)
r = 0
for l in lines[0 : end[0] - start[0]]:
r += len(l)
r += end[1]
return (start, (start[0], r))
def highlight(self):
buf = StringIO.StringIO(self.source)
# tokenize module not too good at getting the
# whitespace at the end of a python block
trailingspace = re.search(r"\n([ \t]+$)", self.source, re.S)
if trailingspace:
trailingspace = trailingspace.group(1)
curl = -1
tokens = []
curstyle = None
line = None
for t in tokenize.generate_tokens(lambda: buf.readline()):
if t[2][0] != curl:
curl = t[2][0]
curc = 0
line = t[4]
# pick up whitespace and output
if t[2][1] > curc:
tokens.append(line[curc : t[2][1]])
curc = t[2][1]
if self.get_style(t[0], t[1]) != curstyle:
if tokens:
self.colorize([(string.join(tokens, ''), curstyle)])
tokens = []
curstyle = self.get_style(t[0], t[1])
(start, end) = self._line_grid(line, t[2], t[3])
text = line[start[1]:end[1]]
# special hardcoded rule to allow "interactive" demos without
# >>> getting sucked in as >> , > operators
if text == '">>>"':
text = '>>>'
tokens.append(text)
curc = t[3][1]
curl = t[3][0]
# any remaining content to output, output it
if tokens:
self.colorize([(string.join(tokens, ''), curstyle)])
if trailingspace:
self.output.write(trailingspace)
return self.content()
def get_style(self, tokenid, str):
if tokenid == token.NAME:
if keyword.iskeyword(str):
return "python_keyword"
else:
return "python_name"
elif tokenid == token.OP:
if "()[]{}".find(str) != -1:
return "python_enclosure"
else:
return "python_operator"
else:
return pystyles.get(tokenid, None)
class MyghtyHighlighter(Highlighter):
def _match(self, regexp):
match = regexp.match(self.source, self.pos)
if match:
(start, end) = match.span()
self.output.write(self.source[self.pos:start])
if start == end:
self.pos = end + 1
else:
self.pos = end
return match
else:
return None
def highlight(self):
while (self.pos < len(self.source)):
if self.match_named_block():
continue
if self.match_block():
continue
if self.match_comp_call():
continue
if self.match_comp_content_call():
continue
if self.match_substitution():
continue
if self.match_line():
continue
if self.match_text():
continue;
break
return self.content()
def pythonize(self, text):
py = PythonHighlighter(text, output = self.output)
py.highlight()
def match_text(self):
textmatch = re.compile(r"""
(.*?) # anything, followed by:
(
(?<=\n)(?=[%#]) # an eval or comment line
|
(?=</?[%&]) # a substitution or block or call start or end
# - don't consume
|
(\\\n) # an escaped newline
|
\Z # end of string
)""", re.X | re.S)
match = self._match(textmatch)
if match:
self.colorize([(match.group(1), 'text')])
if match.group(3):
self.colorize([(match.group(3), 'python_operator')])
return True
else:
return False
def match_named_block(self):
namedmatch = re.compile(r"(<%(def|method))(.*?)(>)(.*?)(</%\2>)", re.M | re.S)
match = self._match(namedmatch)
if match:
self.colorize([(match.group(1), 'deftag')])
self.colorize([(match.group(3), 'compname')])
self.colorize([(match.group(4), 'deftag')])
MyghtyHighlighter(match.group(5), self.output).highlight()
self.colorize([(match.group(6), 'deftag')])
return True
else:
return False
def match_block(self):
blockmatch = re.compile(r"(<%(\w+).*?>)(.*?)(</%\2\s*>)", re.M | re.S)
match = self._match(blockmatch)
if match:
style = {
'doc': 'doctag',
'args': 'argstag',
}.setdefault(match.group(2), "blocktag")
self.colorize([(match.group(1), style)])
if style == 'doctag':
self.colorize([(match.group(3), 'doctag_text')])
else:
self.pythonize(match.group(3))
self.colorize([(match.group(4), style)])
return True
else:
return False
def match_comp_call(self):
compmatch = re.compile(r"(<&[^|])(.*?)(,.*?)?(&>)", re.M)
match = self._match(compmatch)
if match:
self.colorize([(match.group(1), 'compcall')])
self.colorize([(match.group(2), 'compname')])
if match.group(3) is not None:
self.pythonize(match.group(3))
self.colorize([(match.group(4), 'compcall')])
return True
else:
return False
def match_substitution(self):
submatch = re.compile(r"(<%)(.*?)(%>)", re.M)
match = self._match(submatch)
if match:
self.colorize([(match.group(1), 'substitution')])
self.pythonize(match.group(2))
self.colorize([(match.group(3), 'substitution')])
return True
else:
return False
def match_comp_content_call(self):
compcontmatch = re.compile(r"(<&\|)(.*?)(,.*?)?(&>)|(</&>)", re.M | re.S)
match = self._match(compcontmatch)
if match:
if match.group(5) is not None:
self.colorize([(match.group(5), 'compcall')])
else:
self.colorize([(match.group(1), 'compcall')])
self.colorize([(match.group(2), 'compname')])
if match.group(3) is not None:
self.pythonize(match.group(3))
self.colorize([(match.group(4), 'compcall')])
return True
else:
return False
def match_line(self):
linematch = re.compile(r"(?<=^)([%#])([^\n]*)(\n|\Z)", re.M)
match = self._match(linematch)
if match:
if match.group(1) == '#':
self.colorize([(match.group(0), 'doctag')])
else:
#self.colorize([(match.group(0), 'doctag')])
self.colorize([(match.group(1), 'controlline')])
self.pythonize(match.group(2))
self.output.write(match.group(3))
return True
else:
return False
highlighters = {
'.myt': MyghtyHighlighter,
'.myc': MyghtyHighlighter,
'autohandler' : MyghtyHighlighter,
'dhandler': MyghtyHighlighter,
'.py': PythonHighlighter,
'myghty': MyghtyHighlighter,
'python' : PythonHighlighter
}
-1671
View File
File diff suppressed because it is too large Load Diff
-83
View File
@@ -1,83 +0,0 @@
"""
defines a pickleable, recursive "table of contents" datastructure.
TOCElements define a name, a description, and also a uniquely-identifying "path" which is
used to generate hyperlinks between document sections.
"""
import time, re
toc_by_file = {}
toc_by_path = {}
filenames = []
class TOCElement(object):
def __init__(self, filename, name, description, parent=None, version=None, last_updated=None, doctitle=None, requires_paged=False, **kwargs):
self.filename = filename
self.name = re.sub(r'[<>&;%]', '', name)
self.description = description
self.parent = parent
self.content = None
self.filenames = filenames
self.toc_by_path = toc_by_path
self.toc_by_file = toc_by_file
self.last_updated = time.time()
self.version = version
self.doctitle = doctitle
self.requires_paged = requires_paged
(self.path, self.depth) = self._create_path()
#print "NEW TOC:", self.path
for key, value in kwargs.iteritems():
setattr(self, key, value)
toc_by_path[self.path] = self
self.is_top = (self.parent is not None and self.parent.filename != self.filename) or self.parent is None
if self.is_top:
toc_by_file[self.filename] = self
if self.filename:
filenames.append(self.filename)
self.root = self.parent and self.parent.root or self
self.content = None
self.previous = None
self.next = None
self.children = []
if parent:
if parent.children:
self.previous = parent.children[-1]
parent.children[-1].next = self
parent.children.append(self)
if parent is not parent.root:
self.up = parent
else:
self.up = None
def get_page_root(self):
return self.toc_by_file[self.filename]
def get_by_path(self, path):
return self.toc_by_path.get(path)
def get_by_file(self, filename):
return self.toc_by_file[filename]
def get_link(self, extension='html', anchor=True, usefilename=True):
if usefilename or self.requires_paged:
if anchor:
return "%s.%s#%s" % (self.filename, extension, self.path)
else:
return "%s.%s" % (self.filename, extension)
else:
return "#%s" % (self.path)
def _create_path(self):
elem = self
tokens = []
depth = 0
while elem.parent is not None:
tokens.insert(0, elem.name)
elem = elem.parent
depth +=1
return ('_'.join(tokens), depth)
+1794
View File
File diff suppressed because it is too large Load Diff
+844
View File
@@ -0,0 +1,844 @@
.. _metadata_toplevel:
==================
Database Meta Data
==================
Describing Databases with MetaData
==================================
The core of SQLAlchemy's query and object mapping operations are supported by *database metadata*, which is comprised of Python objects that describe tables and other schema-level objects. These objects are at the core of three major types of operations - issuing CREATE and DROP statements (known as *DDL*), constructing SQL queries, and expressing information about structures that already exist within the database.
Database metadata can be expressed by explicitly naming the various components and their properties, using constructs such as ``Table``, ``Column``, ``ForeignKey`` and ``Sequence``, all of which are imported from the ``sqlalchemy.schema`` package. It can also be generated by SQLAlchemy using a process called *reflection*, which means you start with a single object such as ``Table``, assign it a name, and then instruct SQLAlchemy to load all the additional information related to that name from a particular engine source.
A key feature of SQLAlchemy's database metadata constructs is that they are designed to be used in a *declarative* style which closely resembles that of real DDL. They are therefore most intuitive to those who have some background in creating real schema generation scripts.
A collection of metadata entities is stored in an object aptly named ``MetaData``::
from sqlalchemy import *
metadata = MetaData()
``MetaData`` is a container object that keeps together many different features of a database (or multiple databases) being described.
To represent a table, use the ``Table`` class. Its two primary arguments are the table name, then the ``MetaData`` object which it will be associated with. The remaining positional arguments are mostly ``Column`` objects describing each column::
user = Table('user', metadata,
Column('user_id', Integer, primary_key = True),
Column('user_name', String(16), nullable = False),
Column('email_address', String(60)),
Column('password', String(20), nullable = False)
)
Above, a table called ``user`` is described, which contains four columns. The primary key of the table consists of the ``user_id`` column. Multiple columns may be assigned the ``primary_key=True`` flag which denotes a multi-column primary key, known as a *composite* primary key.
Note also that each column describes its datatype using objects corresponding to genericized types, such as ``Integer`` and ``String``. SQLAlchemy features dozens of types of varying levels of specificity as well as the ability to create custom types. Documentation on the type system can be found at :ref:`types`.
Accessing Tables and Columns
----------------------------
The ``MetaData`` object contains all of the schema constructs we've associated with it. It supports a few methods of accessing these table objects, such as the ``sorted_tables`` accessor which returns a list of each ``Table`` object in order of foreign key dependency (that is, each table is preceded by all tables which it references)::
>>> for t in metadata.sorted_tables:
... print t.name
user
user_preference
invoice
invoice_item
In most cases, individual ``Table`` objects have been explicitly declared, and these objects are typically accessed directly as module-level variables in an application.
Once a ``Table`` has been defined, it has a full set of accessors which allow inspection of its properties. Given the following ``Table`` definition::
employees = Table('employees', metadata,
Column('employee_id', Integer, primary_key=True),
Column('employee_name', String(60), nullable=False),
Column('employee_dept', Integer, ForeignKey("departments.department_id"))
)
Note the ``ForeignKey`` object used in this table - this construct defines a reference to a remote table, and is fully described in :ref:`metadata_foreignkeys`. Methods of accessing information about this table include::
# access the column "EMPLOYEE_ID":
employees.columns.employee_id
# or just
employees.c.employee_id
# via string
employees.c['employee_id']
# iterate through all columns
for c in employees.c:
print c
# get the table's primary key columns
for primary_key in employees.primary_key:
print primary_key
# get the table's foreign key objects:
for fkey in employees.foreign_keys:
print fkey
# access the table's MetaData:
employees.metadata
# access the table's bound Engine or Connection, if its MetaData is bound:
employees.bind
# access a column's name, type, nullable, primary key, foreign key
employees.c.employee_id.name
employees.c.employee_id.type
employees.c.employee_id.nullable
employees.c.employee_id.primary_key
employees.c.employee_dept.foreign_key
# get the "key" of a column, which defaults to its name, but can
# be any user-defined string:
employees.c.name.key
# access a column's table:
employees.c.employee_id.table is employees
# get the table related by a foreign key
fcolumn = employees.c.employee_dept.foreign_key.column.table
.. _metadata_binding:
Creating and Dropping Database Tables
-------------------------------------
Once you've defined some ``Table`` objects, assuming you're working with a brand new database one thing you might want to do is issue CREATE statements for those tables and their related constructs (as an aside, it's also quite possible that you *don't* want to do this, if you already have some preferred methodology such as tools included with your database or an existing scripting system - if that's the case, feel free to skip this section - SQLAlchemy has no requirement that it be used to create your tables).
The usual way to issue CREATE is to use ``create_all()`` on the ``MetaData`` object. This method will issue queries that first check for the existence of each individual table, and if not found will issue the CREATE statements:
.. sourcecode:: python+sql
engine = create_engine('sqlite:///:memory:')
metadata = MetaData()
user = Table('user', metadata,
Column('user_id', Integer, primary_key = True),
Column('user_name', String(16), nullable = False),
Column('email_address', String(60), key='email'),
Column('password', String(20), nullable = False)
)
user_prefs = Table('user_prefs', metadata,
Column('pref_id', Integer, primary_key=True),
Column('user_id', Integer, ForeignKey("user.user_id"), nullable=False),
Column('pref_name', String(40), nullable=False),
Column('pref_value', String(100))
)
{sql}metadata.create_all(engine)
PRAGMA table_info(user){}
CREATE TABLE user(
user_id INTEGER NOT NULL PRIMARY KEY,
user_name VARCHAR(16) NOT NULL,
email_address VARCHAR(60),
password VARCHAR(20) NOT NULL
)
PRAGMA table_info(user_prefs){}
CREATE TABLE user_prefs(
pref_id INTEGER NOT NULL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES user(user_id),
pref_name VARCHAR(40) NOT NULL,
pref_value VARCHAR(100)
)
``create_all()`` creates foreign key constraints between tables usually inline with the table definition itself, and for this reason it also generates the tables in order of their dependency. There are options to change this behavior such that ``ALTER TABLE`` is used instead.
Dropping all tables is similarly achieved using the ``drop_all()`` method. This method does the exact opposite of ``create_all()`` - the presence of each table is checked first, and tables are dropped in reverse order of dependency.
Creating and dropping individual tables can be done via the ``create()`` and ``drop()`` methods of ``Table``. These methods by default issue the CREATE or DROP regardless of the table being present:
.. sourcecode:: python+sql
engine = create_engine('sqlite:///:memory:')
meta = MetaData()
employees = Table('employees', meta,
Column('employee_id', Integer, primary_key=True),
Column('employee_name', String(60), nullable=False, key='name'),
Column('employee_dept', Integer, ForeignKey("departments.department_id"))
)
{sql}employees.create(engine)
CREATE TABLE employees(
employee_id SERIAL NOT NULL PRIMARY KEY,
employee_name VARCHAR(60) NOT NULL,
employee_dept INTEGER REFERENCES departments(department_id)
)
{}
``drop()`` method:
.. sourcecode:: python+sql
{sql}employees.drop(engine)
DROP TABLE employees
{}
To enable the "check first for the table existing" logic, add the ``checkfirst=True`` argument to ``create()`` or ``drop()``::
employees.create(engine, checkfirst=True)
employees.drop(engine, checkfirst=False)
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 ``Engine`` object, or an individual ``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 ``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 ``create_all()`` without needing to pass the ``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. If you find yourself constantly needing to specify the same ``Engine`` object throughout the entire application, consider binding as a convenience feature which is applicable to applications that don't have multiple engines in use and don't have the need to reference connections explicitly. It should also be noted that an application which is focused on using the SQLAlchemy ORM will not be dealing explicitly with ``Engine`` or ``Connection`` objects very much in any case, so it's probably less confusing and more "future proof" to not use the `bind` attribute.
Reflecting Tables
-----------------
A ``Table`` object can be instructed to load information about itself from the corresponding database schema object already existing within the database. This process is called *reflection*. Most simply you need only specify the table name, a ``MetaData`` object, and the ``autoload=True`` flag. If the ``MetaData`` is not persistently bound, also add the ``autoload_with`` argument::
>>> messages = Table('messages', meta, autoload=True, autoload_with=engine)
>>> [c.name for c in messages.columns]
['message_id', 'message_name', 'date']
The above operation will use the given engine to query the database for information about the ``messages`` table, and will then generate ``Column``, ``ForeignKey``, and other objects corresponding to this information as though the ``Table`` object were hand-constructed in Python.
When tables are reflected, if a given table references another one via foreign key, a second ``Table`` object is created within the ``MetaData`` object representing the connection. Below, assume the table ``shopping_cart_items`` references a table named ``shopping_carts``. Reflecting the ``shopping_cart_items`` table has the effect such that the ``shopping_carts`` table will also be loaded::
>>> shopping_cart_items = Table('shopping_cart_items', meta, autoload=True, autoload_with=engine)
>>> 'shopping_carts' in meta.tables:
True
The ``MetaData`` has an interesting "singleton-like" behavior such that if you requested both tables individually, ``MetaData`` will ensure that exactly one ``Table`` object is created for each distinct table name. The ``Table`` constructor actually returns to you the already-existing ``Table`` object if one already exists with the given name. Such as below, we can access the already generated ``shopping_carts`` table just by naming it::
shopping_carts = Table('shopping_carts', meta)
Of course, it's a good idea to use ``autoload=True`` with the above table regardless. This is so that the table's attributes will be loaded if they have not been already. The autoload operation only occurs for the table if it hasn't already been loaded; once loaded, new calls to ``Table`` with the same name will not re-issue any reflection queries.
Overriding Reflected Columns
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Individual columns can be overridden with explicit values when reflecting tables; this is handy for specifying custom datatypes, constraints such as primary keys that may not be configured within the database, etc.::
>>> mytable = Table('mytable', meta,
... Column('id', Integer, primary_key=True), # override reflected 'id' to have primary key
... Column('mydata', Unicode(50)), # override reflected 'mydata' to be Unicode
... autoload=True)
Reflecting Views
~~~~~~~~~~~~~~~~
The reflection system can also reflect views. Basic usage is the same as that of a table::
my_view = Table("some_view", metadata, autoload=True)
Above, ``my_view`` is a ``Table`` object with ``Column`` objects representing the names and types
of each column within the view "some_view".
Usually, it's desired to have at least a primary key constraint when reflecting a view, if not
foreign keys as well. View reflection doesn't extrapolate these constraints.
Use the "override" technique for this, specifying explicitly those columns
which are part of the primary key or have foreign key constraints::
my_view = Table("some_view", metadata,
Column("view_id", Integer, primary_key=True),
Column("related_thing", Integer, ForeignKey("othertable.thing_id")),
autoload=True
)
Reflecting All Tables at Once
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``MetaData`` object can also get a listing of tables and reflect the full set. This is achieved by using the ``reflect()`` method. After calling it, all located tables are present within the ``MetaData`` object's dictionary of tables::
meta = MetaData()
meta.reflect(bind=someengine)
users_table = meta.tables['users']
addresses_table = meta.tables['addresses']
``metadata.reflect()`` also provides a handy way to clear or delete all the rows in a database::
meta = MetaData()
meta.reflect(bind=someengine)
for table in reversed(meta.sorted_tables):
someengine.execute(table.delete())
Specifying the Schema Name
---------------------------
Some databases support the concept of multiple schemas. A ``Table`` can reference this by specifying the ``schema`` keyword argument::
financial_info = Table('financial_info', meta,
Column('id', Integer, primary_key=True),
Column('value', String(100), nullable=False),
schema='remote_banks'
)
Within the ``MetaData`` collection, this table will be identified by the combination of ``financial_info`` and ``remote_banks``. If another table called ``financial_info`` is referenced without the ``remote_banks`` schema, it will refer to a different ``Table``. ``ForeignKey`` objects can specify references to columns in this table using the form ``remote_banks.financial_info.id``.
The ``schema`` argument should be used for any name qualifiers required, including Oracle's "owner" attribute and similar. It also can accommodate a dotted name for longer schemes::
schema="dbo.scott"
Backend-Specific Options
------------------------
``Table`` supports database-specific options. For example, MySQL has different table backend types, including "MyISAM" and "InnoDB". This can be expressed with ``Table`` using ``mysql_engine``::
addresses = Table('engine_email_addresses', meta,
Column('address_id', Integer, primary_key = True),
Column('remote_user_id', Integer, ForeignKey(users.c.user_id)),
Column('email_address', String(20)),
mysql_engine='InnoDB'
)
Other backends may support table-level options as well. See the API documentation for each backend for further details.
Column Insert/Update Defaults
==============================
SQLAlchemy provides a very rich featureset regarding column level events which take place during INSERT and UPDATE statements. Options include:
* Scalar values used as defaults during INSERT and UPDATE operations
* Python functions which execute upon INSERT and UPDATE operations
* SQL expressions which are embedded in INSERT statements (or in some cases execute beforehand)
* SQL expressions which are embedded in UPDATE statements
* Server side default values used during INSERT
* Markers for server-side triggers used during UPDATE
The general rule for all insert/update defaults is that they only take effect if no value for a particular column is passed as an ``execute()`` parameter; otherwise, the given value is used.
Scalar Defaults
---------------
The simplest kind of default is a scalar value used as the default value of a column::
Table("mytable", meta,
Column("somecolumn", Integer, default=12)
)
Above, the value "12" will be bound as the column value during an INSERT if no other value is supplied.
A scalar value may also be associated with an UPDATE statement, though this is not very common (as UPDATE statements are usually looking for dynamic defaults)::
Table("mytable", meta,
Column("somecolumn", Integer, onupdate=25)
)
Python-Executed Functions
-------------------------
The ``default`` and ``onupdate`` keyword arguments also accept Python functions. These functions are invoked at the time of insert or update if no other value for that column is supplied, and the value returned is used for the column's value. Below illustrates a crude "sequence" that assigns an incrementing counter to a primary key column::
# a function which counts upwards
i = 0
def mydefault():
global i
i += 1
return i
t = Table("mytable", meta,
Column('id', Integer, primary_key=True, default=mydefault),
)
It should be noted that for real "incrementing sequence" behavior, the built-in capabilities of the database should normally be used, which may include sequence objects or other autoincrementing capabilities. For primary key columns, SQLAlchemy will in most cases use these capabilities automatically. See the API documentation for ``Column`` including the ``autoincrement`` flag, as well as the section on ``Sequence`` later in this chapter for background on standard primary key generation techniques.
To illustrate onupdate, we assign the Python ``datetime`` function ``now`` to the ``onupdate`` attribute::
import datetime
t = Table("mytable", meta,
Column('id', Integer, primary_key=True),
# define 'last_updated' to be populated with datetime.now()
Column('last_updated', DateTime, onupdate=datetime.datetime.now),
)
When an update statement executes and no value is passed for ``last_updated``, the ``datetime.datetime.now()`` Python function is executed and its return value used as the value for ``last_updated``. Notice that we provide ``now`` as the function itself without calling it (i.e. there are no parenthesis following) - SQLAlchemy will execute the function at the time the statement executes.
Context-Sensitive Default Functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The Python functions used by ``default`` and ``onupdate`` may also make use of the current statement's context in order to determine a value. The `context` of a statement is an internal SQLAlchemy object which contains all information about the statement being executed, including its source expression, the parameters associated with it and the cursor. The typical use case for this context with regards to default generation is to have access to the other values being inserted or updated on the row. To access the context, provide a function that accepts a single ``context`` argument::
def mydefault(context):
return context.current_parameters['counter'] + 12
t = Table('mytable', meta,
Column('counter', Integer),
Column('counter_plus_twelve', Integer, default=mydefault, onupdate=mydefault)
)
Above we illustrate a default function which will execute for all INSERT and UPDATE statements where a value for ``counter_plus_twelve`` was otherwise not provided, and the value will be that of whatever value is present in the execution for the ``counter`` column, plus the number 12.
While the context object passed to the default function has many attributes, the ``current_parameters`` member is a special member provided only during the execution of a default function for the purposes of deriving defaults from its existing values. For a single statement that is executing many sets of bind parameters, the user-defined function is called for each set of parameters, and ``current_parameters`` will be provided with each individual parameter set for each execution.
SQL Expressions
---------------
The "default" and "onupdate" keywords may also be passed SQL expressions, including select statements or direct function calls::
t = Table("mytable", meta,
Column('id', Integer, primary_key=True),
# define 'create_date' to default to now()
Column('create_date', DateTime, default=func.now()),
# define 'key' to pull its default from the 'keyvalues' table
Column('key', String(20), default=keyvalues.select(keyvalues.c.type='type1', limit=1)),
# define 'last_modified' to use the current_timestamp SQL function on update
Column('last_modified', DateTime, onupdate=func.utc_timestamp())
)
Above, the ``create_date`` column will be populated with the result of the ``now()`` SQL function (which, depending on backend, compiles into ``NOW()`` or ``CURRENT_TIMESTAMP`` in most cases) during an INSERT statement, and the ``key`` column with the result of a SELECT subquery from another table. The ``last_modified`` column will be populated with the value of ``UTC_TIMESTAMP()``, a function specific to MySQL, when an UPDATE statement is emitted for this table.
Note that when using ``func`` functions, unlike when using Python `datetime` functions we *do* call the function, i.e. with parenthesis "()" - this is because what we want in this case is the return value of the function, which is the SQL expression construct that will be rendered into the INSERT or UPDATE statement.
The above SQL functions are usually executed "inline" with the INSERT or UPDATE statement being executed, meaning, a single statement is executed which embeds the given expressions or subqueries within the VALUES or SET clause of the statement. Although in some cases, the function is "pre-executed" in a SELECT statement of its own beforehand. This happens when all of the following is true:
* the column is a primary key column
* the database dialect does not support a usable ``cursor.lastrowid`` accessor (or equivalent); this currently includes PostgreSQL, Oracle, and Firebird, as well as some MySQL dialects.
* the dialect does not support the "RETURNING" clause or similar, or the ``implicit_returning`` flag is set to ``False`` for the dialect. Dialects which support RETURNING currently include Postgresql, Oracle, Firebird, and MS-SQL.
* the statement is a single execution, i.e. only supplies one set of parameters and doesn't use "executemany" behavior
* the ``inline=True`` flag is not set on the ``Insert()`` or ``Update()`` construct, and the statement has not defined an explicit `returning()` clause.
Whether or not the default generation clause "pre-executes" is not something that normally needs to be considered, unless it is being addressed for performance reasons.
When the statement is executed with a single set of parameters (that is, it is not an "executemany" style execution), the returned ``ResultProxy`` will contain a collection accessible via ``result.postfetch_cols()`` which contains a list of all ``Column`` objects which had an inline-executed default. Similarly, all parameters which were bound to the statement, including all Python and SQL expressions which were pre-executed, are present in the ``last_inserted_params()`` or ``last_updated_params()`` collections on ``ResultProxy``. The ``inserted_primary_key`` collection contains a list of primary key values for the row inserted (a list so that single-column and composite-column primary keys are represented in the same format).
Server Side Defaults
--------------------
A variant on the SQL expression default is the ``server_default``, which gets placed in the CREATE TABLE statement during a ``create()`` operation:
.. sourcecode:: python+sql
t = Table('test', meta,
Column('abc', String(20), server_default='abc'),
Column('created_at', DateTime, server_default=text("sysdate"))
)
A create call for the above table will produce::
CREATE TABLE test (
abc varchar(20) default 'abc',
created_at datetime default sysdate
)
The behavior of ``server_default`` is similar to that of a regular SQL default; if it's placed on a primary key column for a database which doesn't have a way to "postfetch" the ID, and the statement is not "inlined", the SQL expression is pre-executed; otherwise, SQLAlchemy lets the default fire off on the database side normally.
Triggered Columns
------------------
Columns with values set by a database trigger or other external process may be called out with a marker::
t = Table('test', meta,
Column('abc', String(20), server_default=FetchedValue()),
Column('def', String(20), server_onupdate=FetchedValue())
)
These markers do not emit a "default" clause when the table is created, however they do set the same internal flags as a static ``server_default`` clause, providing hints to higher-level tools that a "post-fetch" of these rows should be performed after an insert or update.
Defining Sequences
-------------------
SQLAlchemy represents database sequences using the ``Sequence`` object, which is considered to be a special case of "column default". It only has an effect on databases which have explicit support for sequences, which currently includes Postgresql, Oracle, and Firebird. The ``Sequence`` object is otherwise ignored.
The ``Sequence`` may be placed on any column as a "default" generator to be used during INSERT operations, and can also be configured to fire off during UPDATE operations if desired. It is most commonly used in conjunction with a single integer primary key column::
table = Table("cartitems", meta,
Column("cart_id", Integer, Sequence('cart_id_seq'), primary_key=True),
Column("description", String(40)),
Column("createdate", DateTime())
)
Where above, the table "cartitems" is associated with a sequence named "cart_id_seq". When INSERT statements take place for "cartitems", and no value is passed for the "cart_id" column, the "cart_id_seq" sequence will be used to generate a value.
When the ``Sequence`` is associated with a table, CREATE and DROP statements issued for that table will also issue CREATE/DROP for the sequence object as well, thus "bundling" the sequence object with its parent table.
The ``Sequence`` object also implements special functionality to accommodate Postgresql's SERIAL datatype. The SERIAL type in PG automatically generates a sequence that is used implicitly during inserts. This means that if a ``Table`` object defines a ``Sequence`` on its primary key column so that it works with Oracle and Firebird, the ``Sequence`` would get in the way of the "implicit" sequence that PG would normally use. For this use case, add the flag ``optional=True`` to the ``Sequence`` object - this indicates that the ``Sequence`` should only be used if the database provides no other option for generating primary key identifiers.
The ``Sequence`` object also has the ability to be executed standalone like a SQL expression, which has the effect of calling its "next value" function::
seq = Sequence('some_sequence')
nextid = connection.execute(seq)
Defining Constraints and Indexes
=================================
.. _metadata_foreignkeys:
Defining Foreign Keys
---------------------
A *foreign key* in SQL is a table-level construct that constrains one or more columns in that table to only allow values that are present in a different set of columns, typically but not always located on a different table. We call the columns which are constrained the *foreign key* columns and the columns which they are constrained towards the *referenced* columns. The referenced columns almost always define the primary key for their owning table, though there are exceptions to this. The foreign key is the "joint" that connects together pairs of rows which have a relationship with each other, and SQLAlchemy assigns very deep importance to this concept in virtually every area of its operation.
In SQLAlchemy as well as in DDL, foreign key constraints can be defined as additional attributes within the table clause, or for single-column foreign keys they may optionally be specified within the definition of a single column. The single column foreign key is more common, and at the column level is specified by constructing a ``ForeignKey`` object as an argument to a ``Column`` object::
user_preference = Table('user_preference', metadata,
Column('pref_id', Integer, primary_key=True),
Column('user_id', Integer, ForeignKey("user.user_id"), nullable=False),
Column('pref_name', String(40), nullable=False),
Column('pref_value', String(100))
)
Above, we define a new table ``user_preference`` for which each row must contain a value in the ``user_id`` column that also exists in the ``user`` table's ``user_id`` column.
The argument to ``ForeignKey`` is most commonly a string of the form *<tablename>.<columnname>*, or for a table in a remote schema or "owner" of the form *<schemaname>.<tablename>.<columnname>*. It may also be an actual ``Column`` object, which as we'll see later is accessed from an existing ``Table`` object via its ``c`` collection::
ForeignKey(user.c.user_id)
The advantage to using a string is that the in-python linkage between ``user`` and ``user_preference`` is resolved only when first needed, so that table objects can be easily spread across multiple modules and defined in any order.
Foreign keys may also be defined at the table level, using the ``ForeignKeyConstraint`` object. This object can describe a single- or multi-column foreign key. A multi-column foreign key is known as a *composite* foreign key, and almost always references a table that has a composite primary key. Below we define a table ``invoice`` which has a composite primary key::
invoice = Table('invoice', metadata,
Column('invoice_id', Integer, primary_key=True),
Column('ref_num', Integer, primary_key=True),
Column('description', String(60), nullable=False)
)
And then a table ``invoice_item`` with a composite foreign key referencing ``invoice``::
invoice_item = Table('invoice_item', metadata,
Column('item_id', Integer, primary_key=True),
Column('item_name', String(60), nullable=False),
Column('invoice_id', Integer, nullable=False),
Column('ref_num', Integer, nullable=False),
ForeignKeyConstraint(['invoice_id', 'ref_num'], ['invoice.invoice_id', 'invoice.ref_num'])
)
It's important to note that the ``ForeignKeyConstraint`` is the only way to define a composite foreign key. While we could also have placed individual ``ForeignKey`` objects on both the ``invoice_item.invoice_id`` and ``invoice_item.ref_num`` columns, SQLAlchemy would not be aware that these two values should be paired together - it would be two individual foreign key constraints instead of a single composite foreign key referencing two columns.
Creating/Dropping Foreign Key Constraints via ALTER
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In all the above examples, the ``ForeignKey`` object causes the "REFERENCES" keyword to be added inline to a column definition within a "CREATE TABLE" statement when ``create_all()`` is issued, and ``ForeignKeyConstraint`` invokes the "CONSTRAINT" keyword inline with "CREATE TABLE". There are some cases where this is undesireable, particularly when two tables reference each other mutually, each with a foreign key referencing the other. In such a situation at least one of the foreign key constraints must be generated after both tables have been built. To support such a scheme, ``ForeignKey`` and ``ForeignKeyConstraint`` offer the flag ``use_alter=True``. When using this flag, the constraint will be generated using a definition similar to "ALTER TABLE <tablename> ADD CONSTRAINT <name> ...". Since a name is required, the ``name`` attribute must also be specified. For example::
node = Table('node', meta,
Column('node_id', Integer, primary_key=True),
Column('primary_element', Integer,
ForeignKey('element.element_id', use_alter=True, name='fk_node_element_id')
)
)
element = Table('element', meta,
Column('element_id', Integer, primary_key=True),
Column('parent_node_id', Integer),
ForeignKeyConstraint(
['parent_node_id'],
['node.node_id'],
use_alter=True,
name='fk_element_parent_node_id'
)
)
ON UPDATE and ON DELETE
~~~~~~~~~~~~~~~~~~~~~~~
Most databases support *cascading* of foreign key values, that is the when a parent row is updated the new value is placed in child rows, or when the parent row is deleted all corresponding child rows are set to null or deleted. In data definition language these are specified using phrases like "ON UPDATE CASCADE", "ON DELETE CASCADE", and "ON DELETE SET NULL", corresponding to foreign key constraints. The phrase after "ON UPDATE" or "ON DELETE" may also other allow other phrases that are specific to the database in use. The ``ForeignKey`` and ``ForeignKeyConstraint`` objects support the generation of this clause via the ``onupdate`` and ``ondelete`` keyword arguments. The value is any string which will be output after the appropriate "ON UPDATE" or "ON DELETE" phrase::
child = Table('child', meta,
Column('id', Integer,
ForeignKey('parent.id', onupdate="CASCADE", ondelete="CASCADE"),
primary_key=True
)
)
composite = Table('composite', meta,
Column('id', Integer, primary_key=True),
Column('rev_id', Integer),
Column('note_id', Integer),
ForeignKeyConstraint(
['rev_id', 'note_id'],
['revisions.id', 'revisions.note_id'],
onupdate="CASCADE", ondelete="SET NULL"
)
)
Note that these clauses are not supported on SQLite, and require ``InnoDB`` tables when used with MySQL. They may also not be supported on other databases.
UNIQUE Constraint
-----------------
Unique constraints can be created anonymously on a single column using the ``unique`` keyword on ``Column``. Explicitly named unique constraints and/or those with multiple columns are created via the ``UniqueConstraint`` table-level construct.
.. sourcecode:: python+sql
meta = MetaData()
mytable = Table('mytable', meta,
# per-column anonymous unique constraint
Column('col1', Integer, unique=True),
Column('col2', Integer),
Column('col3', Integer),
# explicit/composite unique constraint. 'name' is optional.
UniqueConstraint('col2', 'col3', name='uix_1')
)
CHECK Constraint
----------------
Check constraints can be named or unnamed and can be created at the Column or Table level, using the ``CheckConstraint`` construct. The text of the check constraint is passed directly through to the database, so there is limited "database independent" behavior. Column level check constraints generally should only refer to the column to which they are placed, while table level constraints can refer to any columns in the table.
Note that some databases do not actively support check constraints such as MySQL and SQLite.
.. sourcecode:: python+sql
meta = MetaData()
mytable = Table('mytable', meta,
# per-column CHECK constraint
Column('col1', Integer, CheckConstraint('col1>5')),
Column('col2', Integer),
Column('col3', Integer),
# table level CHECK constraint. 'name' is optional.
CheckConstraint('col2 > col3 + 5', name='check1')
)
{sql}mytable.create(engine)
CREATE TABLE mytable (
col1 INTEGER CHECK (col1>5),
col2 INTEGER,
col3 INTEGER,
CONSTRAINT check1 CHECK (col2 > col3 + 5)
){stop}
Indexes
-------
Indexes can be created anonymously (using an auto-generated name ``ix_<column label>``) for a single column using the inline ``index`` keyword on ``Column``, which also modifies the usage of ``unique`` to apply the uniqueness to the index itself, instead of adding a separate UNIQUE constraint. For indexes with specific names or which encompass more than one column, use the ``Index`` construct, which requires a name.
Note that the ``Index`` construct is created **externally** to the table which it corresponds, using ``Column`` objects and not strings.
Below we illustrate a ``Table`` with several ``Index`` objects associated. The DDL for "CREATE INDEX" is issued right after the create statements for the table:
.. sourcecode:: python+sql
meta = MetaData()
mytable = Table('mytable', meta,
# an indexed column, with index "ix_mytable_col1"
Column('col1', Integer, index=True),
# a uniquely indexed column with index "ix_mytable_col2"
Column('col2', Integer, index=True, unique=True),
Column('col3', Integer),
Column('col4', Integer),
Column('col5', Integer),
Column('col6', Integer),
)
# place an index on col3, col4
Index('idx_col34', mytable.c.col3, mytable.c.col4)
# place a unique index on col5, col6
Index('myindex', mytable.c.col5, mytable.c.col6, unique=True)
{sql}mytable.create(engine)
CREATE TABLE mytable (
col1 INTEGER,
col2 INTEGER,
col3 INTEGER,
col4 INTEGER,
col5 INTEGER,
col6 INTEGER
)
CREATE INDEX ix_mytable_col1 ON mytable (col1)
CREATE UNIQUE INDEX ix_mytable_col2 ON mytable (col2)
CREATE UNIQUE INDEX myindex ON mytable (col5, col6)
CREATE INDEX idx_col34 ON mytable (col3, col4){stop}
The ``Index`` object also supports its own ``create()`` method:
.. sourcecode:: python+sql
i = Index('someindex', mytable.c.col5)
{sql}i.create(engine)
CREATE INDEX someindex ON mytable (col5){stop}
Customizing DDL
===============
In the preceding sections we've discussed a variety of schema constructs including ``Table``, ``ForeignKeyConstraint``, ``CheckConstraint``, and ``Sequence``. Throughout, we've relied upon the ``create()`` and ``create_all()`` methods of ``Table`` and ``MetaData`` in order to issue data definition language (DDL) for all constructs. When issued, a pre-determined order of operations is invoked, and DDL to create each table is created unconditionally including all constraints and other objects associated with it. For more complex scenarios where database-specific DDL is required, SQLAlchemy offers two techniques which can be used to add any DDL based on any condition, either accompanying the standard generation of tables or by itself.
Controlling DDL Sequences
-------------------------
The ``sqlalchemy.schema`` package contains SQL expression constructs that provide DDL expressions. For example, to produce a ``CREATE TABLE`` statement:
.. sourcecode:: python+sql
from sqlalchemy.schema import CreateTable
{sql}engine.execute(CreateTable(mytable))
CREATE TABLE mytable (
col1 INTEGER,
col2 INTEGER,
col3 INTEGER,
col4 INTEGER,
col5 INTEGER,
col6 INTEGER
){stop}
Above, the ``CreateTable`` construct works like any other expression construct (such as ``select()``, ``table.insert()``, etc.). A full reference of available constructs is in :ref:`schema_api_ddl`.
The DDL constructs all extend a common base class which provides the capability to be associated with an individual ``Table`` or ``MetaData`` object, to be invoked upon create/drop events. Consider the example of a table which contains a CHECK constraint:
.. sourcecode:: python+sql
users = Table('users', metadata,
Column('user_id', Integer, primary_key=True),
Column('user_name', String(40), nullable=False),
CheckConstraint('length(user_name) >= 8',name="cst_user_name_length")
)
{sql}users.create(engine)
CREATE TABLE users (
user_id SERIAL NOT NULL,
user_name VARCHAR(40) NOT NULL,
PRIMARY KEY (user_id),
CONSTRAINT cst_user_name_length CHECK (length(user_name) >= 8)
){stop}
The above table contains a column "user_name" which is subject to a CHECK constraint that validates that the length of the string is at least eight characters. When a ``create()`` is issued for this table, DDL for the ``CheckConstraint`` will also be issued inline within the table definition.
The ``CheckConstraint`` construct can also be constructed externally and associated with the ``Table`` afterwards::
constraint = CheckConstraint('length(user_name) >= 8',name="cst_user_name_length")
users.append_constraint(constraint)
So far, the effect is the same. However, if we create DDL elements corresponding to the creation and removal of this constraint, and associate them with the ``Table`` as events, these new events will take over the job of issuing DDL for the constraint. Additionally, the constraint will be added via ALTER:
.. sourcecode:: python+sql
AddConstraint(constraint).execute_at("after-create", users)
DropConstraint(constraint).execute_at("before-drop", users)
{sql}users.create(engine)
CREATE TABLE users (
user_id SERIAL NOT NULL,
user_name VARCHAR(40) NOT NULL,
PRIMARY KEY (user_id)
)
ALTER TABLE users ADD CONSTRAINT cst_user_name_length CHECK (length(user_name) >= 8){stop}
{sql}users.drop(engine)
ALTER TABLE users DROP CONSTRAINT cst_user_name_length
DROP TABLE users{stop}
The real usefulness of the above becomes clearer once we illustrate the ``on`` attribute of a DDL event. The ``on`` parameter is part of the constructor, and may be a string name of a database dialect name, a tuple containing dialect names, or a Python callable. This will limit the execution of the item to just those dialects, or when the return value of the callable is ``True``. So if our ``CheckConstraint`` was only supported by Postgresql and not other databases, we could limit it to just that dialect::
AddConstraint(constraint, on='postgresql').execute_at("after-create", users)
DropConstraint(constraint, on='postgresql').execute_at("before-drop", users)
Or to any set of dialects::
AddConstraint(constraint, on=('postgresql', 'mysql')).execute_at("after-create", users)
DropConstraint(constraint, on=('postgresql', 'mysql')).execute_at("before-drop", users)
When using a callable, the callable is passed the ddl element, event name, the ``Table`` or ``MetaData`` object whose "create" or "drop" event is in progress, and the ``Connection`` object being used for the operation, as well as additional information as keyword arguments. The callable can perform checks, such as whether or not a given item already exists. Below we define ``should_create()`` and ``should_drop()`` callables that check for the presence of our named constraint:
.. sourcecode:: python+sql
def should_create(ddl, event, target, connection, **kw):
row = connection.execute("select conname from pg_constraint where conname='%s'" % ddl.element.name).scalar()
return not bool(row)
def should_drop(ddl, event, target, connection, **kw):
return not should_create(ddl, event, target, connection, **kw)
AddConstraint(constraint, on=should_create).execute_at("after-create", users)
DropConstraint(constraint, on=should_drop).execute_at("before-drop", users)
{sql}users.create(engine)
CREATE TABLE users (
user_id SERIAL NOT NULL,
user_name VARCHAR(40) NOT NULL,
PRIMARY KEY (user_id)
)
select conname from pg_constraint where conname='cst_user_name_length'
ALTER TABLE users ADD CONSTRAINT cst_user_name_length CHECK (length(user_name) >= 8){stop}
{sql}users.drop(engine)
select conname from pg_constraint where conname='cst_user_name_length'
ALTER TABLE users DROP CONSTRAINT cst_user_name_length
DROP TABLE users{stop}
Custom DDL
----------
Custom DDL phrases are most easily achieved using the :class:`~sqlalchemy.schema.DDL` construct. This construct works like all the other DDL elements except it accepts a string which is the
text to be emitted:
.. sourcecode:: python+sql
DDL("ALTER TABLE users ADD CONSTRAINT "
"cst_user_name_length "
" CHECK (length(user_name) >= 8)").execute_at("after-create", metadata)
A more comprehensive method of creating libraries of DDL constructs is to use the :ref:`sqlalchemy.ext.compiler_toplevel` extension. See that chapter for full details.
Adapting Tables to Alternate Metadata
======================================
A ``Table`` object created against a specific ``MetaData`` object can be re-created against a new MetaData using the ``tometadata`` method:
.. sourcecode:: python+sql
# create two metadata
meta1 = MetaData('sqlite:///querytest.db')
meta2 = MetaData()
# load 'users' from the sqlite engine
users_table = Table('users', meta1, autoload=True)
# create the same Table object for the plain metadata
users_table_2 = users_table.tometadata(meta2)
+1341
View File
File diff suppressed because it is too large Load Diff
-244
View File
@@ -1,244 +0,0 @@
"""loads Markdown files, converts each one to HTML and parses the HTML into an ElementTree structure.
The collection of ElementTrees are further parsed to generate a table of contents structure, and are
manipulated to replace various markdown-generated HTML with specific Mako tags before being written
to Mako templates, which then re-access the table of contents structure at runtime.
Much thanks to Alexey Shamrin, who came up with the original idea and did all the heavy Markdown/Elementtree
lifting for this module.
"""
import sys, re, os
from toc import TOCElement
try:
import xml.etree.ElementTree as et
except ImportError:
try:
import elementtree.ElementTree as et
except:
raise "This module requires ElementTree to run (http://effbot.org/zone/element-index.htm)"
import markdown
def dump_tree(elem, stream):
if elem.tag.startswith('MAKO:'):
dump_mako_tag(elem, stream)
else:
if elem.tag != 'html':
if elem.attrib:
stream.write("<%s %s>" % (elem.tag, " ".join(["%s=%s" % (key, repr(val)) for key, val in elem.attrib.iteritems()])))
else:
stream.write("<%s>" % elem.tag)
if elem.text:
stream.write(elem.text)
for child in elem:
dump_tree(child, stream)
if child.tail:
stream.write(child.tail)
if elem.tag != 'html':
stream.write("</%s>" % elem.tag)
def dump_mako_tag(elem, stream):
tag = elem.tag[5:]
params = ','.join(['%s=%s' % i for i in elem.items()])
stream.write('<%%call expr="%s(%s)">' % (tag, params))
if elem.text:
stream.write(elem.text)
for n in elem:
dump_tree(n, stream)
if n.tail:
stream.write(n.tail)
stream.write("</%call>")
def create_toc(filename, tree, tocroot):
title = [None]
current = [tocroot]
level = [0]
def process(tree):
while True:
i = find_header_index(tree)
if i is None:
return
node = tree[i]
taglevel = int(node.tag[1])
start, end = i, end_of_header(tree, taglevel, i+1)
content = tree[start+1:end]
description = node.text.strip()
if title[0] is None:
title[0] = description
name = node.get('name')
if name is None:
name = description.split()[0].lower()
taglevel = node.tag[1]
if taglevel > level[0]:
current[0] = TOCElement(filename, name, description, current[0])
elif taglevel == level[0]:
current[0] = TOCElement(filename, name, description, current[0].parent)
else:
current[0] = TOCElement(filename, name, description, current[0].parent.parent)
level[0] = taglevel
tag = et.Element("MAKO:formatting.section", path=repr(current[0].path), paged='paged', extension='extension', toc='toc')
tag.text = (node.tail or "") + '\n'
tag.tail = '\n'
tag[:] = content
tree[start:end] = [tag]
process(tag)
process(tree)
return (title[0], tocroot.get_by_file(filename))
def literal(s):
return '"%s"' % s
def index(parent, item):
for n, i in enumerate(parent):
if i is item:
return n
def find_header_index(tree):
for i, node in enumerate(tree):
if is_header(node):
return i
def is_header(node):
t = node.tag
return (isinstance(t, str) and len(t) == 2 and t[0] == 'h'
and t[1] in '123456789')
def end_of_header(tree, level, start):
for i, node in enumerate(tree[start:]):
if is_header(node) and int(node.tag[1]) <= level:
return start + i
return len(tree)
def process_rel_href(tree):
parent = get_parent_map(tree)
for a in tree.findall('.//a'):
m = re.match(r'(bold)?rel\:(.+)', a.get('href'))
if m:
(bold, path) = m.group(1,2)
text = a.text
if text == path:
tag = et.Element("MAKO:nav.toclink", path=repr(path), extension='extension', paged='paged', toc='toc')
else:
tag = et.Element("MAKO:nav.toclink", path=repr(path), description=repr(text), extension='extension', paged='paged', toc='toc')
a_parent = parent[a]
if bold:
bold = et.Element('strong')
bold.tail = a.tail
bold.append(tag)
a_parent[index(a_parent, a)] = bold
else:
tag.tail = a.tail
a_parent[index(a_parent, a)] = tag
def replace_pre_with_mako(tree):
def splice_code_tag(pre, text, code=None, title=None):
doctest_directives = re.compile(r'#\s*doctest:\s*[+-]\w+(,[+-]\w+)*\s*$', re.M)
text = re.sub(doctest_directives, '', text)
# process '>>>' to have quotes around it, to work with the pygments
# syntax highlighter which uses the tokenize module
text = re.sub(r'>>> ', r'">>>" ', text)
sqlre = re.compile(r'{sql}(.*?)\n((?:PRAGMA|BEGIN|SELECT|INSERT|DELETE|ROLLBACK|COMMIT|UPDATE|CREATE|DROP|PRAGMA|DESCRIBE).*?)\n\s*((?:{stop})|\n|$)', re.S)
if sqlre.search(text) is not None:
use_sliders = False
else:
use_sliders = True
text = sqlre.sub(r"""${formatting.poplink()}\1<%call expr="formatting.codepopper()">\2</%call>""", text)
#sqlre2 = re.compile(r'{opensql}(.*?\n)((?:PRAGMA|BEGIN|SELECT|INSERT|DELETE|UPDATE|ROLLBACK|COMMIT|CREATE|DROP).*?)\n\s*((?:{stop})|\n|$)', re.S)
sqlre2 = re.compile(r'{opensql}(.*?)\n?((?:PRAGMA|BEGIN|SELECT|INSERT|DELETE|ROLLBACK|COMMIT|UPDATE|CREATE|DROP|PRAGMA|DESCRIBE).*?)\n\s*((?:{stop})|\n|$)', re.S)
text = sqlre2.sub(r"\1<%call expr='formatting.poppedcode()' >\2</%call>\n\n", text)
tag = et.Element("MAKO:formatting.code", extension='extension', paged='paged', toc='toc')
if code:
tag.attrib["syntaxtype"] = repr(code)
if title:
tag.attrib["title"] = repr(title)
if use_sliders:
tag.attrib['use_sliders'] = True
tag.text = text
pre_parent = parents[pre]
tag.tail = pre.tail
pre_parent[reverse_parent(pre_parent, pre)] = tag
parents = get_parent_map(tree)
for precode in tree.findall('.//pre/code'):
reg = re.compile(r'\{(python|code|diagram)(?: title="(.*?)"){0,1}\}(.*)', re.S)
m = reg.match(precode[0].text.lstrip())
if m:
code = m.group(1)
title = m.group(2)
text = m.group(3)
text = re.sub(r'{(python|code|diagram).*?}(\n\s*)?', '', text)
text = re.sub(r'\\\n', r'${r"\\\\" + "\\n\\n"}', text)
splice_code_tag(parents[precode], text, code=code, title=title)
elif precode.text.lstrip().startswith('>>> '):
splice_code_tag(parents[precode], precode.text)
def safety_code(tree):
parents = get_parent_map(tree)
for code in tree.findall('.//code'):
tag = et.Element('%text')
if parents[code].tag != 'pre':
tag.attrib["filter"] = "h"
tag.text = code.text
code.append(tag)
code.text = ""
def reverse_parent(parent, item):
for n, i in enumerate(parent):
if i is item:
return n
def get_parent_map(tree):
return dict([(c, p) for p in tree.getiterator() for c in p])
def header(toc, title, filename):
return \
"""# -*- coding: utf-8 -*-
<%%inherit file="content_layout.html"/>
<%%page args="toc, extension, paged"/>
<%%namespace name="formatting" file="formatting.html"/>
<%%namespace name="nav" file="nav.html"/>
<%%def name="title()">%s - %s</%%def>
<%%!
filename = '%s'
%%>
## This file is generated. Edit the .txt files instead of this one.
""" % (toc.root.doctitle, title, filename)
class utf8stream(object):
def __init__(self, stream):
self.stream = stream
def write(self, str):
self.stream.write(str.encode('utf8'))
def parse_markdown_files(toc, files):
for inname in files:
infile = 'content/%s.txt' % inname
if not os.access(infile, os.F_OK):
continue
html = markdown.markdown(file(infile).read())
#foo = file('foo', 'w')
#foo.write(html)
tree = et.fromstring("<html>" + html + "</html>")
(title, toc_element) = create_toc(inname, tree, toc)
safety_code(tree)
replace_pre_with_mako(tree)
process_rel_href(tree)
outname = 'output/%s.html' % inname
print infile, '->', outname
outfile = utf8stream(file(outname, 'w'))
outfile.write(header(toc, title, inname))
dump_tree(tree, outfile)
+4
View File
@@ -0,0 +1,4 @@
Microsoft Access
================
.. automodule:: sqlalchemy.dialects.access.base
+11
View File
@@ -0,0 +1,11 @@
Firebird
========
.. automodule:: sqlalchemy.dialects.firebird.base
.. _kinterbasdb:
kinterbasdb
-----------
.. automodule:: sqlalchemy.dialects.firebird.kinterbasdb
+35
View File
@@ -0,0 +1,35 @@
.. _sqlalchemy.dialects:
sqlalchemy.dialects
====================
Supported Databases
-------------------
These backends are fully operational with
current versions of SQLAlchemy.
.. toctree::
:glob:
firebird
mssql
mysql
oracle
postgresql
sqlite
Unsupported Databases
---------------------
These backends are untested and may not be completely
ported to current versions of SQLAlchemy.
.. toctree::
:glob:
access
informix
maxdb
sybase
+4
View File
@@ -0,0 +1,4 @@
Informix
========
.. automodule:: sqlalchemy.dialects.informix.base
+4
View File
@@ -0,0 +1,4 @@
MaxDB
=====
.. automodule:: sqlalchemy.dialects.maxdb.base
+21
View File
@@ -0,0 +1,21 @@
Microsoft SQL Server
====================
.. automodule:: sqlalchemy.dialects.mssql.base
PyODBC
------
.. automodule:: sqlalchemy.dialects.mssql.pyodbc
AdoDBAPI
--------
.. automodule:: sqlalchemy.dialects.mssql.adodbapi
pymssql
-------
.. automodule:: sqlalchemy.dialects.mssql.pymssql
zxjdbc Notes
--------------
.. automodule:: sqlalchemy.dialects.mssql.zxjdbc
+164
View File
@@ -0,0 +1,164 @@
MySQL
=====
.. automodule:: sqlalchemy.dialects.mysql.base
MySQL Column Types
------------------
.. autoclass:: NUMERIC
:members: __init__
:show-inheritance:
.. autoclass:: DECIMAL
:members: __init__
:show-inheritance:
.. autoclass:: DOUBLE
:members: __init__
:show-inheritance:
.. autoclass:: REAL
:members: __init__
:show-inheritance:
.. autoclass:: FLOAT
:members: __init__
:show-inheritance:
.. autoclass:: INTEGER
:members: __init__
:show-inheritance:
.. autoclass:: BIGINT
:members: __init__
:show-inheritance:
.. autoclass:: MEDIUMINT
:members: __init__
:show-inheritance:
.. autoclass:: TINYINT
:members: __init__
:show-inheritance:
.. autoclass:: SMALLINT
:members: __init__
:show-inheritance:
.. autoclass:: BIT
:members: __init__
:show-inheritance:
.. autoclass:: DATETIME
:members: __init__
:show-inheritance:
.. autoclass:: DATE
:members: __init__
:show-inheritance:
.. autoclass:: TIME
:members: __init__
:show-inheritance:
.. autoclass:: TIMESTAMP
:members: __init__
:show-inheritance:
.. autoclass:: YEAR
:members: __init__
:show-inheritance:
.. autoclass:: TEXT
:members: __init__
:show-inheritance:
.. autoclass:: TINYTEXT
:members: __init__
:show-inheritance:
.. autoclass:: MEDIUMTEXT
:members: __init__
:show-inheritance:
.. autoclass:: LONGTEXT
:members: __init__
:show-inheritance:
.. autoclass:: VARCHAR
:members: __init__
:show-inheritance:
.. autoclass:: CHAR
:members: __init__
:show-inheritance:
.. autoclass:: NVARCHAR
:members: __init__
:show-inheritance:
.. autoclass:: NCHAR
:members: __init__
:show-inheritance:
.. autoclass:: VARBINARY
:members: __init__
:show-inheritance:
.. autoclass:: BINARY
:members: __init__
:show-inheritance:
.. autoclass:: BLOB
:members: __init__
:show-inheritance:
.. autoclass:: TINYBLOB
:members: __init__
:show-inheritance:
.. autoclass:: MEDIUMBLOB
:members: __init__
:show-inheritance:
.. autoclass:: LONGBLOB
:members: __init__
:show-inheritance:
.. autoclass:: ENUM
:members: __init__
:show-inheritance:
.. autoclass:: SET
:members: __init__
:show-inheritance:
.. autoclass:: BOOLEAN
:members: __init__
:show-inheritance:
MySQL-Python Notes
--------------------
.. automodule:: sqlalchemy.dialects.mysql.mysqldb
OurSQL Notes
--------------
.. automodule:: sqlalchemy.dialects.mysql.oursql
MySQL-Connector Notes
----------------------
.. automodule:: sqlalchemy.dialects.mysql.mysqlconnector
pyodbc Notes
--------------
.. automodule:: sqlalchemy.dialects.mysql.pyodbc
zxjdbc Notes
--------------
.. automodule:: sqlalchemy.dialects.mysql.zxjdbc
+14
View File
@@ -0,0 +1,14 @@
Oracle
======
.. automodule:: sqlalchemy.dialects.oracle.base
cx_Oracle Notes
---------------
.. automodule:: sqlalchemy.dialects.oracle.cx_oracle
zxjdbc Notes
--------------
.. automodule:: sqlalchemy.dialects.oracle.zxjdbc
+68
View File
@@ -0,0 +1,68 @@
PostgreSQL
==========
.. automodule:: sqlalchemy.dialects.postgresql.base
PostgresSQL Column Types
------------------------
.. autoclass:: ARRAY
:members: __init__
:show-inheritance:
.. autoclass:: BIT
:members: __init__
:show-inheritance:
.. autoclass:: BYTEA
:members: __init__
:show-inheritance:
.. autoclass:: CIDR
:members: __init__
:show-inheritance:
.. autoclass:: DOUBLE_PRECISION
:members: __init__
:show-inheritance:
.. autoclass:: ENUM
:members: __init__
:show-inheritance:
.. autoclass:: INET
:members: __init__
:show-inheritance:
.. autoclass:: INTERVAL
:members: __init__
:show-inheritance:
.. autoclass:: MACADDR
:members: __init__
:show-inheritance:
.. autoclass:: REAL
:members: __init__
:show-inheritance:
.. autoclass:: UUID
:members: __init__
:show-inheritance:
psycopg2 Notes
--------------
.. automodule:: sqlalchemy.dialects.postgresql.psycopg2
pg8000 Notes
--------------
.. automodule:: sqlalchemy.dialects.postgresql.pg8000
zxjdbc Notes
--------------
.. automodule:: sqlalchemy.dialects.postgresql.zxjdbc
+9
View File
@@ -0,0 +1,9 @@
SQLite
======
.. automodule:: sqlalchemy.dialects.sqlite.base
Pysqlite
--------
.. automodule:: sqlalchemy.dialects.sqlite.pysqlite
+4
View File
@@ -0,0 +1,4 @@
Sybase
======
.. automodule:: sqlalchemy.dialects.sybase.base
+305
View File
@@ -0,0 +1,305 @@
.. _associationproxy:
associationproxy
================
.. module:: sqlalchemy.ext.associationproxy
``associationproxy`` is used to create a simplified, read/write view of a
relationship. It can be used to cherry-pick fields from a collection of
related objects or to greatly simplify access to associated objects in an
association relationship.
Simplifying Relations
---------------------
Consider this "association object" mapping::
users_table = Table('users', metadata,
Column('id', Integer, primary_key=True),
Column('name', String(64)),
)
keywords_table = Table('keywords', metadata,
Column('id', Integer, primary_key=True),
Column('keyword', String(64))
)
userkeywords_table = Table('userkeywords', metadata,
Column('user_id', Integer, ForeignKey("users.id"),
primary_key=True),
Column('keyword_id', Integer, ForeignKey("keywords.id"),
primary_key=True)
)
class User(object):
def __init__(self, name):
self.name = name
class Keyword(object):
def __init__(self, keyword):
self.keyword = keyword
mapper(User, users_table, properties={
'kw': relation(Keyword, secondary=userkeywords_table)
})
mapper(Keyword, keywords_table)
Above are three simple tables, modeling users, keywords and a many-to-many
relationship between the two. These ``Keyword`` objects are little more
than a container for a name, and accessing them via the relation is
awkward::
user = User('jek')
user.kw.append(Keyword('cheese inspector'))
print user.kw
# [<__main__.Keyword object at 0xb791ea0c>]
print user.kw[0].keyword
# 'cheese inspector'
print [keyword.keyword for keyword in user.kw]
# ['cheese inspector']
With ``association_proxy`` you have a "view" of the relation that contains
just the ``.keyword`` of the related objects. The proxy is a Python
property, and unlike the mapper relation, is defined in your class::
from sqlalchemy.ext.associationproxy import association_proxy
class User(object):
def __init__(self, name):
self.name = name
# proxy the 'keyword' attribute from the 'kw' relation
keywords = association_proxy('kw', 'keyword')
# ...
>>> user.kw
[<__main__.Keyword object at 0xb791ea0c>]
>>> user.keywords
['cheese inspector']
>>> user.keywords.append('snack ninja')
>>> user.keywords
['cheese inspector', 'snack ninja']
>>> user.kw
[<__main__.Keyword object at 0x9272a4c>, <__main__.Keyword object at 0xb7b396ec>]
The proxy is read/write. New associated objects are created on demand when
values are added to the proxy, and modifying or removing an entry through
the proxy also affects the underlying collection.
- The association proxy property is backed by a mapper-defined relation,
either a collection or scalar.
- You can access and modify both the proxy and the backing
relation. Changes in one are immediate in the other.
- The proxy acts like the type of the underlying collection. A list gets a
list-like proxy, a dict a dict-like proxy, and so on.
- Multiple proxies for the same relation are fine.
- Proxies are lazy, and won't trigger a load of the backing relation until
they are accessed.
- The relation is inspected to determine the type of the related objects.
- To construct new instances, the type is called with the value being
assigned, or key and value for dicts.
- A ````creator```` function can be used to create instances instead.
Above, the ``Keyword.__init__`` takes a single argument ``keyword``, which
maps conveniently to the value being set through the proxy. A ``creator``
function could have been used instead if more flexibility was required.
Because the proxies are backed by a regular relation collection, all of the
usual hooks and patterns for using collections are still in effect. The
most convenient behavior is the automatic setting of "parent"-type
relationships on assignment. In the example above, nothing special had to
be done to associate the Keyword to the User. Simply adding it to the
collection is sufficient.
Simplifying Association Object Relations
----------------------------------------
Association proxies are also useful for keeping ``association objects`` out
the way during regular use. For example, the ``userkeywords`` table
might have a bunch of auditing columns that need to get updated when changes
are made- columns that are updated but seldom, if ever, accessed in your
application. A proxy can provide a very natural access pattern for the
relation.
.. sourcecode:: python
from sqlalchemy.ext.associationproxy import association_proxy
# users_table and keywords_table tables as above, then:
def get_current_uid():
"""Return the uid of the current user."""
return 1 # hardcoded for this example
userkeywords_table = Table('userkeywords', metadata,
Column('user_id', Integer, ForeignKey("users.id"), primary_key=True),
Column('keyword_id', Integer, ForeignKey("keywords.id"), primary_key=True),
# add some auditing columns
Column('updated_at', DateTime, default=datetime.now),
Column('updated_by', Integer, default=get_current_uid, onupdate=get_current_uid),
)
def _create_uk_by_keyword(keyword):
"""A creator function."""
return UserKeyword(keyword=keyword)
class User(object):
def __init__(self, name):
self.name = name
keywords = association_proxy('user_keywords', 'keyword', creator=_create_uk_by_keyword)
class Keyword(object):
def __init__(self, keyword):
self.keyword = keyword
def __repr__(self):
return 'Keyword(%s)' % repr(self.keyword)
class UserKeyword(object):
def __init__(self, user=None, keyword=None):
self.user = user
self.keyword = keyword
mapper(User, users_table)
mapper(Keyword, keywords_table)
mapper(UserKeyword, userkeywords_table, properties={
'user': relation(User, backref='user_keywords'),
'keyword': relation(Keyword),
})
user = User('log')
kw1 = Keyword('new_from_blammo')
# Creating a UserKeyword association object will add a Keyword.
# the "user" reference assignment in the UserKeyword() constructor
# populates "user_keywords" via backref.
UserKeyword(user, kw1)
# Accessing Keywords requires traversing UserKeywords
print user.user_keywords[0]
# <__main__.UserKeyword object at 0xb79bbbec>
print user.user_keywords[0].keyword
# Keyword('new_from_blammo')
# Lots of work.
# It's much easier to go through the association proxy!
for kw in (Keyword('its_big'), Keyword('its_heavy'), Keyword('its_wood')):
user.keywords.append(kw)
print user.keywords
# [Keyword('new_from_blammo'), Keyword('its_big'), Keyword('its_heavy'), Keyword('its_wood')]
Building Complex Views
----------------------
.. sourcecode:: python
stocks_table = Table("stocks", meta,
Column('symbol', String(10), primary_key=True),
Column('last_price', Numeric)
)
brokers_table = Table("brokers", meta,
Column('id', Integer,primary_key=True),
Column('name', String(100), nullable=False)
)
holdings_table = Table("holdings", meta,
Column('broker_id', Integer, ForeignKey('brokers.id'), primary_key=True),
Column('symbol', String(10), ForeignKey('stocks.symbol'), primary_key=True),
Column('shares', Integer)
)
Above are three tables, modeling stocks, their brokers and the number of
shares of a stock held by each broker. This situation is quite different
from the association example above. ``shares`` is a *property of the
relation*, an important one that we need to use all the time.
For this example, it would be very convenient if ``Broker`` objects had a
dictionary collection that mapped ``Stock`` instances to the shares held for
each. That's easy::
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm.collections import attribute_mapped_collection
def _create_holding(stock, shares):
"""A creator function, constructs Holdings from Stock and share quantity."""
return Holding(stock=stock, shares=shares)
class Broker(object):
def __init__(self, name):
self.name = name
holdings = association_proxy('by_stock', 'shares', creator=_create_holding)
class Stock(object):
def __init__(self, symbol):
self.symbol = symbol
self.last_price = 0
class Holding(object):
def __init__(self, broker=None, stock=None, shares=0):
self.broker = broker
self.stock = stock
self.shares = shares
mapper(Stock, stocks_table)
mapper(Broker, brokers_table, properties={
'by_stock': relation(Holding,
collection_class=attribute_mapped_collection('stock'))
})
mapper(Holding, holdings_table, properties={
'stock': relation(Stock),
'broker': relation(Broker)
})
Above, we've set up the ``by_stock`` relation collection to act as a
dictionary, using the ``.stock`` property of each Holding as a key.
Populating and accessing that dictionary manually is slightly inconvenient
because of the complexity of the Holdings association object::
stock = Stock('ZZK')
broker = Broker('paj')
broker.by_stock[stock] = Holding(broker, stock, 10)
print broker.by_stock[stock].shares
# 10
The ``holdings`` proxy we've added to the ``Broker`` class hides the details
of the ``Holding`` while also giving access to ``.shares``::
for stock in (Stock('JEK'), Stock('STPZ')):
broker.holdings[stock] = 123
for stock, shares in broker.holdings.items():
print stock, shares
session.add(broker)
session.commit()
# lets take a peek at that holdings_table after committing changes to the db
print list(holdings_table.select().execute())
# [(1, 'ZZK', 10), (1, 'JEK', 123), (1, 'STEPZ', 123)]
Further examples can be found in the ``examples/`` directory in the
SQLAlchemy distribution.
API
---
.. autofunction:: association_proxy
.. autoclass:: AssociationProxy
:members:
:undoc-members:
+7
View File
@@ -0,0 +1,7 @@
.. _sqlalchemy.ext.compiler_toplevel:
compiler
========
.. automodule:: sqlalchemy.ext.compiler
:members:
+5
View File
@@ -0,0 +1,5 @@
declarative
===========
.. automodule:: sqlalchemy.ext.declarative
:members:
+20
View File
@@ -0,0 +1,20 @@
.. _plugins:
.. _sqlalchemy.ext:
sqlalchemy.ext
==============
SQLAlchemy has a variety of extensions available which provide extra
functionality to SA, either via explicit usage or by augmenting the
core behavior.
.. toctree::
:glob:
declarative
associationproxy
orderinglist
serializer
sqlsoup
compiler
+88
View File
@@ -0,0 +1,88 @@
orderinglist
============
.. module: sqlalchemy.ext.orderinglist
:author: Jason Kirtland
``orderinglist`` is a helper for mutable ordered relations. It will intercept
list operations performed on a relation collection and automatically
synchronize changes in list position with an attribute on the related objects.
(See :ref:`advdatamapping_entitycollections` for more information on the general pattern.)
Example: Two tables that store slides in a presentation. Each slide
has a number of bullet points, displayed in order by the 'position'
column on the bullets table. These bullets can be inserted and re-ordered
by your end users, and you need to update the 'position' column of all
affected rows when changes are made.
.. sourcecode:: python+sql
slides_table = Table('Slides', metadata,
Column('id', Integer, primary_key=True),
Column('name', String))
bullets_table = Table('Bullets', metadata,
Column('id', Integer, primary_key=True),
Column('slide_id', Integer, ForeignKey('Slides.id')),
Column('position', Integer),
Column('text', String))
class Slide(object):
pass
class Bullet(object):
pass
mapper(Slide, slides_table, properties={
'bullets': relation(Bullet, order_by=[bullets_table.c.position])
})
mapper(Bullet, bullets_table)
The standard relation mapping will produce a list-like attribute on each Slide
containing all related Bullets, but coping with changes in ordering is totally
your responsibility. If you insert a Bullet into that list, there is no
magic- it won't have a position attribute unless you assign it it one, and
you'll need to manually renumber all the subsequent Bullets in the list to
accommodate the insert.
An ``orderinglist`` can automate this and manage the 'position' attribute on all
related bullets for you.
.. sourcecode:: python+sql
mapper(Slide, slides_table, properties={
'bullets': relation(Bullet,
collection_class=ordering_list('position'),
order_by=[bullets_table.c.position])
})
mapper(Bullet, bullets_table)
s = Slide()
s.bullets.append(Bullet())
s.bullets.append(Bullet())
s.bullets[1].position
>>> 1
s.bullets.insert(1, Bullet())
s.bullets[2].position
>>> 2
Use the ``ordering_list`` function to set up the ``collection_class`` on relations
(as in the mapper example above). This implementation depends on the list
starting in the proper order, so be SURE to put an order_by on your relation.
``ordering_list`` takes the name of the related object's ordering attribute as
an argument. By default, the zero-based integer index of the object's
position in the ``ordering_list`` is synchronized with the ordering attribute:
index 0 will get position 0, index 1 position 1, etc. To start numbering at 1
or some other integer, provide ``count_from=1``.
Ordering values are not limited to incrementing integers. Almost any scheme
can implemented by supplying a custom ``ordering_func`` that maps a Python list
index to any value you require. See the [module
documentation](rel:docstrings_sqlalchemy.ext.orderinglist) for more
information, and also check out the unit tests for examples of stepped
numbering, alphabetical and Fibonacci numbering.
.. automodule:: sqlalchemy.ext.orderinglist
:members:
:undoc-members:
+8
View File
@@ -0,0 +1,8 @@
serializer
==========
:author: Mike Bayer
.. automodule:: sqlalchemy.ext.serializer
:members:
:undoc-members:
+6
View File
@@ -0,0 +1,6 @@
SqlSoup
=======
.. automodule:: sqlalchemy.ext.sqlsoup
:members:
+13
View File
@@ -0,0 +1,13 @@
.. _api_reference_toplevel:
API Reference
=============
.. toctree::
:maxdepth: 3
sqlalchemy/index
orm/index
dialects/index
ext/index
+20
View File
@@ -0,0 +1,20 @@
Collection Mapping
==================
This is an in-depth discussion of collection mechanics. For simple examples, see :ref:`alternate_collection_implementations`.
.. automodule:: sqlalchemy.orm.collections
.. autofunction:: attribute_mapped_collection
.. autoclass:: collection
.. autoclass:: sqlalchemy.orm.collections.MappedCollection
:members:
.. autofunction:: collection_adapter
.. autofunction:: column_mapped_collection
.. autofunction:: mapped_collection
+16
View File
@@ -0,0 +1,16 @@
.. _sqlalchemy_orm_toplevel:
sqlalchemy.orm
==============
.. toctree::
:glob:
mapping
collections
query
sessions
interfaces
utilities
+7
View File
@@ -0,0 +1,7 @@
Interfaces
==========
.. automodule:: sqlalchemy.orm.interfaces
:members: AttributeExtension, InstrumentationManager, MapperExtension, PropComparator, SessionExtension
:undoc-members:
+91
View File
@@ -0,0 +1,91 @@
Class Mapping
=============
.. module:: sqlalchemy.orm
Defining Mappings
-----------------
Python classes are mapped to the database using the :func:`mapper` function.
.. autofunction:: mapper
Mapper Properties
-----------------
A basic mapping of a class will simply make the columns of the
database table or selectable available as attributes on the class.
**Mapper properties** allow you to customize and add additional
properties to your classes, for example making the results one-to-many
join available as a Python list of :func:`related <relation>` objects.
Mapper properties are most commonly included in the :func:`mapper`
call::
mapper(Parent, properties={
'children': relation(Children)
}
.. autofunction:: backref
.. autofunction:: column_property
.. autofunction:: comparable_property
.. autofunction:: composite
.. autofunction:: deferred
.. autofunction:: dynamic_loader
.. autofunction:: relation
.. autofunction:: synonym
Decorators
----------
.. autofunction:: reconstructor
.. autofunction:: validates
Utilities
---------
.. autofunction:: object_mapper
.. autofunction:: class_mapper
.. autofunction:: compile_mappers
.. autofunction:: clear_mappers
Attribute Utilities
-------------------
.. autofunction:: sqlalchemy.orm.attributes.del_attribute
.. autofunction:: sqlalchemy.orm.attributes.get_attribute
.. autofunction:: sqlalchemy.orm.attributes.get_history
.. autofunction:: sqlalchemy.orm.attributes.init_collection
.. function:: sqlalchemy.orm.attributes.instance_state
Return the :class:`InstanceState` for a given object.
.. autofunction:: sqlalchemy.orm.attributes.is_instrumented
.. function:: sqlalchemy.orm.attributes.manager_of_class
Return the :class:`ClassManager` for a given class.
.. autofunction:: sqlalchemy.orm.attributes.set_attribute
.. autofunction:: sqlalchemy.orm.attributes.set_committed_value
Internals
---------
.. autoclass:: sqlalchemy.orm.mapper.Mapper
:members:
+48
View File
@@ -0,0 +1,48 @@
.. _query_api_toplevel:
Querying
========
.. module:: sqlalchemy.orm
The Query Object
----------------
:class:`~sqlalchemy.orm.query.Query` is produced in terms of a given :class:`~sqlalchemy.orm.session.Session`, using the :func:`~sqlalchemy.orm.query.Query.query` function::
q = session.query(SomeMappedClass)
Following is the full interface for the :class:`Query` object.
.. autoclass:: sqlalchemy.orm.query.Query
:members:
:undoc-members:
ORM-Specific Query Constructs
-----------------------------
.. autoclass:: aliased
.. autofunction:: join
.. autofunction:: outerjoin
Query Options
-------------
Options which are passed to ``query.options()``, to affect the behavior of loading.
.. autofunction:: contains_eager
.. autofunction:: defer
.. autofunction:: eagerload
.. autofunction:: eagerload_all
.. autofunction:: extension
.. autofunction:: lazyload
.. autofunction:: undefer
+17
View File
@@ -0,0 +1,17 @@
Sessions
========
.. module:: sqlalchemy.orm
.. autofunction:: create_session
.. autofunction:: scoped_session
.. autofunction:: sessionmaker
.. autoclass:: sqlalchemy.orm.session.Session
:members:
.. autoclass:: sqlalchemy.orm.scoping.ScopedSession
:members:
+6
View File
@@ -0,0 +1,6 @@
Utilities
=========
.. automodule:: sqlalchemy.orm.util
:members: identity_key, Validator, with_parent, polymorphic_union
:undoc-members:
+63
View File
@@ -0,0 +1,63 @@
Connections
===========
Creating Engines
----------------
.. autofunction:: sqlalchemy.create_engine
.. autofunction:: sqlalchemy.engine_from_config
.. autoclass:: sqlalchemy.engine.url.URL
:members:
Connectables
------------
.. currentmodule:: sqlalchemy.engine.base
.. autoclass:: Engine
:members:
.. autoclass:: Connection
:members:
.. autoclass:: Connectable
:members:
:undoc-members:
Result Objects
--------------
.. autoclass:: sqlalchemy.engine.base.ResultProxy
:members:
.. autoclass:: sqlalchemy.engine.base.RowProxy
:members:
Transactions
------------
.. autoclass:: Transaction
:members:
:undoc-members:
Internals
---------
.. autofunction:: connection_memoize
.. autoclass:: Dialect
:members:
.. autoclass:: sqlalchemy.engine.default.DefaultDialect
:members:
:show-inheritance:
.. autoclass:: sqlalchemy.engine.default.DefaultExecutionContext
:members:
:show-inheritance:
.. autoclass:: ExecutionContext
:members:
+207
View File
@@ -0,0 +1,207 @@
SQL Statements and Expressions
==============================
.. module:: sqlalchemy.sql.expression
Functions
---------
The expression package uses functions to construct SQL expressions. The return value of each function is an object instance which is a subclass of :class:`~sqlalchemy.sql.expression.ClauseElement`.
.. autofunction:: alias
.. autofunction:: and_
.. autofunction:: asc
.. autofunction:: between
.. autofunction:: bindparam
.. autofunction:: case
.. autofunction:: cast
.. autofunction:: column
.. autofunction:: collate
.. autofunction:: delete
.. autofunction:: desc
.. autofunction:: distinct
.. autofunction:: except_
.. autofunction:: except_all
.. autofunction:: exists
.. autofunction:: extract
.. attribute:: func
Generate SQL function expressions.
``func`` is a special object instance which generates SQL functions based on name-based attributes, e.g.::
>>> print func.count(1)
count(:param_1)
Any name can be given to `func`. If the function name is unknown to SQLAlchemy, it will be rendered exactly as is. For common SQL functions which SQLAlchemy is aware of, the name may be interpreted as a *generic function* which will be compiled appropriately to the target database::
>>> print func.current_timestamp()
CURRENT_TIMESTAMP
To call functions which are present in dot-separated packages, specify them in the same manner::
>>> print func.stats.yield_curve(5, 10)
stats.yield_curve(:yield_curve_1, :yield_curve_2)
SQLAlchemy can be made aware of the return type of functions to enable type-specific lexical and result-based behavior. For example, to ensure that a string-based function returns a Unicode value and is similarly treated as a string in expressions, specify :class:`~sqlalchemy.types.Unicode` as the type:
>>> print func.my_string(u'hi', type_=Unicode) + ' ' + \
... func.my_string(u'there', type_=Unicode)
my_string(:my_string_1) || :my_string_2 || my_string(:my_string_3)
Functions which are interpreted as "generic" functions know how to calculate their return type automatically. For a listing of known generic functions, see :ref:`generic_functions`.
.. autofunction:: insert
.. autofunction:: intersect
.. autofunction:: intersect_all
.. autofunction:: join
.. autofunction:: label
.. autofunction:: literal
.. autofunction:: literal_column
.. autofunction:: not_
.. autofunction:: null
.. autofunction:: or_
.. autofunction:: outparam
.. autofunction:: outerjoin
.. autofunction:: select
.. autofunction:: subquery
.. autofunction:: table
.. autofunction:: text
.. autofunction:: tuple_
.. autofunction:: union
.. autofunction:: union_all
.. autofunction:: update
Classes
-------
.. autoclass:: Alias
:members:
:show-inheritance:
.. autoclass:: _BindParamClause
:members:
:show-inheritance:
.. autoclass:: ClauseElement
:members:
:show-inheritance:
.. autoclass:: ColumnClause
:members:
:show-inheritance:
.. autoclass:: ColumnCollection
:members:
:show-inheritance:
.. autoclass:: ColumnElement
:members:
:show-inheritance:
.. autoclass:: _CompareMixin
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: ColumnOperators
:members:
:undoc-members:
:inherited-members:
.. autoclass:: CompoundSelect
:members:
:show-inheritance:
.. autoclass:: Delete
:members: where
:show-inheritance:
.. autoclass:: FunctionElement
:members:
:show-inheritance:
.. autoclass:: Function
:members:
:show-inheritance:
.. autoclass:: FromClause
:members:
:show-inheritance:
.. autoclass:: Insert
:members: prefix_with, values
:show-inheritance:
.. autoclass:: Join
:members:
:show-inheritance:
.. autoclass:: Select
:members:
:show-inheritance:
.. autoclass:: Selectable
:members:
:show-inheritance:
.. autoclass:: _SelectBaseMixin
:members:
:show-inheritance:
.. autoclass:: TableClause
:members:
:show-inheritance:
.. autoclass:: Update
:members: where, values
:show-inheritance:
.. _generic_functions:
Generic Functions
-----------------
SQL functions which are known to SQLAlchemy with regards to database-specific rendering, return types and argument behavior. Generic functions are invoked like all SQL functions, using the :attr:`func` attribute::
select([func.count()]).select_from(sometable)
.. automodule:: sqlalchemy.sql.functions
:members:
:undoc-members:
:show-inheritance:
+14
View File
@@ -0,0 +1,14 @@
sqlalchemy
==========
.. toctree::
:glob:
connections
pooling
expressions
schema
types
interfaces
+6
View File
@@ -0,0 +1,6 @@
Interfaces
----------
.. automodule:: sqlalchemy.interfaces
:members:
:undoc-members:
+153
View File
@@ -0,0 +1,153 @@
.. _pooling_toplevel:
Connection Pooling
==================
.. module:: sqlalchemy.pool
SQLAlchemy ships with a connection pooling framework that integrates
with the Engine system and can also be used on its own to manage plain
DB-API connections.
At the base of any database helper library is a system for efficiently
acquiring connections to the database. Since the establishment of a
database connection is typically a somewhat expensive operation, an
application needs a way to get at database connections repeatedly
without incurring the full overhead each time. Particularly for
server-side web applications, a connection pool is the standard way to
maintain a group or "pool" of active database connections which are
reused from request to request in a single server process.
Connection Pool Configuration
-----------------------------
The :class:`~sqlalchemy.engine.Engine` returned by the
:func:`~sqlalchemy.create_engine` function in most cases has a :class:`QueuePool`
integrated, pre-configured with reasonable pooling defaults. If
you're reading this section to simply enable pooling- congratulations!
You're already done.
The most common :class:`QueuePool` tuning parameters can be passed
directly to :func:`~sqlalchemy.create_engine` as keyword arguments:
``pool_size``, ``max_overflow``, ``pool_recycle`` and
``pool_timeout``. For example::
engine = create_engine('postgresql://me@localhost/mydb',
pool_size=20, max_overflow=0)
In the case of SQLite, a :class:`SingletonThreadPool` is provided instead,
to provide compatibility with SQLite's restricted threading model.
Custom Pool Construction
------------------------
:class:`Pool` instances may be created directly for your own use or to
supply to :func:`sqlalchemy.create_engine` via the ``pool=``
keyword argument.
Constructing your own pool requires supplying a callable function the
Pool can use to create new connections. The function will be called
with no arguments.
Through this method, custom connection schemes can be made, such as a
using connections from another library's pool, or making a new
connection that automatically executes some initialization commands::
import sqlalchemy.pool as pool
import psycopg2
def getconn():
c = psycopg2.connect(username='ed', host='127.0.0.1', dbname='test')
# execute an initialization function on the connection before returning
c.cursor.execute("setup_encodings()")
return c
p = pool.QueuePool(getconn, max_overflow=10, pool_size=5)
Or with SingletonThreadPool::
import sqlalchemy.pool as pool
import sqlite
p = pool.SingletonThreadPool(lambda: sqlite.connect(filename='myfile.db'))
Builtin Pool Implementations
----------------------------
.. autoclass:: AssertionPool
:members:
:show-inheritance:
.. autoclass:: NullPool
:members:
:show-inheritance:
.. autoclass:: sqlalchemy.pool.Pool
:members:
:show-inheritance:
:undoc-members:
:inherited-members:
.. autoclass:: sqlalchemy.pool.QueuePool
:members:
:show-inheritance:
.. autoclass:: SingletonThreadPool
:members:
:show-inheritance:
.. autoclass:: StaticPool
:members:
:show-inheritance:
Pooling Plain DB-API Connections
--------------------------------
Any :pep:`249` DB-API module can be "proxied" through the connection
pool transparently. Usage of the DB-API is exactly as before, except
the ``connect()`` method will consult the pool. Below we illustrate
this with ``psycopg2``::
import sqlalchemy.pool as pool
import psycopg2 as psycopg
psycopg = pool.manage(psycopg)
# then connect normally
connection = psycopg.connect(database='test', username='scott',
password='tiger')
This produces a :class:`_DBProxy` object which supports the same
``connect()`` function as the original DB-API module. Upon
connection, a connection proxy object is returned, which delegates its
calls to a real DB-API connection object. This connection object is
stored persistently within a connection pool (an instance of
:class:`Pool`) that corresponds to the exact connection arguments sent
to the ``connect()`` function.
The connection proxy supports all of the methods on the original
connection object, most of which are proxied via ``__getattr__()``.
The ``close()`` method will return the connection to the pool, and the
``cursor()`` method will return a proxied cursor object. Both the
connection proxy and the cursor proxy will also return the underlying
connection to the pool after they have both been garbage collected,
which is detected via weakref callbacks (``__del__`` is not used).
Additionally, when connections are returned to the pool, a
``rollback()`` is issued on the connection unconditionally. This is
to release any locks still held by the connection that may have
resulted from normal activity.
By default, the ``connect()`` method will return the same connection
that is already checked out in the current thread. This allows a
particular connection to be used in a given thread without needing to
pass it around between functions. To disable this behavior, specify
``use_threadlocal=False`` to the ``manage()`` function.
.. autofunction:: sqlalchemy.pool.manage
.. autofunction:: sqlalchemy.pool.clear_managers
+169
View File
@@ -0,0 +1,169 @@
.. _schema_api_toplevel:
Database Schema
===============
.. module:: sqlalchemy.schema
SQLAlchemy schema definition language. For more usage examples, see :ref:`metadata_toplevel`.
Tables and Columns
------------------
.. autoclass:: Column
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: MetaData
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: Table
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: ThreadLocalMetaData
:members:
:undoc-members:
:show-inheritance:
Constraints
-----------
.. autoclass:: CheckConstraint
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: Constraint
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: ForeignKey
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: ForeignKeyConstraint
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: Index
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: PrimaryKeyConstraint
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: UniqueConstraint
:members:
:undoc-members:
:show-inheritance:
Default Generators and Markers
------------------------------
.. autoclass:: ColumnDefault
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: DefaultClause
:undoc-members:
:show-inheritance:
.. autoclass:: DefaultGenerator
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: FetchedValue
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: PassiveDefault
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: Sequence
:members:
:undoc-members:
:show-inheritance:
.. _schema_api_ddl:
DDL Generation
--------------
.. autoclass:: DDLElement
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: DDL
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: CreateTable
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: DropTable
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: CreateSequence
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: DropSequence
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: CreateIndex
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: DropIndex
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: AddConstraint
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: DropConstraint
:members:
:undoc-members:
:show-inheritance:
Internals
---------
.. autoclass:: SchemaItem
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: SchemaVisitor
:members:
:undoc-members:
:show-inheritance:
+267
View File
@@ -0,0 +1,267 @@
.. _types:
Column and Data Types
=====================
.. module:: sqlalchemy.types
SQLAlchemy provides abstractions for most common database data types,
and a mechanism for specifying your own custom data types.
The methods and attributes of type objects are rarely used directly.
Type objects are supplied to :class:`~sqlalchemy.Table` definitions
and can be supplied as type hints to `functions` for occasions where
the database driver returns an incorrect type.
.. code-block:: pycon
>>> users = Table('users', metadata,
... Column('id', Integer, primary_key=True)
... Column('login', String(32))
... )
SQLAlchemy will use the ``Integer`` and ``String(32)`` type
information when issuing a ``CREATE TABLE`` statement and will use it
again when reading back rows ``SELECTed`` from the database.
Functions that accept a type (such as :func:`~sqlalchemy.Column`) will
typically accept a type class or instance; ``Integer`` is equivalent
to ``Integer()`` with no construction arguments in this case.
Generic Types
-------------
Generic types specify a column that can read, write and store a
particular type of Python data. SQLAlchemy will choose the best
database column type available on the target database when issuing a
``CREATE TABLE`` statement. For complete control over which column
type is emitted in ``CREATE TABLE``, such as ``VARCHAR`` see `SQL
Standard Types`_ and the other sections of this chapter.
.. autoclass:: Boolean
:show-inheritance:
.. autoclass:: Date
:show-inheritance:
.. autoclass:: DateTime
:show-inheritance:
.. autoclass:: Enum
:show-inheritance:
:members:
.. autoclass:: Float
:show-inheritance:
:members:
.. autoclass:: Integer
:show-inheritance:
.. autoclass:: Interval
:show-inheritance:
.. autoclass:: LargeBinary
:show-inheritance:
.. autoclass:: Numeric
:show-inheritance:
:members:
.. autoclass:: PickleType
:show-inheritance:
.. autoclass:: SchemaType
:show-inheritance:
:members:
:undoc-members:
.. autoclass:: SmallInteger
:show-inheritance:
.. autoclass:: String
:show-inheritance:
.. autoclass:: Text
:show-inheritance:
.. autoclass:: Time
:show-inheritance:
.. autoclass:: Unicode
:show-inheritance:
.. autoclass:: UnicodeText
:show-inheritance:
SQL Standard Types
------------------
The SQL standard types always create database column types of the same
name when ``CREATE TABLE`` is issued. Some types may not be supported
on all databases.
.. autoclass:: BINARY
:show-inheritance:
.. autoclass:: BLOB
:show-inheritance:
.. autoclass:: BOOLEAN
:show-inheritance:
.. autoclass:: CHAR
:show-inheritance:
.. autoclass:: CLOB
:show-inheritance:
.. autoclass:: DATE
:show-inheritance:
.. autoclass:: DATETIME
:show-inheritance:
.. autoclass:: DECIMAL
:show-inheritance:
.. autoclass:: FLOAT
:show-inheritance:
.. autoclass:: INT
:show-inheritance:
.. autoclass:: sqlalchemy.types.INTEGER
:show-inheritance:
.. autoclass:: NCHAR
:show-inheritance:
.. autoclass:: NUMERIC
:show-inheritance:
.. autoclass:: SMALLINT
:show-inheritance:
.. autoclass:: TEXT
:show-inheritance:
.. autoclass:: TIME
:show-inheritance:
.. autoclass:: TIMESTAMP
:show-inheritance:
.. autoclass:: VARBINARY
:show-inheritance:
.. autoclass:: VARCHAR
:show-inheritance:
Vendor-Specific Types
---------------------
Database-specific types are also available for import from each
database's dialect module. See the :ref:`sqlalchemy.dialects`
reference for the database you're interested in.
For example, MySQL has a ``BIGINTEGER`` type and PostgreSQL has an
``INET`` type. To use these, import them from the module explicitly::
from sqlalchemy.dialects import mysql
table = Table('foo', meta,
Column('id', mysql.BIGINTEGER),
Column('enumerates', mysql.ENUM('a', 'b', 'c'))
)
Or some PostgreSQL types::
from sqlalchemy.dialects import postgresql
table = Table('foo', meta,
Column('ipaddress', postgresql.INET),
Column('elements', postgresql.ARRAY(str))
)
Each dialect provides the full set of typenames supported by
that backend within its `__all__` collection, so that a simple
`import *` or similar will import all supported types as
implemented for that backend::
from sqlalchemy.dialects.postgresql import *
t = Table('mytable', metadata,
Column('id', INTEGER, primary_key=True),
Column('name', VARCHAR(300)),
Column('inetaddr', INET)
)
Where above, the INTEGER and VARCHAR types are ultimately from
sqlalchemy.types, and INET is specific to the Postgresql dialect.
Some dialect level types have the same name as the SQL standard type,
but also provide additional arguments. For example, MySQL implements
the full range of character and string types including additional arguments
such as `collation` and `charset`::
from sqlalchemy.dialects.mysql import VARCHAR, TEXT
table = Table('foo', meta,
Column('col1', VARCHAR(200, collation='binary')),
Column('col2', TEXT(charset='latin1'))
)
Custom Types
------------
User-defined types may be created to match special capabilities of a
particular database or simply for implementing custom processing logic
in Python.
The simplest method is implementing a :class:`TypeDecorator`, a helper
class that makes it easy to augment the bind parameter and result
processing capabilities of one of the built in types.
To build a type object from scratch, subclass `:class:UserDefinedType`.
.. autoclass:: TypeDecorator
:members:
:undoc-members:
:inherited-members:
:show-inheritance:
.. autoclass:: UserDefinedType
:members:
:undoc-members:
:inherited-members:
:show-inheritance:
.. autoclass:: TypeEngine
:members:
:undoc-members:
:inherited-members:
:show-inheritance:
.. autoclass:: AbstractType
:members:
:undoc-members:
:inherited-members:
:show-inheritance:
.. autoclass:: MutableType
:members:
:undoc-members:
:inherited-members:
:show-inheritance:
.. autoclass:: Concatenable
:members:
:undoc-members:
:inherited-members:
:show-inheritance:
.. autoclass:: NullType
:show-inheritance:
+708
View File
@@ -0,0 +1,708 @@
.. _session_toplevel:
=================
Using the Session
=================
The `Mapper` is the entrypoint to the configurational API of the SQLAlchemy object relational mapper. But the primary object one works with when using the ORM is the :class:`~sqlalchemy.orm.session.Session`.
What does the Session do ?
==========================
In the most general sense, the ``Session`` establishes all conversations with the database and represents a "holding zone" for all the mapped instances which you've loaded or created during its lifespan. It implements the `Unit of Work <http://martinfowler.com/eaaCatalog/unitOfWork.html>`_ pattern, which means it keeps track of all changes which occur, and is capable of **flushing** those changes to the database as appropriate. Another important facet of the ``Session`` is that it's also maintaining **unique** copies of each instance, where "unique" means "only one object with a particular primary key" - this pattern is called the `Identity Map <http://martinfowler.com/eaaCatalog/identityMap.html>`_.
Beyond that, the ``Session`` implements an interface which lets you move objects in or out of the session in a variety of ways, it provides the entryway to a ``Query`` object which is used to query the database for data, and it also provides a transactional context for SQL operations which rides on top of the transactional capabilities of ``Engine`` and ``Connection`` objects.
Getting a Session
=================
``Session`` is a regular Python class which can be directly instantiated. However, to standardize how sessions are configured and acquired, the ``sessionmaker()`` function is normally used to create a top level ``Session`` configuration which can then be used throughout an application without the need to repeat the configurational arguments.
Using a sessionmaker() Configuration
------------------------------------
The usage of ``sessionmaker()`` is illustrated below:
.. sourcecode:: python+sql
from sqlalchemy.orm import sessionmaker
# create a configured "Session" class
Session = sessionmaker(bind=some_engine)
# create a Session
session = Session()
# work with sess
myobject = MyObject('foo', 'bar')
session.add(myobject)
session.commit()
# close when finished
session.close()
Above, the ``sessionmaker`` call creates a class for us, which we assign to the name ``Session``. This class is a subclass of the actual ``sqlalchemy.orm.session.Session`` class, which will instantiate with a particular bound engine.
When you write your application, place the call to ``sessionmaker()`` somewhere global, and then make your new ``Session`` class available to the rest of your application.
Binding Session to an Engine
----------------------------
In our previous example regarding ``sessionmaker()``, we specified a ``bind`` for a particular ``Engine``. If we'd like to construct a ``sessionmaker()`` without an engine available and bind it later on, or to specify other options to an existing ``sessionmaker()``, we may use the ``configure()`` method::
# configure Session class with desired options
Session = sessionmaker()
# later, we create the engine
engine = create_engine('postgresql://...')
# associate it with our custom Session class
Session.configure(bind=engine)
# work with the session
session = Session()
It's actually entirely optional to bind a Session to an engine. If the underlying mapped ``Table`` objects use "bound" metadata, the ``Session`` will make use of the bound engine instead (or will even use multiple engines if multiple binds are present within the mapped tables). "Bound" metadata is described at :ref:`metadata_binding`.
The ``Session`` also has the ability to be bound to multiple engines explicitly. Descriptions of these scenarios are described in :ref:`session_partitioning`.
Binding Session to a Connection
-------------------------------
The ``Session`` can also be explicitly bound to an individual database ``Connection``. Reasons for doing this may include to join a ``Session`` with an ongoing transaction local to a specific ``Connection`` object, or to bypass connection pooling by just having connections persistently checked out and associated with distinct, long running sessions::
# global application scope. create Session class, engine
Session = sessionmaker()
engine = create_engine('postgresql://...')
...
# local scope, such as within a controller function
# connect to the database
connection = engine.connect()
# bind an individual Session to the connection
session = Session(bind=connection)
Using create_session()
----------------------
As an alternative to ``sessionmaker()``, ``create_session()`` is a function which calls the normal ``Session`` constructor directly. All arguments are passed through and the new ``Session`` object is returned::
session = create_session(bind=myengine, autocommit=True, autoflush=False)
Note that ``create_session()`` disables all optional "automation" by default. Called with no arguments, the session produced is not autoflushing, does not auto-expire, and does not maintain a transaction (i.e. it begins and commits a new transaction for each ``flush()``). SQLAlchemy uses ``create_session()`` extensively within its own unit tests.
Configurational Arguments
-------------------------
Configurational arguments accepted by ``sessionmaker()`` and ``create_session()`` are the same as that of the ``Session`` class itself, and are described at :func:`sqlalchemy.orm.sessionmaker`.
Note that the defaults of ``create_session()`` are the opposite of that of ``sessionmaker()``: autoflush and expire_on_commit are False, autocommit is True. It is recommended to use the ``sessionmaker()`` function instead of ``create_session()``. ``create_session()`` is used to get a session with no automation turned on and is useful for testing.
Using the Session
==================
Quickie Intro to Object States
------------------------------
It's helpful to know the states which an instance can have within a session:
* *Transient* - an instance that's not in a session, and is not saved to the database; i.e. it has no database identity. The only relationship such an object has to the ORM is that its class has a ``mapper()`` associated with it.
* *Pending* - when you ``add()`` a transient instance, it becomes pending. It still wasn't actually flushed to the database yet, but it will be when the next flush occurs.
* *Persistent* - An instance which is present in the session and has a record in the database. You get persistent instances by either flushing so that the pending instances become persistent, or by querying the database for existing instances (or moving persistent instances from other sessions into your local session).
* *Detached* - an instance which has a record in the database, but is not in any session. There's nothing wrong with this, and you can use objects normally when they're detached, **except** they will not be able to issue any SQL in order to load collections or attributes which are not yet loaded, or were marked as "expired".
Knowing these states is important, since the ``Session`` tries to be strict about ambiguous operations (such as trying to save the same object to two different sessions at the same time).
Frequently Asked Questions
--------------------------
* When do I make a ``sessionmaker`` ?
Just one time, somewhere in your application's global scope. It should be looked upon as part of your application's configuration. If your application has three .py files in a package, you could, for example, place the ``sessionmaker`` line in your ``__init__.py`` file; from that point on your other modules say "from mypackage import Session". That way, everyone else just uses ``Session()``, and the configuration of that session is controlled by that central point.
If your application starts up, does imports, but does not know what database it's going to be connecting to, you can bind the ``Session`` at the "class" level to the engine later on, using ``configure()``.
In the examples in this section, we will frequently show the ``sessionmaker`` being created right above the line where we actually invoke ``Session()``. But that's just for example's sake ! In reality, the ``sessionmaker`` would be somewhere at the module level, and your individual ``Session()`` calls would be sprinkled all throughout your app, such as in a web application within each controller method.
* When do I make a ``Session`` ?
You typically invoke ``Session()`` when you first need to talk to your database, and want to save some objects or load some existing ones. Then, you work with it, save your changes, and then dispose of it....or at the very least ``close()`` it. It's not a "global" kind of object, and should be handled more like a "local variable", as it's generally **not** safe to use with concurrent threads. Sessions are very inexpensive to make, and don't use any resources whatsoever until they are first used...so create some !
There is also a pattern whereby you're using a **contextual session**, this is described later in :ref:`unitofwork_contextual`. In this pattern, a helper object is maintaining a ``Session`` for you, most commonly one that is local to the current thread (and sometimes also local to an application instance). SQLAlchemy has worked this pattern out such that it still *looks* like you're creating a new session as you need one...so in that case, it's still a guaranteed win to just say ``Session()`` whenever you want a session.
* Is the Session a cache ?
Yeee...no. It's somewhat used as a cache, in that it implements the identity map pattern, and stores objects keyed to their primary key. However, it doesn't do any kind of query caching. This means, if you say ``session.query(Foo).filter_by(name='bar')``, even if ``Foo(name='bar')`` is right there, in the identity map, the session has no idea about that. It has to issue SQL to the database, get the rows back, and then when it sees the primary key in the row, *then* it can look in the local identity map and see that the object is already there. It's only when you say ``query.get({some primary key})`` that the ``Session`` doesn't have to issue a query.
Additionally, the Session stores object instances using a weak reference by default. This also defeats the purpose of using the Session as a cache, unless the ``weak_identity_map`` flag is set to ``False``.
The ``Session`` is not designed to be a global object from which everyone consults as a "registry" of objects. That is the job of a **second level cache**. A good library for implementing second level caching is `Memcached <http://www.danga.com/memcached/>`_. It *is* possible to "sort of" use the ``Session`` in this manner, if you set it to be non-transactional and it never flushes any SQL, but it's not a terrific solution, since if concurrent threads load the same objects at the same time, you may have multiple copies of the same objects present in collections.
* How can I get the ``Session`` for a certain object ?
Use the ``object_session()`` classmethod available on ``Session``::
session = Session.object_session(someobject)
.. index::
single: thread safety; sessions
single: thread safety; Session
* Is the session thread-safe?
Nope. It has no thread synchronization of any kind built in, and particularly when you do a flush operation, it definitely is not open to concurrent threads accessing it, because it holds onto a single database connection at that point. If you use a session which is non-transactional for read operations only, it's still not thread-"safe", but you also wont get any catastrophic failures either, since it opens and closes connections on an as-needed basis; it's just that different threads might load the same objects independently of each other, but only one will wind up in the identity map (however, the other one might still live in a collection somewhere).
But the bigger point here is, you should not *want* to use the session with multiple concurrent threads. That would be like having everyone at a restaurant all eat from the same plate. The session is a local "workspace" that you use for a specific set of tasks; you don't want to, or need to, share that session with other threads who are doing some other task. If, on the other hand, there are other threads participating in the same task you are, such as in a desktop graphical application, then you would be sharing the session with those threads, but you also will have implemented a proper locking scheme (or your graphical framework does) so that those threads do not collide.
Querying
--------
The ``query()`` function takes one or more *entities* and returns a new ``Query`` object which will issue mapper queries within the context of this Session. An entity is defined as a mapped class, a ``Mapper`` object, an orm-enabled *descriptor*, or an ``AliasedClass`` object::
# query from a class
session.query(User).filter_by(name='ed').all()
# query with multiple classes, returns tuples
session.query(User, Address).join('addresses').filter_by(name='ed').all()
# query using orm-enabled descriptors
session.query(User.name, User.fullname).all()
# query from a mapper
user_mapper = class_mapper(User)
session.query(user_mapper)
When ``Query`` returns results, each object instantiated is stored within the identity map. When a row matches an object which is already present, the same object is returned. In the latter case, whether or not the row is populated onto an existing object depends upon whether the attributes of the instance have been *expired* or not. A default-configured ``Session`` automatically expires all instances along transaction boundaries, so that with a normally isolated transaction, there shouldn't be any issue of instances representing data which is stale with regards to the current transaction.
Adding New or Existing Items
----------------------------
``add()`` is used to place instances in the session. For *transient* (i.e. brand new) instances, this will have the effect of an INSERT taking place for those instances upon the next flush. For instances which are *persistent* (i.e. were loaded by this session), they are already present and do not need to be added. Instances which are *detached* (i.e. have been removed from a session) may be re-associated with a session using this method::
user1 = User(name='user1')
user2 = User(name='user2')
session.add(user1)
session.add(user2)
session.commit() # write changes to the database
To add a list of items to the session at once, use ``add_all()``::
session.add_all([item1, item2, item3])
The ``add()`` operation **cascades** along the ``save-update`` cascade. For more details see the section :ref:`unitofwork_cascades`.
Merging
-------
``merge()`` reconciles the current state of an instance and its associated children with existing data in the database, and returns a copy of the instance associated with the session. Usage is as follows::
merged_object = session.merge(existing_object)
When given an instance, it follows these steps:
* It examines the primary key of the instance. If it's present, it attempts to load an instance with that primary key (or pulls from the local identity map).
* If there's no primary key on the given instance, or the given primary key does not exist in the database, a new instance is created.
* The state of the given instance is then copied onto the located/newly created instance.
* The operation is cascaded to associated child items along the ``merge`` cascade. Note that all changes present on the given instance, including changes to collections, are merged.
* The new instance is returned.
With ``merge()``, the given instance is not placed within the session, and can be associated with a different session or detached. ``merge()`` is very useful for taking the state of any kind of object structure without regard for its origins or current session associations and placing that state within a session. Here's two examples:
* An application which reads an object structure from a file and wishes to save it to the database might parse the file, build up the structure, and then use ``merge()`` to save it to the database, ensuring that the data within the file is used to formulate the primary key of each element of the structure. Later, when the file has changed, the same process can be re-run, producing a slightly different object structure, which can then be ``merged()`` in again, and the ``Session`` will automatically update the database to reflect those changes.
* A web application stores mapped entities within an HTTP session object. When each request starts up, the serialized data can be merged into the session, so that the original entity may be safely shared among requests and threads.
``merge()`` is frequently used by applications which implement their own second level caches. This refers to an application which uses an in memory dictionary, or an tool like Memcached to store objects over long running spans of time. When such an object needs to exist within a ``Session``, ``merge()`` is a good choice since it leaves the original cached object untouched. For this use case, merge provides a keyword option called ``load=False``. When this boolean flag is set to ``False``, ``merge()`` will not issue any SQL to reconcile the given object against the current state of the database, thereby reducing query overhead. The limitation is that the given object and all of its children may not contain any pending changes, and it's also of course possible that newer information in the database will not be present on the merged object, since no load is issued.
Deleting
--------
The ``delete`` method places an instance into the Session's list of objects to be marked as deleted::
# mark two objects to be deleted
session.delete(obj1)
session.delete(obj2)
# commit (or flush)
session.commit()
The big gotcha with ``delete()`` is that **nothing is removed from collections**. Such as, if a ``User`` has a collection of three ``Addresses``, deleting an ``Address`` will not remove it from ``user.addresses``::
>>> address = user.addresses[1]
>>> session.delete(address)
>>> session.flush()
>>> address in user.addresses
True
The solution is to use proper cascading::
mapper(User, users_table, properties={
'addresses':relation(Address, cascade="all, delete, delete-orphan")
})
del user.addresses[1]
session.flush()
Deleting based on Filter Criterion
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The caveat with ``Session.delete()`` is that you need to have an object handy already in order to delete. The Query includes a ``delete()`` method which deletes based on filtering criteria::
session.query(User).filter(User.id==7).delete()
The ``Query.delete()`` method includes functionality to "expire" objects already in the session which
match the criteria. However it does have some caveats, including that "delete" and "delete-orphan"
cascades won't be fully expressed for collections which are already loaded. See the API docs for :meth:`~sqlalchemy.orm.query.Query.delete` for more details.
Flushing
--------
When the ``Session`` is used with its default configuration, the flush step is nearly always done transparently. Specifically, the flush occurs before any individual ``Query`` is issued, as well as within the ``commit()`` call before the transaction is committed. It also occurs before a SAVEPOINT is issued when ``begin_nested()`` is used.
Regardless of the autoflush setting, a flush can always be forced by issuing ``flush()``::
session.flush()
The "flush-on-Query" aspect of the behavior can be disabled by constructing ``sessionmaker()`` with the flag ``autoflush=False``::
Session = sessionmaker(autoflush=False)
Additionally, autoflush can be temporarily disabled by setting the ``autoflush`` flag at any time::
mysession = Session()
mysession.autoflush = False
Some autoflush-disable recipes are available at `DisableAutoFlush <http://www.sqlalchemy.org/trac/wiki/UsageRecipes/DisableAutoflush>`_.
The flush process *always* occurs within a transaction, even if the ``Session`` has been configured with ``autocommit=True``, a setting that disables the session's persistent transactional state. If no transaction is present, ``flush()`` creates its own transaction and commits it. Any failures during flush will always result in a rollback of whatever transaction is present. If the Session is not in ``autocommit=True`` mode, an explicit call to ``rollback()`` is required after a flush fails, even though the underlying transaction will have been rolled back already - this is so that the overall nesting pattern of so-called "subtransactions" is consistently maintained.
Committing
----------
``commit()`` is used to commit the current transaction. It always issues ``flush()`` beforehand to flush any remaining state to the database; this is independent of the "autoflush" setting. If no transaction is present, it raises an error. Note that the default behavior of the ``Session`` is that a transaction is always present; this behavior can be disabled by setting ``autocommit=True``. In autocommit mode, a transaction can be initiated by calling the ``begin()`` method.
Another behavior of ``commit()`` is that by default it expires the state of all instances present after the commit is complete. This is so that when the instances are next accessed, either through attribute access or by them being present in a ``Query`` result set, they receive the most recent state. To disable this behavior, configure ``sessionmaker()`` with ``expire_on_commit=False``.
Normally, instances loaded into the ``Session`` are never changed by subsequent queries; the assumption is that the current transaction is isolated so the state most recently loaded is correct as long as the transaction continues. Setting ``autocommit=True`` works against this model to some degree since the ``Session`` behaves in exactly the same way with regard to attribute state, except no transaction is present.
Rolling Back
------------
``rollback()`` rolls back the current transaction. With a default configured session, the post-rollback state of the session is as follows:
* All connections are rolled back and returned to the connection pool, unless the Session was bound directly to a Connection, in which case the connection is still maintained (but still rolled back).
* Objects which were initially in the *pending* state when they were added to the ``Session`` within the lifespan of the transaction are expunged, corresponding to their INSERT statement being rolled back. The state of their attributes remains unchanged.
* Objects which were marked as *deleted* within the lifespan of the transaction are promoted back to the *persistent* state, corresponding to their DELETE statement being rolled back. Note that if those objects were first *pending* within the transaction, that operation takes precedence instead.
* All objects not expunged are fully expired.
With that state understood, the ``Session`` may safely continue usage after a rollback occurs.
When a ``flush()`` fails, typically for reasons like primary key, foreign key, or "not nullable" constraint violations, a ``rollback()`` is issued automatically (it's currently not possible for a flush to continue after a partial failure). However, the flush process always uses its own transactional demarcator called a *subtransaction*, which is described more fully in the docstrings for ``Session``. What it means here is that even though the database transaction has been rolled back, the end user must still issue ``rollback()`` to fully reset the state of the ``Session``.
Expunging
---------
Expunge removes an object from the Session, sending persistent instances to the detached state, and pending instances to the transient state:
.. sourcecode:: python+sql
session.expunge(obj1)
To remove all items, call ``session.expunge_all()`` (this method was formerly known as ``clear()``).
Closing
-------
The ``close()`` method issues a ``expunge_all()``, and releases any transactional/connection resources. When connections are returned to the connection pool, transactional state is rolled back as well.
Refreshing / Expiring
---------------------
To assist with the Session's "sticky" behavior of instances which are present, individual objects can have all of their attributes immediately re-loaded from the database, or marked as "expired" which will cause a re-load to occur upon the next access of any of the object's mapped attributes. This includes all relationships, so lazy-loaders will be re-initialized, eager relationships will be repopulated. Any changes marked on the object are discarded::
# immediately re-load attributes on obj1, obj2
session.refresh(obj1)
session.refresh(obj2)
# expire objects obj1, obj2, attributes will be reloaded
# on the next access:
session.expire(obj1)
session.expire(obj2)
``refresh()`` and ``expire()`` also support being passed a list of individual attribute names in which to be refreshed. These names can reference any attribute, column-based or relation based::
# immediately re-load the attributes 'hello', 'world' on obj1, obj2
session.refresh(obj1, ['hello', 'world'])
session.refresh(obj2, ['hello', 'world'])
# expire the attributes 'hello', 'world' objects obj1, obj2, attributes will be reloaded
# on the next access:
session.expire(obj1, ['hello', 'world'])
session.expire(obj2, ['hello', 'world'])
The full contents of the session may be expired at once using ``expire_all()``::
session.expire_all()
``refresh()`` and ``expire()`` are usually not needed when working with a default-configured ``Session``. The usual need is when an UPDATE or DELETE has been issued manually within the transaction using ``Session.execute()``.
Session Attributes
------------------
The ``Session`` itself acts somewhat like a set-like collection. All items present may be accessed using the iterator interface::
for obj in session:
print obj
And presence may be tested for using regular "contains" semantics::
if obj in session:
print "Object is present"
The session is also keeping track of all newly created (i.e. pending) objects, all objects which have had changes since they were last loaded or saved (i.e. "dirty"), and everything that's been marked as deleted::
# pending objects recently added to the Session
session.new
# persistent objects which currently have changes detected
# (this collection is now created on the fly each time the property is called)
session.dirty
# persistent objects that have been marked as deleted via session.delete(obj)
session.deleted
Note that objects within the session are by default *weakly referenced*. This means that when they are dereferenced in the outside application, they fall out of scope from within the ``Session`` as well and are subject to garbage collection by the Python interpreter. The exceptions to this include objects which are pending, objects which are marked as deleted, or persistent objects which have pending changes on them. After a full flush, these collections are all empty, and all objects are again weakly referenced. To disable the weak referencing behavior and force all objects within the session to remain until explicitly expunged, configure ``sessionmaker()`` with the ``weak_identity_map=False`` setting.
.. _unitofwork_cascades:
Cascades
========
Mappers support the concept of configurable *cascade* behavior on :func:`~sqlalchemy.orm.relation()` constructs. This behavior controls how the Session should treat the instances that have a parent-child relationship with another instance that is operated upon by the Session. Cascade is indicated as a comma-separated list of string keywords, with the possible values ``all``, ``delete``, ``save-update``, ``refresh-expire``, ``merge``, ``expunge``, and ``delete-orphan``.
Cascading is configured by setting the ``cascade`` keyword argument on a ``relation()``::
mapper(Order, order_table, properties={
'items' : relation(Item, items_table, cascade="all, delete-orphan"),
'customer' : relation(User, users_table, user_orders_table, cascade="save-update"),
})
The above mapper specifies two relations, ``items`` and ``customer``. The ``items`` relationship specifies "all, delete-orphan" as its ``cascade`` value, indicating that all ``add``, ``merge``, ``expunge``, ``refresh`` ``delete`` and ``expire`` operations performed on a parent ``Order`` instance should also be performed on the child ``Item`` instances attached to it. The ``delete-orphan`` cascade value additionally indicates that if an ``Item`` instance is no longer associated with an ``Order``, it should also be deleted. The "all, delete-orphan" cascade argument allows a so-called *lifecycle* relationship between an ``Order`` and an ``Item`` object.
The ``customer`` relationship specifies only the "save-update" cascade value, indicating most operations will not be cascaded from a parent ``Order`` instance to a child ``User`` instance except for the ``add()`` operation. "save-update" cascade indicates that an ``add()`` on the parent will cascade to all child items, and also that items added to a parent which is already present in the session will also be added. "save-update" cascade also cascades the *pending history* of a relation()-based attribute, meaning that objects which were removed from a scalar or collection attribute whose changes have not yet been flushed are also placed into the new session - this so that foreign key clear operations and deletions will take place (new in 0.6).
Note that the ``delete-orphan`` cascade only functions for relationships where the target object can have a single parent at a time, meaning it is only appropriate for one-to-one or one-to-many relationships. For a :func:`~sqlalchemy.orm.relation` which establishes one-to-one via a local foreign key, i.e. a many-to-one that stores only a single parent, or one-to-one/one-to-many via a "secondary" (association) table, a warning will be issued if ``delete-orphan`` is configured. To disable this warning, also specify the ``single_parent=True`` flag on the relationship, which constrains objects to allow attachment to only one parent at a time.
The default value for ``cascade`` on :func:`~sqlalchemy.orm.relation()` is ``save-update, merge``.
.. _unitofwork_transaction:
Managing Transactions
=====================
The ``Session`` manages transactions across all engines associated with it. As the ``Session`` receives requests to execute SQL statements using a particular ``Engine`` or ``Connection``, it adds each individual ``Engine`` encountered to its transactional state and maintains an open connection for each one (note that a simple application normally has just one ``Engine``). At commit time, all unflushed data is flushed, and each individual transaction is committed. If the underlying databases support two-phase semantics, this may be used by the Session as well if two-phase transactions are enabled.
Normal operation ends the transactional state using the ``rollback()`` or ``commit()`` methods. After either is called, the ``Session`` starts a new transaction::
Session = sessionmaker()
session = Session()
try:
item1 = session.query(Item).get(1)
item2 = session.query(Item).get(2)
item1.foo = 'bar'
item2.bar = 'foo'
# commit- will immediately go into a new transaction afterwards
session.commit()
except:
# rollback - will immediately go into a new transaction afterwards.
session.rollback()
A session which is configured with ``autocommit=True`` may be placed into a transaction using ``begin()``. With an ``autocommit=True`` session that's been placed into a transaction using ``begin()``, the session releases all connection resources after a ``commit()`` or ``rollback()`` and remains transaction-less (with the exception of flushes) until the next ``begin()`` call::
Session = sessionmaker(autocommit=True)
session = Session()
session.begin()
try:
item1 = session.query(Item).get(1)
item2 = session.query(Item).get(2)
item1.foo = 'bar'
item2.bar = 'foo'
session.commit()
except:
session.rollback()
raise
The ``begin()`` method also returns a transactional token which is compatible with the Python 2.6 ``with`` statement::
Session = sessionmaker(autocommit=True)
session = Session()
with session.begin():
item1 = session.query(Item).get(1)
item2 = session.query(Item).get(2)
item1.foo = 'bar'
item2.bar = 'foo'
Using SAVEPOINT
---------------
SAVEPOINT transactions, if supported by the underlying engine, may be delineated using the ``begin_nested()`` method::
Session = sessionmaker()
session = Session()
session.add(u1)
session.add(u2)
session.begin_nested() # establish a savepoint
session.add(u3)
session.rollback() # rolls back u3, keeps u1 and u2
session.commit() # commits u1 and u2
``begin_nested()`` may be called any number of times, which will issue a new SAVEPOINT with a unique identifier for each call. For each ``begin_nested()`` call, a corresponding ``rollback()`` or ``commit()`` must be issued.
When ``begin_nested()`` is called, a ``flush()`` is unconditionally issued (regardless of the ``autoflush`` setting). This is so that when a ``rollback()`` occurs, the full state of the session is expired, thus causing all subsequent attribute/instance access to reference the full state of the ``Session`` right before ``begin_nested()`` was called.
Enabling Two-Phase Commit
-------------------------
Finally, for MySQL, PostgreSQL, and soon Oracle as well, the session can be instructed to use two-phase commit semantics. This will coordinate the committing of transactions across databases so that the transaction is either committed or rolled back in all databases. You can also ``prepare()`` the session for interacting with transactions not managed by SQLAlchemy. To use two phase transactions set the flag ``twophase=True`` on the session::
engine1 = create_engine('postgresql://db1')
engine2 = create_engine('postgresql://db2')
Session = sessionmaker(twophase=True)
# bind User operations to engine 1, Account operations to engine 2
Session.configure(binds={User:engine1, Account:engine2})
session = Session()
# .... work with accounts and users
# commit. session will issue a flush to all DBs, and a prepare step to all DBs,
# before committing both transactions
session.commit()
Embedding SQL Insert/Update Expressions into a Flush
=====================================================
This feature allows the value of a database column to be set to a SQL expression instead of a literal value. It's especially useful for atomic updates, calling stored procedures, etc. All you do is assign an expression to an attribute::
class SomeClass(object):
pass
mapper(SomeClass, some_table)
someobject = session.query(SomeClass).get(5)
# set 'value' attribute to a SQL expression adding one
someobject.value = some_table.c.value + 1
# issues "UPDATE some_table SET value=value+1"
session.commit()
This technique works both for INSERT and UPDATE statements. After the flush/commit operation, the ``value`` attribute on ``someobject`` above is expired, so that when next accessed the newly generated value will be loaded from the database.
Using SQL Expressions with Sessions
====================================
SQL expressions and strings can be executed via the ``Session`` within its transactional context. This is most easily accomplished using the ``execute()`` method, which returns a ``ResultProxy`` in the same manner as an ``Engine`` or ``Connection``::
Session = sessionmaker(bind=engine)
session = Session()
# execute a string statement
result = session.execute("select * from table where id=:id", {'id':7})
# execute a SQL expression construct
result = session.execute(select([mytable]).where(mytable.c.id==7))
The current ``Connection`` held by the ``Session`` is accessible using the ``connection()`` method::
connection = session.connection()
The examples above deal with a ``Session`` that's bound to a single ``Engine`` or ``Connection``. To execute statements using a ``Session`` which is bound either to multiple engines, or none at all (i.e. relies upon bound metadata), both ``execute()`` and ``connection()`` accept a ``mapper`` keyword argument, which is passed a mapped class or ``Mapper`` instance, which is used to locate the proper context for the desired engine::
Session = sessionmaker()
session = Session()
# need to specify mapper or class when executing
result = session.execute("select * from table where id=:id", {'id':7}, mapper=MyMappedClass)
result = session.execute(select([mytable], mytable.c.id==7), mapper=MyMappedClass)
connection = session.connection(MyMappedClass)
Joining a Session into an External Transaction
===============================================
If a ``Connection`` is being used which is already in a transactional state (i.e. has a ``Transaction``), a ``Session`` can be made to participate within that transaction by just binding the ``Session`` to that ``Connection``::
Session = sessionmaker()
# non-ORM connection + transaction
conn = engine.connect()
trans = conn.begin()
# create a Session, bind to the connection
session = Session(bind=conn)
# ... work with session
session.commit() # commit the session
session.close() # close it out, prohibit further actions
trans.commit() # commit the actual transaction
Note that above, we issue a ``commit()`` both on the ``Session`` as well as the ``Transaction``. This is an example of where we take advantage of ``Connection``'s ability to maintain *subtransactions*, or nested begin/commit pairs. The ``Session`` is used exactly as though it were managing the transaction on its own; its ``commit()`` method issues its ``flush()``, and commits the subtransaction. The subsequent transaction the ``Session`` starts after commit will not begin until it's next used. Above we issue a ``close()`` to prevent this from occurring. Finally, the actual transaction is committed using ``Transaction.commit()``.
When using the ``threadlocal`` engine context, the process above is simplified; the ``Session`` uses the same connection/transaction as everyone else in the current thread, whether or not you explicitly bind it::
engine = create_engine('postgresql://mydb', strategy="threadlocal")
engine.begin()
session = Session() # session takes place in the transaction like everyone else
# ... go nuts
engine.commit() # commit the transaction
.. _unitofwork_contextual:
Contextual/Thread-local Sessions
=================================
A common need in applications, particularly those built around web frameworks, is the ability to "share" a ``Session`` object among disparate parts of an application, without needing to pass the object explicitly to all method and function calls. What you're really looking for is some kind of "global" session object, or at least "global" to all the parts of an application which are tasked with servicing the current request. For this pattern, SQLAlchemy provides the ability to enhance the ``Session`` class generated by ``sessionmaker()`` to provide auto-contextualizing support. This means that whenever you create a ``Session`` instance with its constructor, you get an *existing* ``Session`` object which is bound to some "context". By default, this context is the current thread. This feature is what previously was accomplished using the ``sessioncontext`` SQLAlchemy extension.
Creating a Thread-local Context
-------------------------------
The ``scoped_session()`` function wraps around the ``sessionmaker()`` function, and produces an object which behaves the same as the ``Session`` subclass returned by ``sessionmaker()``::
from sqlalchemy.orm import scoped_session, sessionmaker
Session = scoped_session(sessionmaker())
However, when you instantiate this ``Session`` "class", in reality the object is pulled from a threadlocal variable, or if it doesn't exist yet, it's created using the underlying class generated by ``sessionmaker()``::
>>> # call Session() the first time. the new Session instance is created.
>>> session = Session()
>>> # later, in the same application thread, someone else calls Session()
>>> session2 = Session()
>>> # the two Session objects are *the same* object
>>> session is session2
True
Since the ``Session()`` constructor now returns the same ``Session`` object every time within the current thread, the object returned by ``scoped_session()`` also implements most of the ``Session`` methods and properties at the "class" level, such that you don't even need to instantiate ``Session()``::
# create some objects
u1 = User()
u2 = User()
# save to the contextual session, without instantiating
Session.add(u1)
Session.add(u2)
# view the "new" attribute
assert u1 in Session.new
# commit changes
Session.commit()
The contextual session may be disposed of by calling ``Session.remove()``::
# remove current contextual session
Session.remove()
After ``remove()`` is called, the next operation with the contextual session will start a new ``Session`` for the current thread.
.. _session_lifespan:
Lifespan of a Contextual Session
--------------------------------
A (really, really) common question is when does the contextual session get created, when does it get disposed ? We'll consider a typical lifespan as used in a web application::
Web Server Web Framework User-defined Controller Call
-------------- -------------- ------------------------------
web request ->
call controller -> # call Session(). this establishes a new,
# contextual Session.
session = Session()
# load some objects, save some changes
objects = session.query(MyClass).all()
# some other code calls Session, it's the
# same contextual session as "sess"
session2 = Session()
session2.add(foo)
session2.commit()
# generate content to be returned
return generate_content()
Session.remove() <-
web response <-
The above example illustrates an explicit call to ``Session.remove()``. This has the effect such that each web request starts fresh with a brand new session. When integrating with a web framework, there's actually many options on how to proceed for this step:
* Session.remove() - this is the most cut and dry approach; the ``Session`` is thrown away, all of its transactional/connection resources are closed out, everything within it is explicitly gone. A new ``Session`` will be used on the next request.
* Session.close() - Similar to calling ``remove()``, in that all objects are explicitly expunged and all transactional/connection resources closed, except the actual ``Session`` object hangs around. It doesn't make too much difference here unless the start of the web request would like to pass specific options to the initial construction of ``Session()``, such as a specific ``Engine`` to bind to.
* Session.commit() - In this case, the behavior is that any remaining changes pending are flushed, and the transaction is committed. The full state of the session is expired, so that when the next web request is started, all data will be reloaded. In reality, the contents of the ``Session`` are weakly referenced anyway so its likely that it will be empty on the next request in any case.
* Session.rollback() - Similar to calling commit, except we assume that the user would have called commit explicitly if that was desired; the ``rollback()`` ensures that no transactional state remains and expires all data, in the case that the request was aborted and did not roll back itself.
* do nothing - this is a valid option as well. The controller code is responsible for doing one of the above steps at the end of the request.
Scoped Session API docs: :func:`sqlalchemy.orm.scoped_session`
.. _session_partitioning:
Partitioning Strategies
=======================
Vertical Partitioning
---------------------
Vertical partitioning places different kinds of objects, or different tables, across multiple databases::
engine1 = create_engine('postgresql://db1')
engine2 = create_engine('postgresql://db2')
Session = sessionmaker(twophase=True)
# bind User operations to engine 1, Account operations to engine 2
Session.configure(binds={User:engine1, Account:engine2})
session = Session()
Horizontal Partitioning
-----------------------
Horizontal partitioning partitions the rows of a single table (or a set of tables) across multiple databases.
See the "sharding" example in `attribute_shard.py <http://www.sqlalchemy.org/trac/browser/sqlalchemy/trunk/examples/sharding/attribute_shard.py>`_
Extending Session
=================
Extending the session can be achieved through subclassing as well as through a simple extension class, which resembles the style of :ref:`extending_mapper` called :class:`~sqlalchemy.orm.interfaces.SessionExtension`. See the docstrings for more information on this class' methods.
Basic usage is similar to :class:`~sqlalchemy.orm.interfaces.MapperExtension`::
class MySessionExtension(SessionExtension):
def before_commit(self, session):
print "before commit!"
Session = sessionmaker(extension=MySessionExtension())
or with :func:`~sqlalchemy.orm.create_session()`::
session = create_session(extension=MySessionExtension())
The same ``SessionExtension`` instance can be used with any number of sessions.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

File diff suppressed because it is too large Load Diff
+294
View File
@@ -0,0 +1,294 @@
/* documentation section styles */
body, td {
font-family: verdana, sans-serif;
font-size:.95em;
}
body {
background-color: #FDFBFC;
margin:20px 20px 20px 20px;
}
form {
display:inline;
}
p {
margin-top:10px;
margin-bottom:10px;
}
a {font-weight:normal; text-decoration:underline;}
a:link {color:#0000FF;}
a:visited {color:#0000FF;}
a:active {color:#0000FF;}
a:hover {color:#700000;}
strong a {
font-weight: bold;
}
#search {
float:right;
}
#searchform {
padding:20px;
}
#pagecontrol {
float:right;
}
.topnav
{
background-color: #fbfbee;
border: solid 1px #ccc;
padding:10px;
margin:10px 0px 10px 0px;
}
.document {
border: solid 1px #ccc;
}
.topnav .prevnext {
padding: 5px 0px 0px 0px;
font-size: 0.8em
}
h1, h2, h3, h4, h5 {
font-family:arial,helvetica,sans-serif;
font-weight:bold;
}
.document h1, .document h2, .document h3, .document h4, .document h5 {
font-size: 1.4em;
}
.document img {
display:block;
margin: 0 auto;
}
.document h1 {
display:none;
}
h1 {
font: normal 20px/22px arial,helvetica,sans-serif;
color: #222;
padding:0px;
margin:0px;
}
.topnav h2 {
margin:26px 4px 0px 5px;
font-family:arial,helvetica,sans-serif;
font-size:1.6em;
font-weight:normal;
line-height:1.6em;
}
.topnav h3 {
font-weight: bold;
font-size: 1.4em;
margin:0px;
display:inline;
font-family:verdana,sans-serif;
}
.topnav li,
li.toctree-l1,
li.toctree-l1 li
{
list-style-type:disc;
margin:0px;
padding:1px 8px;
}
.topnav li ul,
li.toctree-l1 ul
{
padding:0px 0px 0px 20px;
}
.topnav li ul li li,
li.toctree-l1 ul li li
{
/*font-size:.90em;*/
}
.sourcelink {
font-size:.8em;
text-align:right;
padding-top:10px;
}
.section {
line-height: 1.5em;
padding:8px 10px 20px 10px;
margin:10px 0px 0px;
}
.section .section {
margin:0px 0px 0px 0px;
padding: 0px;
}
.section .section .section {
margin:0px 0px 0px 20px;
}
.section .section .section .section {
margin:0px 0px 0px 20px;
}
.bottomnav {
background-color:#FBFBEE;
border:1px solid #CCCCCC;
float:right;
margin: 1em 0 1em 5px;
padding:10px;
}
.totoc {
}
.doc_copyright {
font-size:.85em;
padding:10px 0px 10px 0px;
}
pre {
background-color: #f0f0f0;
border: solid 1px #ccc;
padding:10px;
margin: 5px 5px 5px 5px;
overflow:auto;
line-height:1.3em;
}
.popup_sql, .show_sql
{
background-color: #fbfbee;
padding:0px 10px;
margin:0px -10px;
}
.sql_link
{
font-weight:normal;
font-family: arial, sans-serif;
text-transform: uppercase;
font-size: 0.9em;
color:#666;
border:1px solid;
padding:1px 2px 1px 2px;
margin:0px 10px 0px 15px;
float:right;
line-height:1.2em;
}
#docs a.sql_link, .sql_link
{
text-decoration: none;
padding:1px 2px;
}
#docs a.sql_link:hover {
text-decoration: none;
color:#fff;
border:1px solid #900;
background-color: #900;
}
.versionheader {
margin-top: 0.5em;
}
.versionnum {
font-weight: bold;
}
.prerelease {
border: solid #c25757 2px;
border-radius: 4px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
background-color: #c21a1a;
color: white;
padding: 0.05em 0.2em;
}
dl.function > dt,
dl.class > dt
{
background-color:#F0F0F0;
margin:0px -10px;
padding: 0px 10px;
}
dt:target, span.highlight {
background-color:#FBE54E;
}
a.headerlink {
font-size: 0.8em;
padding: 0 4px 0 4px;
text-decoration: none;
visibility: hidden;
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
dt:hover > a.headerlink {
visibility: visible;
}
a.headerlink:hover {
background-color: #00f;
color: white;
}
.clearboth {
clear:both;
}
tt.descname {
background-color:transparent;
font-size:1.2em;
font-weight:bold;
}
tt.descclassname {
background-color:transparent;
}
tt {
background-color:#ECF0F3;
padding:0 1px;
}
@media print {
#nav { display: none; }
#pagecontrol { display: none; }
.topnav .prevnext { display: none; }
.bottomnav { display: none; }
.totoc { display: none; }
.topnav ul li a { text-decoration: none; color: #000; }
}
/* syntax highlighting overrides */
.k, .kn {color:#0908CE;}
.o {color:#BF0005;}
.go {color:#804049;}
+7
View File
@@ -0,0 +1,7 @@
$(document).ready(function(){
$('div.popup_sql').hide();
$('a.sql_link').click(function() {
$(this).nextAll('div.popup_sql:first').toggle();
return false;
})
});
-30
View File
@@ -1,30 +0,0 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>${self.title()}</title>
${self.style()}
<%def name="style()">
</%def>
</head>
<body>
${next.body()}
</body>
</html>
<%def name="style()">
<link rel="stylesheet" href="style.css"></link>
<link rel="stylesheet" href="docs.css"></link>
<link href="syntaxhighlight.css" rel="stylesheet" type="text/css"></link>
<script src="scripts.js"></script>
% if parent:
${parent.style()}
% endif
</%def>
<%def name="title()">
Documentation
</%def>
-41
View File
@@ -1,41 +0,0 @@
<%!
from mako.ext.autohandler import autohandler
%>
<%inherit file="${autohandler(template, context)}"/>
<%page cached="True" cache_key="${self.filename}"/>
<%doc>
base.html - common to all documentation pages. intentionally separate
from autohandler, which can be swapped out for a different one
</%doc>
<%
# bootstrap TOC structure from request args, or pickled file if not present.
import cPickle as pickle
import os, time
#print "%s generating from table of contents for file %s" % (local.filename, self.filename)
filename = os.path.join(os.path.dirname(self.filename), 'table_of_contents.pickle')
toc = pickle.load(file(filename))
version = toc.version
last_updated = toc.last_updated
kwargs = context.kwargs
kwargs.setdefault('extension', 'html')
extension = kwargs['extension']
kwargs.setdefault('paged', True)
kwargs.setdefault('toc', toc)
%>
<div id="topanchor"><a name="top">&nbsp;</a></div>
<h1>${toc.root.doctitle}</h1>
<div id="pagecontrol"><a href="index.${extension}">Multiple Pages</a> | <a href="documentation.${extension}">One Page</a></div>
<div class="versionheader">Version: ${version} Last Updated: ${time.strftime('%x %X', time.localtime(last_updated))}</div>
${next.body(**kwargs)}
-16
View File
@@ -1,16 +0,0 @@
## defines the default layout for normal documentation pages (not including the index)
<%inherit file="base.html"/>
<%page args="toc, extension, paged"/>
<%namespace file="nav.html" import="topnav, pagenav, bottomnav"/>
<%
current = toc.get_by_file(self.template.module.filename)
%>
<A name="<% current.path %>"></a>
${topnav(item=current, toc=toc, extension=extension, paged=paged)}
${next.body(toc=toc, extension=extension, paged=paged)}
${bottomnav(item=current, extension=extension, paged=paged)}
-152
View File
@@ -1,152 +0,0 @@
## formatting.myt - Provides section formatting elements, syntax-highlighted code blocks, and other special filters.
<%!
import string, re, cgi
from mako import filters
import highlight
def plainfilter(f):
f = re.sub(r'\n[\s\t]*\n[\s\t]*', '</p>\n<p>', f)
f = "<p>" + f + "</p>"
return f
%>
<%namespace name="nav" file="nav.html"/>
<%def name="section(toc, path, paged, extension, description=None)">
## Main section formatting element.
<%
content = capture(caller.body)
re2 = re.compile(r"'''PYESC(.+?)PYESC'''", re.S)
content = re2.sub(lambda m: filters.url_unescape(m.group(1)), content)
item = toc.get_by_path(path)
subsection = item.depth > 1
level = min(item.depth, 4)
%>
<A name="${item.path}"></a>
<div class="${'sectionL%d' % level}">
% if (subsection):
<h3>${description or item.description}</h3>
% endif
${content}
% if len(item.children) == 0:
% if paged:
<a href="#top">back to section top</a>
% else:
<a href="#${item.get_page_root().path}">back to section top</a>
% endif
% endif
</div>
</%def>
<%def name="formatplain()" filter="plainfilter">
${ caller.body() | h}
</%def>
<%def name="codeline()" filter="trim,h">
<span class="codeline">${ caller.body() }</span>
</%def>
<%def name="code(toc, paged, extension, title=None, syntaxtype='mako', html_escape=True, use_sliders=False)">
<%
def fix_indent(f):
f =string.expandtabs(f, 4)
g = ''
lines = string.split(f, "\n")
whitespace = None
for line in lines:
if whitespace is None:
match = re.match(r"^([ ]*).+", line)
if match is not None:
whitespace = match.group(1)
if whitespace is not None:
line = re.sub(r"^%s" % whitespace, "", line)
if whitespace is not None or re.search(r"\w", line) is not None:
g += (line + "\n")
else:
g += "\n"
return g[:-1] #.rstrip()
p = re.compile(r'<pre>(.*?)</pre>', re.S)
def hlight(match):
try:
return "<pre>" + highlight.highlight(fix_indent(match.group(1)), html_escape = html_escape, syntaxtype = syntaxtype) + "</pre>"
except:
print "TEXT IS", fix_indent(match.group(1))
def link(match):
return capture(nav.toclink, toc, match.group(2), extension, paged, description=match.group(1))
content = re.sub(r'\[(.+?)\]\(rel:(.+?)\)', link, capture(caller.body))
if syntaxtype != 'diagram':
content = p.sub(hlight, "<pre>" + content + "</pre>")
else:
content = "<pre>" + content + "</pre>"
%>
<div class="${ use_sliders and "sliding_code" or "code" }">
% if title is not None:
<div class="codetitle">${title}</div>
% endif
${ content }
</div>
</%def>
<%def name="popboxlink(name=None, show='show', hide='hide')" filter="trim">
<%
if name is None:
name = attributes.setdefault('popbox_name', 0)
name += 1
attributes['popbox_name'] = name
name = "popbox_" + repr(name)
%>
javascript:togglePopbox('${name}', '${show}', '${hide}')
</%def>
<%def name="popbox(name=None, class_=None)" filter="trim">
<%
if name is None:
name = 'popbox_' + repr(attributes['popbox_name'])
%>
<div id="${name}_div" class="${class_}" style="display:none;">${capture(caller.body) | trim}</div>
</%def>
<%def name="poplink(link='sql')" filter="trim">
<%
href = capture(popboxlink)
%>
'''PYESC${capture(nav.link, href=href, text=link, class_="codepoplink") | u}PYESC'''
</%def>
<%def name="codepopper()" filter="trim">
<%
c = capture(caller.body)
c = re.sub(r'\n', '<br/>\n', filters.html_escape(c.strip()))
%>
</pre><%call expr="popbox(class_='codepop')">${c}</%call><pre>
</%def>
<%def name="poppedcode()" filter="trim">
<%
c = capture(caller.body)
c = re.sub(r'\n', '<br/>\n', filters.html_escape(c.strip()))
%>
</pre><div class="codepop">${c}</div><pre>
</%def>
+72
View File
@@ -0,0 +1,72 @@
<%inherit file="layout.mako"/>
<%def name="show_title()">${_('Index')}</%def>
<h1 id="index">${_('Index')}</h1>
% for i, (key, dummy) in enumerate(genindexentries):
${i != 0 and '| ' or ''}<a href="#${key}"><strong>${key}</strong></a>
% endfor
<hr />
% for i, (key, entries) in enumerate(genindexentries):
<h2 id="${key}">${key}</h2>
<table width="100%" class="indextable"><tr><td width="33%" valign="top">
<dl>
<%
breakat = genindexcounts[i] // 2
numcols = 1
numitems = 0
%>
% for entryname, (links, subitems) in entries:
<dt>
% if links:
<a href="${links[0]}">${entryname|h}</a>
% for link in links[1:]:
, <a href="${link}">[${i}]</a>
% endfor
% else:
${entryname|h}
% endif
% if subitems:
<dd><dl>
% for subentryname, subentrylinks in subitems:
<dt><a href="${subentrylinks[0]}">${subentryname|h}</a>
% for j, link in enumerate(subentrylinks[1:]):
<a href="${link}">[${j}]</a>
% endfor
</dt>
% endfor
</dl></dd>
% endif
<%
numitems = numitems + 1 + len(subitems)
%>
% if numcols <2 and numitems > breakat:
<%
numcols = numcols + 1
%>
</dl></td><td width="33%" valign="top"><dl>
% endif
% endfor
</dl></td></tr></table>
% endfor
<%def name="sidebarrel()">
% if split_index:
<h4>${_('Index')}</h4>
<p>
% for i, (key, dummy) in enumerate(genindexentries):
${i > 0 and '| ' or ''}
<a href="${pathto('genindex-' + key)}"><strong>${key}</strong></a>
% endfor
</p>
<p><a href="${pathto('genindex-all')}"><strong>${_('Full index on one page')}</strong></a></p>
% endif
${parent.sidebarrel()}
</%def>
+132
View File
@@ -0,0 +1,132 @@
## coding: utf-8
<%inherit file="${context['mako_layout']}"/>
<%def name="headers()">
<link rel="stylesheet" href="${pathto('_static/pygments.css', 1)}" type="text/css" />
<link rel="stylesheet" href="${pathto('_static/docs.css', 1)}" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: '${pathto("", 1)}',
VERSION: '${release|h}',
COLLAPSE_MODINDEX: false,
FILE_SUFFIX: '${file_suffix}'
};
</script>
% for scriptfile in script_files + self.attr.local_script_files:
<script type="text/javascript" src="${pathto(scriptfile, 1)}"></script>
% endfor
<script type="text/javascript" src="${pathto('_static/init.js', 1)}"></script>
% if hasdoc('about'):
<link rel="author" title="${_('About these documents')}" href="${pathto('about')}" />
% endif
<link rel="index" title="${_('Index')}" href="${pathto('genindex')}" />
<link rel="search" title="${_('Search')}" href="${pathto('search')}" />
% if hasdoc('copyright'):
<link rel="copyright" title="${_('Copyright')}" href="${pathto('copyright')}" />
% endif
<link rel="top" title="${docstitle|h}" href="${pathto('index')}" />
% if parents:
<link rel="up" title="${parents[-1]['title']|util.striptags}" href="${parents[-1]['link']|h}" />
% endif
% if nexttopic:
<link rel="next" title="${nexttopic['title']|util.striptags}" href="${nexttopic['link']|h}" />
% endif
% if prevtopic:
<link rel="prev" title="${prevtopic['title']|util.striptags}" href="${prevtopic['link']|h}" />
% endif
${self.extrahead()}
</%def>
<%def name="extrahead()"></%def>
<h1>${docstitle|h}</h1>
<div id="search">
Search:
<form class="search" action="${pathto('search')}" method="get">
<input type="text" name="q" size="18" /> <input type="submit" value="${_('Search')}" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
<div class="versionheader">
Version: <span class="versionnum">${release}</span> Last Updated: ${last_updated}
</div>
<div class="clearboth"></div>
<div class="topnav">
<div id="pagecontrol">
<a href="${pathto('reference/index')}">API Reference</a>
|
<a href="${pathto('genindex')}">Index</a>
% if sourcename:
<div class="sourcelink">(<a href="${pathto('_sources/' + sourcename, True)|h}">${_('view source')})</div>
% endif
</div>
<div class="navbanner">
<a class="totoc" href="${pathto(master_doc)}">Table of Contents</a>
% if parents:
% for parent in parents:
» <a href="${parent['link']|h}" title="${parent['title']}">${parent['title']}</a>
% endfor
% endif
% if current_page_name != master_doc:
» ${self.show_title()}
% endif
${prevnext()}
<h2>
${self.show_title()}
</h2>
</div>
% if display_toc and not current_page_name.startswith('index'):
${toc}
% endif
<div class="clearboth"></div>
</div>
<div class="document">
<div class="body">
${next.body()}
</div>
</div>
<%def name="footer()">
<div class="bottomnav">
${prevnext()}
<div class="doc_copyright">
% if hasdoc('copyright'):
&copy; <a href="${pathto('copyright')}">Copyright</a> ${copyright|h}.
% else:
&copy; Copyright ${copyright|h}.
% endif
% if show_sphinx:
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> ${sphinx_version|h}.
% endif
</div>
</div>
</%def>
${self.footer()}
<%def name="prevnext()">
<div class="prevnext">
% if prevtopic:
Previous:
<a href="${prevtopic['link']|h}" title="${_('previous chapter')}">${prevtopic['title']}</a>
% endif
% if nexttopic:
Next:
<a href="${nexttopic['link']|h}" title="${_('next chapter')}">${nexttopic['title']}</a>
% endif
</div>
</%def>
<%def name="show_title()">
% if title:
${title}
% endif
</%def>
-27
View File
@@ -1,27 +0,0 @@
<%inherit file="base.html"/>
<%page args="toc, extension, paged"/>
<%namespace name="formatting" file="formatting.html"/>
<%namespace name="nav" file="nav.html"/>
<%namespace name="pydoc" file="pydoc.html"/>
<%!
import cPickle as pickle
import os
%>
<%
current = toc.get_by_file(self.template.module.filename)
docfile = os.path.join(os.path.dirname(self.filename), 'compiled_docstrings.pickle')
data = dict(pickle.load(file(docfile)))
data = data[self.template.module.docstring]
%>
<%def name="style()">
${parent.style()}
<link rel="stylesheet" href="docutil.css"></link>
</%def>
${nav.topnav(item=current, toc=toc, extension=extension, paged=True)}
${pydoc.obj_doc(obj=data, toc=toc, extension=extension, paged=True)}
${nav.bottomnav(item=current, extension=extension, paged=True)}
-76
View File
@@ -1,76 +0,0 @@
## nav.myt - Provides page navigation elements that are derived from toc.TOCElement structures, including
## individual hyperlinks as well as navigational toolbars and table-of-content listings.
<%namespace name="tocns" file="toc.html"/>
<%def name="itemlink(item, paged, extension, anchor=True)" filter="trim">
<a href="${ item.get_link(anchor=anchor, usefilename=paged, extension=extension) }">${ item.description }</a>
</%def>
<%def name="toclink(toc, path, extension, paged, description=None)" filter="trim">
<%
item = toc.get_by_path(path)
if description is None:
if item:
description = item.description
else:
description = path
if item:
anchor = not paged or item.depth > 1
else:
anchor = False
%>
% if item:
<a href="${ item.get_link(extension=extension, anchor=anchor, usefilename=paged) }">${ description }</a>
% else:
<%
#raise Exception("Can't find TOC link for '%s'" % path)
%>
<b>${ description }</b>
% endif
</%def>
<%def name="link(href, text, class_)" filter="trim">
<a href="${ href }" ${ class_ and (('class=\"%s\"' % class_) or '')}>${ text }</a>
</%def>
<%def name="topnav(item, toc, extension, paged)">
<div class="topnav">
${pagenav(item, extension=extension, paged=paged)}
${tocns.printtoc(root=item, current=None, anchor_toplevel=True, paged=paged, extension=extension)}
</div>
</%def>
<%def name="pagenav(item, paged, extension)">
<div class="navbanner">
<a href="${paged and 'index' or 'documentation'}.${ extension }">Table of Contents</a>
${prevnext(item, paged, extension)}
<h2>${item.description}</h2>
</div>
</%def>
<%def name="bottomnav(item, paged, extension)">
<div class="bottomnav">
${prevnext(item, paged, extension)}
</div>
</%def>
<%def name="prevnext(item, paged, extension)">
<div class="prevnext">
% if item.up:
Up: ${itemlink(item=item.up, paged=paged, anchor=not paged, extension=extension)}
% endif
% if item.previous is not None:
${item.up is not None and " | " or ""}
Previous: ${itemlink(item=item.previous, paged=paged, anchor=not paged, extension=extension)}
% endif
% if item.next is not None:
${item.previous is not None and " | " or ""}
Next: ${itemlink(item=item.next, paged=paged, anchor=not paged, extension=extension)}
% endif
</div>
</%def>
+2
View File
@@ -0,0 +1,2 @@
<%inherit file="layout.mako"/>
${body| util.strip_toplevel_anchors}
-128
View File
@@ -1,128 +0,0 @@
<%doc>pydoc.myt - provides formatting functions for printing docstring.AbstractDoc generated python documentation objects.</%doc>
<%!
import docstring
from docutils.core import publish_parts
import re, sys
def whitespace(content):
"""trim left whitespace."""
if not content:
return ''
# Convert tabs to spaces (following the normal Python rules)
# and split into a list of lines:
lines = content.expandtabs().splitlines()
# Determine minimum indentation (first line doesn't count):
indent = sys.maxint
for line in lines[1:]:
stripped = line.lstrip()
if stripped:
indent = min(indent, len(line) - len(stripped))
# Remove indentation (first line is special):
trimmed = [lines[0].strip()]
if indent < sys.maxint:
for line in lines[1:]:
trimmed.append(line[indent:].rstrip())
# Strip off trailing and leading blank lines:
while trimmed and not trimmed[-1]:
trimmed.pop()
while trimmed and not trimmed[0]:
trimmed.pop(0)
# Return a single string:
return '\n'.join(trimmed)
def formatdocstring(content):
return publish_parts(whitespace(content), writer_name='html')['body']
%>
<%def name="inline_links(toc, extension, paged)"><%
def link(match):
(module, desc) = match.group(1,2)
if not desc:
path = "docstrings_" + module
elif desc.endswith('()'):
path = "docstrings_" + module + "_modfunc_" + desc[:-2]
else:
path = "docstrings_" + module + "_" + desc
return capture(nav.toclink, toc=toc, path=path, description=desc or None, extension=extension, paged=paged)
return lambda content: re.sub('\[(.+?)#(.*?)\]', link, content)
%></%def>
<%namespace name="formatting" file="formatting.html"/>
<%namespace name="nav" file="nav.html"/>
<%def name="obj_doc(obj, toc, extension, paged)">
<%
if obj.isclass:
links = []
for elem in obj.inherits:
if isinstance(elem, docstring.ObjectDoc):
links.append(capture(nav.toclink, toc=toc, path=elem.toc_path, extension=extension, description=elem.name, paged=paged))
else:
links.append(str(elem))
htmldescription = "class " + obj.classname + "(%s)" % (','.join(links))
else:
htmldescription = obj.description
%>
<%call expr="formatting.section(toc=toc, path=obj.toc_path, description=htmldescription, paged=paged, extension=extension)">
% if obj.doc:
<div class="darkcell">${obj.doc or '' | formatdocstring, inline_links(toc, extension, paged)}</div>
% endif
% if not obj.isclass and obj.functions:
<%call expr="formatting.section(toc=toc, path=obj.mod_path, paged=paged, extension=extension)">
% for func in obj.functions:
${function_doc(func=func,toc=toc, extension=extension, paged=paged)}
% endfor
</%call>
% else:
% if obj.functions:
% for func in obj.functions:
% if isinstance(func, docstring.FunctionDoc):
${function_doc(func=func, toc=toc, extension=extension, paged=paged)}
% elif isinstance(func, docstring.PropertyDoc):
${property_doc(prop=func, toc=toc, extension=extension, paged=paged)}
% endif
% endfor
% endif
% endif
% if obj.classes:
% for class_ in obj.classes:
${obj_doc(obj=class_, toc=toc, extension=extension, paged=paged)}
% endfor
% endif
</%call>
</%def>
<%def name="function_doc(func, toc, extension, paged)">
<div class="darkcell">
<%
if hasattr(func, 'toc_path'):
item = toc.get_by_path(func.toc_path)
else:
item = None
%>
<A name="${item and item.path or ''}"></a>
<b>def ${func.name}(${", ".join(map(lambda k: "<i>%s</i>" % k, func.arglist))})</b>
<div class="docstring">
${func.doc or '' | formatdocstring, inline_links(toc, extension, paged)}
</div>
</div>
</%def>
<%def name="property_doc(prop, toc, extension, paged)">
<div class="darkcell">
<A name=""></a>
<b>${prop.name} = property()</b>
<div class="docstring">
${prop.doc or '' | formatdocstring, inline_links(toc, extension, paged)}
</div>
</div>
</%def>
+22
View File
@@ -0,0 +1,22 @@
<%inherit file="layout.mako"/>
<%!
local_script_files = ['_static/searchtools.js']
%>
<%def name="show_title()">${_('Search')}</%def>
<div id="searchform">
<h3>Enter Search Terms:</h3>
<form class="search" action="${pathto('search')}" method="get">
<input type="text" name="q" size="18" /> <input type="submit" value="${_('Search')}" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
<div id="search-results"></div>
<%def name="footer()">
${parent.footer()}
<script type="text/javascript" src="searchindex.js"></script>
</%def>
+28
View File
@@ -0,0 +1,28 @@
<%text>#coding:utf-8
<%inherit file="/base.html"/>
<%page cache_type="file" cached="True"/>
<%!
in_docs=True
%>
</%text>
<div style="text-align:right">
<b>Quick Select:</b> <a href="/docs/06/">0.6</a> | <a href="/docs/05/">0.5</a> | <a href="/docs/04/">0.4</a><br/>
<b>PDF Download:</b> <a href="${pathto('sqlalchemy_' + release.replace('.', '_') + '.pdf', 1)}">download</a>
</div>
${'<%text>'}
${next.body()}
${'</%text>'}
<%text><%def name="style()"></%text>
${self.headers()}
<%text>${parent.style()}</%text>
<link href="/css/site_docs.css" rel="stylesheet" type="text/css"></link>
<%text></%def></%text>
<%text><%def name="title()"></%text>${capture(self.show_title)|util.striptags} &mdash; ${docstitle|h}<%text></%def></%text>
<%!
local_script_files = []
%>
+19
View File
@@ -0,0 +1,19 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
${metatags and metatags or ''}
<title>${capture(self.show_title)|util.striptags} &mdash; ${docstitle|h}</title>
${self.headers()}
</head>
<body>
${next.body()}
</body>
</html>
<%!
local_script_files = []
%>
-44
View File
@@ -1,44 +0,0 @@
## toc.myt - prints table of contents listings given toc.TOCElement strucures
<%def name="toc(toc, paged, extension)">
<div class="topnav">
<a name="table_of_contents"></a>
<h3>Table of Contents</h3>
&nbsp;&nbsp;
<a href="#full_index">(view full table)</a>
<br/><br/>
${printtoc(root=toc,paged=paged, extension=extension, current=None,children=False,anchor_toplevel=False)}
<a name="full_index"></a>
<h3>Table of Contents: Full</h3>
&nbsp;&nbsp;
<a href="#table_of_contents">(view brief table)</a>
${printtoc(root=toc,paged=paged, extension=extension, current=None,children=True,anchor_toplevel=False)}
</div>
</%def>
<%def name="printtoc(root, paged, extension, current=None, children=True, anchor_toplevel=False)">
% if root.children:
<ul>
% for item in root.children:
<%
anchor = anchor_toplevel
if paged and item.filename != root.filename:
anchor = False
%>
<li><a style="${item is current and "font-weight:bold;" or "" }" href="${item.get_link(extension=extension,anchor=anchor, usefilename=paged) }">${item.description}</a></li>
% if children and item.children:
<li>
${printtoc(item, current=current, children=True,anchor_toplevel=True, paged=paged, extension=extension)}
</li>
% endif
% endfor
</ul>
% endif
</%def>
+3 -3
View File
@@ -5,7 +5,7 @@ import os
import re
import doctest
import sqlalchemy.util as util
import sqlalchemy.logging as salog
import sqlalchemy.log as salog
import logging
salog.default_enabled=True
@@ -55,7 +55,7 @@ def teststring(s, name, globs=None, verbose=None, report=True,
return runner.failures, runner.tries
def replace_file(s, newfile):
engine = r"'(sqlite|postgres|mysql):///.*'"
engine = r"'(sqlite|postgresql|mysql):///.*'"
engine = re.compile(engine, re.MULTILINE)
s, n = re.subn(engine, "'sqlite:///" + newfile + "'", s)
if not n:
@@ -63,7 +63,7 @@ def replace_file(s, newfile):
return s
for filename in ('ormtutorial', 'sqlexpression'):
filename = 'content/%s.txt' % filename
filename = '%s.rst' % filename
s = open(filename).read()
#s = replace_file(s, ':memory:')
s = re.sub(r'{(?:stop|sql|opensql)}', '', s)
+711
View File
@@ -0,0 +1,711 @@
%
% sphinx.sty
%
% Adapted from the old python.sty, mostly written by Fred Drake,
% by Georg Brandl.
%
\NeedsTeXFormat{LaTeX2e}[1995/12/01]
\ProvidesPackage{sphinx}[2008/05/01 LaTeX package (Sphinx markup)]
\RequirePackage{textcomp}
\RequirePackage{fancyhdr}
\RequirePackage{fancybox}
\RequirePackage{titlesec}
\RequirePackage{tabulary}
\RequirePackage{amsmath} % for \text
\RequirePackage{makeidx}
\RequirePackage{framed}
\RequirePackage{color}
\RequirePackage{fancyvrb}
\RequirePackage{threeparttable}
% Redefine these colors to your liking in the preamble.
\definecolor{TitleColor}{rgb}{0.126,0.263,0.361}
\definecolor{InnerLinkColor}{rgb}{0.208,0.374,0.486}
\definecolor{OuterLinkColor}{rgb}{0.216,0.439,0.388}
% Redefine these colors to something not white if you want to have colored
% background and border for code examples.
\definecolor{VerbatimColor}{rgb}{1,1,1}
\definecolor{VerbatimBorderColor}{rgb}{1,1,1}
% Uncomment these two lines to ignore the paper size and make the page
% size more like a typical published manual.
%\renewcommand{\paperheight}{9in}
%\renewcommand{\paperwidth}{8.5in} % typical squarish manual
%\renewcommand{\paperwidth}{7in} % O'Reilly ``Programmming Python''
% For graphicx, check if we are compiling under latex or pdflatex.
\ifx\pdftexversion\undefined
\usepackage{graphicx}
\else
\usepackage[pdftex]{graphicx}
\fi
% for PDF output, use colors and maximal compression
\newif\ifsphinxpdfoutput\sphinxpdfoutputfalse
\ifx\pdfoutput\undefined\else\ifcase\pdfoutput
\let\py@NormalColor\relax
\let\py@TitleColor\relax
\else
\sphinxpdfoutputtrue
\input{pdfcolor}
\def\py@NormalColor{\color[rgb]{0.0,0.0,0.0}}
\def\py@TitleColor{\color{TitleColor}}
\pdfcompresslevel=9
\fi\fi
% XeLaTeX can do colors, too
\ifx\XeTeXrevision\undefined\else
\def\py@NormalColor{\color[rgb]{0.0,0.0,0.0}}
\def\py@TitleColor{\color{TitleColor}}
\fi
% Increase printable page size (copied from fullpage.sty)
\topmargin 0pt
\advance \topmargin by -\headheight
\advance \topmargin by -\headsep
% attempt to work a little better for A4 users
\textheight \paperheight
\advance\textheight by -2in
\oddsidemargin 0pt
\evensidemargin 0pt
%\evensidemargin -.25in % for ``manual size'' documents
\marginparwidth 0.5in
\textwidth \paperwidth
\advance\textwidth by -2in
% Style parameters and macros used by most documents here
\raggedbottom
\sloppy
\parindent = 0mm
\parskip = 2mm
\hbadness = 5000 % don't print trivial gripes
\pagestyle{empty} % start this way; change for
\pagenumbering{roman} % ToC & chapters
% Use this to set the font family for headers and other decor:
\newcommand{\py@HeaderFamily}{\sffamily\bfseries}
% Redefine the 'normal' header/footer style when using "fancyhdr" package:
\@ifundefined{fancyhf}{}{
% Use \pagestyle{normal} as the primary pagestyle for text.
\fancypagestyle{normal}{
\fancyhf{}
\fancyfoot[LE,RO]{{\py@HeaderFamily\thepage}}
\fancyfoot[LO]{{\py@HeaderFamily\nouppercase{\rightmark}}}
\fancyfoot[RE]{{\py@HeaderFamily\nouppercase{\leftmark}}}
\fancyhead[LE,RO]{{\py@HeaderFamily \@title, \py@release}}
\renewcommand{\headrulewidth}{0.4pt}
\renewcommand{\footrulewidth}{0.4pt}
}
% Update the plain style so we get the page number & footer line,
% but not a chapter or section title. This is to keep the first
% page of a chapter and the blank page between chapters `clean.'
\fancypagestyle{plain}{
\fancyhf{}
\fancyfoot[LE,RO]{{\py@HeaderFamily\thepage}}
\renewcommand{\headrulewidth}{0pt}
\renewcommand{\footrulewidth}{0.4pt}
}
}
% Some custom font markup commands.
%
\newcommand{\strong}[1]{{\bf #1}}
\newcommand{\code}[1]{\texttt{#1}}
\newcommand{\bfcode}[1]{\code{\bfseries#1}}
\newcommand{\samp}[1]{`\code{#1}'}
\newcommand{\email}[1]{\textsf{#1}}
\newcommand{\py@modulebadkey}{{--just-some-junk--}}
% Redefine the Verbatim environment to allow border and background colors.
% The original environment is still used for verbatims within tables.
\let\OriginalVerbatim=\Verbatim
\let\endOriginalVerbatim=\endVerbatim
% Play with vspace to be able to keep the indentation.
\newlength\distancetoright
\newlength\leftsidespace
\def\mycolorbox#1{%
\setlength\leftsidespace{\@totalleftmargin}%
\setlength\distancetoright{\textwidth}%
\advance\distancetoright -\@totalleftmargin %
\noindent\hspace*{\@totalleftmargin}%
\fcolorbox{VerbatimBorderColor}{VerbatimColor}{%
\begin{minipage}{\distancetoright}%
\smallskip%
\noindent\hspace*{-\leftsidespace}%
#1
\end{minipage}%
}%
}
\def\FrameCommand{\mycolorbox}
\renewcommand{\Verbatim}[1][1]{%
\OriginalVerbatim[#1]%
}
\renewcommand{\endVerbatim}{%
\endOriginalVerbatim%
}
% Index-entry generation support.
%
% Command to generate two index entries (using subentries)
\newcommand{\indexii}[2]{\index{#1!#2}\index{#2!#1}}
% And three entries (using only one level of subentries)
\newcommand{\indexiii}[3]{\index{#1!#2 #3}\index{#2!#3, #1}\index{#3!#1 #2}}
% And four (again, using only one level of subentries)
\newcommand{\indexiv}[4]{
\index{#1!#2 #3 #4}
\index{#2!#3 #4, #1}
\index{#3!#4, #1 #2}
\index{#4!#1 #2 #3}
}
% support for the module index
\newif\ifpy@UseModuleIndex
\py@UseModuleIndexfalse
\newcommand{\makemodindex}{
\newwrite\modindexfile
\openout\modindexfile=mod\jobname.idx
\py@UseModuleIndextrue
}
\newcommand{\printmodindex}{
\@input@{mod\jobname.ind}
}
% Add the defining entry for a module
\newcommand{\py@modindex}[2]{%
\renewcommand{\py@thismodule}{#1}
\ifpy@UseModuleIndex%
\@ifundefined{py@modplat@\py@thismodulekey}{
\write\modindexfile{\protect\indexentry{#1@{\texttt{#1}}|hyperpage}{\thepage}}%
}{\write\modindexfile{\protect\indexentry{#1@{\texttt{#1 }%
\emph{(\platformof{\py@thismodulekey})}}|hyperpage}{\thepage}}%
}
\fi%
}
% "Current" keys
\newcommand{\py@thisclass}{}
\newcommand{\py@thismodule}{}
\newcommand{\py@thismodulekey}{}
\newcommand{\py@thismoduletype}{}
\newcommand{\py@emptymodule}{}
% \declaremodule[key]{type}{name}
\newcommand{\declaremodule}[3][\py@modulebadkey]{
\renewcommand{\py@thismoduletype}{#2}
\ifx\py@modulebadkey#1
\renewcommand{\py@thismodulekey}{#3}
\else
\renewcommand{\py@thismodulekey}{#1}
\fi
\py@modindex{#3}{}
%\label{module-\py@thismodulekey}
}
% Record module platforms for the Module Index
\newif\ifpy@ModPlatformFileIsOpen \py@ModPlatformFileIsOpenfalse
\long\def\py@writeModPlatformFile#1{%
\protected@write\py@ModPlatformFile%
{\let\label\@gobble \let\index\@gobble \let\glossary\@gobble}%
{\string#1}%
}
\newcommand{\py@ModPlatformFilename}{\jobname.pla}
\newcommand{\platform}[1]{
\ifpy@ModPlatformFileIsOpen\else
\newwrite\py@ModPlatformFile
\openout\py@ModPlatformFile=\py@ModPlatformFilename
\py@ModPlatformFileIsOpentrue
\fi
\py@writeModPlatformFile{\py@defplatform{\py@thismodulekey}{#1}}
}
\newcommand{\py@defplatform}[2]{\expandafter\def\csname py@modplat@#1\endcsname{#2}}
\newcommand{\platformof}[1]{\csname py@modplat@#1\endcsname}
\InputIfFileExists{\jobname.pla}{}{}
% \moduleauthor{name}{email}
\newcommand{\moduleauthor}[2]{}
% \sectionauthor{name}{email}
\newcommand{\sectionauthor}[2]{}
% Ignore module synopsis.
\newcommand{\modulesynopsis}[1]{}
% Reset "current" objects.
\newcommand{\resetcurrentobjects}{
\renewcommand{\py@thisclass}{}
\renewcommand{\py@thismodule}{}
\renewcommand{\py@thismodulekey}{}
\renewcommand{\py@thismoduletype}{}
}
% Augment the sectioning commands used to get our own font family in place,
% and reset some internal data items:
\titleformat{\section}{\Large\py@HeaderFamily}%
{\py@TitleColor\thesection}{0.5em}{\py@TitleColor}{\py@NormalColor}
\titleformat{\subsection}{\large\py@HeaderFamily}%
{\py@TitleColor\thesubsection}{0.5em}{\py@TitleColor}{\py@NormalColor}
\titleformat{\subsubsection}{\py@HeaderFamily}%
{\py@TitleColor\thesubsubsection}{0.5em}{\py@TitleColor}{\py@NormalColor}
\titleformat{\paragraph}{\large\py@HeaderFamily}%
{\py@TitleColor}{0em}{\py@TitleColor}{\py@NormalColor}
% Now for a lot of semantically-loaded environments that do a ton of magical
% things to get the right formatting and index entries for the stuff in
% Python modules and C API.
% {fulllineitems} is used in one place in libregex.tex, but is really for
% internal use in this file.
%
\newcommand{\py@itemnewline}[1]{%
\@tempdima\linewidth%
\advance\@tempdima \leftmargin\makebox[\@tempdima][l]{#1}%
}
\newenvironment{fulllineitems}{
\begin{list}{}{\labelwidth \leftmargin \labelsep 0pt
\rightmargin 0pt \topsep -\parskip \partopsep \parskip
\itemsep -\parsep
\let\makelabel=\py@itemnewline}
}{\end{list}}
% \optional is mostly for use in the arguments parameters to the various
% {*desc} environments defined below, but may be used elsewhere. Known to
% be used in the debugger chapter.
%
% Typical usage:
%
% \begin{funcdesc}{myfunc}{reqparm\optional{, optparm}}
% ^^^ ^^^
% No space here No space here
%
% When a function has multiple optional parameters, \optional should be
% nested, not chained. This is right:
%
% \begin{funcdesc}{myfunc}{\optional{parm1\optional{, parm2}}}
%
\let\py@badkey=\@undefined
\newcommand{\optional}[1]{%
{\textnormal{\Large[}}{#1}\hspace{0.5mm}{\textnormal{\Large]}}}
% This can be used when a function or method accepts an varying number
% of arguments, such as by using the *args syntax in the parameter list.
\newcommand{\py@moreargs}{...}
% This can be used when you don't want to document the parameters to a
% function or method, but simply state that it's an alias for
% something else.
\newcommand{\py@unspecified}{...}
\newcommand{\py@varvars}[1]{{%
{\let\unspecified=\py@unspecified%
\let\moreargs=\py@moreargs%
\emph{#1}}}}
\newlength{\py@argswidth}
\newcommand{\py@sigparams}[1]{%
\parbox[t]{\py@argswidth}{\py@varvars{#1}\code{)}}}
\newcommand{\py@sigline}[2]{%
\settowidth{\py@argswidth}{#1\code{(}}%
\addtolength{\py@argswidth}{-2\py@argswidth}%
\addtolength{\py@argswidth}{\textwidth}%
\item[#1\code{(}\py@sigparams{#2}]}
% C functions ------------------------------------------------------------
% \begin{cfuncdesc}[refcount]{type}{name}{arglist}
% Note that the [refcount] slot should only be filled in by
% tools/anno-api.py; it pulls the value from the refcounts database.
\newcommand{\cfuncline}[3]{
\py@sigline{\code{#1 \bfcode{#2}}}{#3}%
}
\newenvironment{cfuncdesc}[3]{
\begin{fulllineitems}
\cfuncline{#1}{#2}{#3}
}{\end{fulllineitems}}
% C variables ------------------------------------------------------------
% \begin{cvardesc}{type}{name}
\newenvironment{cvardesc}[2]{
\begin{fulllineitems}
\item[\code{#1 \bfcode{#2}}]
}{\end{fulllineitems}}
% C data types -----------------------------------------------------------
% \begin{ctypedesc}[index name]{typedef name}
\newenvironment{ctypedesc}[2][\py@badkey]{
\begin{fulllineitems}
\item[\bfcode{#2}]
}{\end{fulllineitems}}
% C type fields ----------------------------------------------------------
% \begin{cmemberdesc}{container type}{ctype}{membername}
\newcommand{\cmemberline}[3]{
\item[\code{#2 \bfcode{#3}}]
}
\newenvironment{cmemberdesc}[3]{
\begin{fulllineitems}
\cmemberline{#1}{#2}{#3}
}{\end{fulllineitems}}
% Funky macros -----------------------------------------------------------
% \begin{csimplemacrodesc}{name}
% -- "simple" because it has no args; NOT for constant definitions!
\newenvironment{csimplemacrodesc}[1]{
\begin{fulllineitems}
\item[\bfcode{#1}]
}{\end{fulllineitems}}
% simple functions (not methods) -----------------------------------------
% \begin{funcdesc}{name}{args}
\newcommand{\funcline}[2]{%
\py@sigline{\bfcode{#1}}{#2}}
\newenvironment{funcdesc}[2]{
\begin{fulllineitems}
\funcline{#1}{#2}
}{\end{fulllineitems}}
% classes ----------------------------------------------------------------
% \begin{classdesc}{name}{constructor args}
\newcommand{\classline}[2]{
\py@sigline{\strong{class }\bfcode{#1}}{#2}}
\newenvironment{classdesc}[2]{
% Using \renewcommand doesn't work for this, for unknown reasons:
\global\def\py@thisclass{#1}
\begin{fulllineitems}
\classline{#1}{#2}
}{\end{fulllineitems}}
% \begin{excclassdesc}{name}{constructor args}
% but indexes as an exception
\newenvironment{excclassdesc}[2]{
% Using \renewcommand doesn't work for this, for unknown reasons:
\global\def\py@thisclass{#1}
\begin{fulllineitems}
\py@sigline{\strong{exception }\bfcode{#1}}{#2}%
}{\end{fulllineitems}}
% There is no corresponding {excclassdesc*} environment. To describe
% a class exception without parameters, use the {excdesc} environment.
\let\py@classbadkey=\@undefined
% object method ----------------------------------------------------------
% \begin{methoddesc}[classname]{methodname}{args}
\newcommand{\methodline}[3][\@undefined]{
\py@sigline{\bfcode{#2}}{#3}}
\newenvironment{methoddesc}[3][\@undefined]{
\begin{fulllineitems}
\ifx\@undefined#1\relax
\methodline{#2}{#3}
\else
\def\py@thisclass{#1}
\methodline{#2}{#3}
\fi
}{\end{fulllineitems}}
% static method ----------------------------------------------------------
% \begin{staticmethoddesc}[classname]{methodname}{args}
\newcommand{\staticmethodline}[3][\@undefined]{
\py@sigline{static \bfcode{#2}}{#3}}
\newenvironment{staticmethoddesc}[3][\@undefined]{
\begin{fulllineitems}
\ifx\@undefined#1\relax
\staticmethodline{#2}{#3}
\else
\def\py@thisclass{#1}
\staticmethodline{#2}{#3}
\fi
}{\end{fulllineitems}}
% class method ----------------------------------------------------------
% \begin{classmethoddesc}[classname]{methodname}{args}
\newcommand{\classmethodline}[3][\@undefined]{
\py@sigline{class \bfcode{#2}}{#3}}
\newenvironment{classmethoddesc}[3][\@undefined]{
\begin{fulllineitems}
\ifx\@undefined#1\relax
\classmethodline{#2}{#3}
\else
\def\py@thisclass{#1}
\classmethodline{#2}{#3}
\fi
}{\end{fulllineitems}}
% object data attribute --------------------------------------------------
% \begin{memberdesc}[classname]{membername}
\newcommand{\memberline}[2][\py@classbadkey]{%
\ifx\@undefined#1\relax
\item[\bfcode{#2}]
\else
\item[\bfcode{#2}]
\fi
}
\newenvironment{memberdesc}[2][\py@classbadkey]{
\begin{fulllineitems}
\ifx\@undefined#1\relax
\memberline{#2}
\else
\def\py@thisclass{#1}
\memberline{#2}
\fi
}{\end{fulllineitems}}
% For exceptions: --------------------------------------------------------
% \begin{excdesc}{name}
% -- for constructor information, use excclassdesc instead
\newenvironment{excdesc}[1]{
\begin{fulllineitems}
\item[\strong{exception }\bfcode{#1}]
}{\end{fulllineitems}}
% Module data or constants: ----------------------------------------------
% \begin{datadesc}{name}
\newcommand{\dataline}[1]{%
\item[\bfcode{#1}]\nopagebreak}
\newenvironment{datadesc}[1]{
\begin{fulllineitems}
\dataline{#1}
}{\end{fulllineitems}}
% bytecode instruction ---------------------------------------------------
% \begin{opcodedesc}{name}{var}
% -- {var} may be {}
\newenvironment{opcodedesc}[2]{
\begin{fulllineitems}
\item[\bfcode{#1}\quad\emph{#2}]
}{\end{fulllineitems}}
% generic description ----------------------------------------------------
\newcommand{\descline}[1]{%
\item[\bfcode{#1}]\nopagebreak%
}
\newenvironment{describe}[1]{
\begin{fulllineitems}
\descline{#1}
}{\end{fulllineitems}}
% This version is being checked in for the historical record; it shows
% how I've managed to get some aspects of this to work. It will not
% be used in practice, so a subsequent revision will change things
% again. This version has problems, but shows how to do something
% that proved more tedious than I'd expected, so I don't want to lose
% the example completely.
%
\newcommand{\grammartoken}[1]{\texttt{#1}}
\newenvironment{productionlist}[1][\py@badkey]{
\def\optional##1{{\Large[}##1{\Large]}}
\def\production##1##2{\code{##1}&::=&\code{##2}\\}
\def\productioncont##1{& &\code{##1}\\}
\def\token##1{##1}
\let\grammartoken=\token
\parindent=2em
\indent
\begin{tabular}{lcl}
}{%
\end{tabular}
}
% Notices / Admonitions
%
\newlength{\py@noticelength}
\newcommand{\py@heavybox}{
\setlength{\fboxrule}{1pt}
\setlength{\fboxsep}{7pt}
\setlength{\py@noticelength}{\linewidth}
\addtolength{\py@noticelength}{-2\fboxsep}
\addtolength{\py@noticelength}{-2\fboxrule}
\setlength{\shadowsize}{3pt}
\Sbox
\minipage{\py@noticelength}
}
\newcommand{\py@endheavybox}{
\endminipage
\endSbox
\fbox{\TheSbox}
}
% Some are quite plain:
\newcommand{\py@noticestart@note}{}
\newcommand{\py@noticeend@note}{}
\newcommand{\py@noticestart@hint}{}
\newcommand{\py@noticeend@hint}{}
\newcommand{\py@noticestart@important}{}
\newcommand{\py@noticeend@important}{}
\newcommand{\py@noticestart@tip}{}
\newcommand{\py@noticeend@tip}{}
% Others gets more visible distinction:
\newcommand{\py@noticestart@warning}{\py@heavybox}
\newcommand{\py@noticeend@warning}{\py@endheavybox}
\newcommand{\py@noticestart@caution}{\py@heavybox}
\newcommand{\py@noticeend@caution}{\py@endheavybox}
\newcommand{\py@noticestart@attention}{\py@heavybox}
\newcommand{\py@noticeend@attention}{\py@endheavybox}
\newcommand{\py@noticestart@danger}{\py@heavybox}
\newcommand{\py@noticeend@danger}{\py@endheavybox}
\newcommand{\py@noticestart@error}{\py@heavybox}
\newcommand{\py@noticeend@error}{\py@endheavybox}
\newenvironment{notice}[2]{
\def\py@noticetype{#1}
\csname py@noticestart@#1\endcsname
\par\strong{#2}
}{\csname py@noticeend@\py@noticetype\endcsname}
% Allow the release number to be specified independently of the
% \date{}. This allows the date to reflect the document's date and
% release to specify the release that is documented.
%
\newcommand{\py@release}{}
\newcommand{\version}{}
\newcommand{\shortversion}{}
\newcommand{\releaseinfo}{}
\newcommand{\releasename}{Release}
\newcommand{\release}[1]{%
\renewcommand{\py@release}{\releasename\space\version}%
\renewcommand{\version}{#1}}
\newcommand{\setshortversion}[1]{%
\renewcommand{\shortversion}{#1}}
\newcommand{\setreleaseinfo}[1]{%
\renewcommand{\releaseinfo}{#1}}
% Allow specification of the author's address separately from the
% author's name. This can be used to format them differently, which
% is a good thing.
%
\newcommand{\py@authoraddress}{}
\newcommand{\authoraddress}[1]{\renewcommand{\py@authoraddress}{#1}}
% This sets up the fancy chapter headings that make the documents look
% at least a little better than the usual LaTeX output.
%
\@ifundefined{ChTitleVar}{}{
\ChNameVar{\raggedleft\normalsize\py@HeaderFamily}
\ChNumVar{\raggedleft \bfseries\Large\py@HeaderFamily}
\ChTitleVar{\raggedleft \rm\Huge\py@HeaderFamily}
% This creates chapter heads without the leading \vspace*{}:
\def\@makechapterhead#1{%
{\parindent \z@ \raggedright \normalfont
\ifnum \c@secnumdepth >\m@ne
\DOCH
\fi
\interlinepenalty\@M
\DOTI{#1}
}
}
}
% Redefine description environment so that it is usable inside fulllineitems.
%
\renewcommand{\description}{%
\list{}{\labelwidth\z@%
\itemindent-\leftmargin%
\labelsep5pt%
\let\makelabel=\descriptionlabel}}
% Definition lists; requested by AMK for HOWTO documents. Probably useful
% elsewhere as well, so keep in in the general style support.
%
\newenvironment{definitions}{%
\begin{description}%
\def\term##1{\item[##1]\mbox{}\\*[0mm]}
}{%
\end{description}%
}
% Tell TeX about pathological hyphenation cases:
\hyphenation{Base-HTTP-Re-quest-Hand-ler}
% The following is stuff copied from docutils' latex writer.
%
\newcommand{\optionlistlabel}[1]{\bf #1 \hfill}
\newenvironment{optionlist}[1]
{\begin{list}{}
{\setlength{\labelwidth}{#1}
\setlength{\rightmargin}{1cm}
\setlength{\leftmargin}{\rightmargin}
\addtolength{\leftmargin}{\labelwidth}
\addtolength{\leftmargin}{\labelsep}
\renewcommand{\makelabel}{\optionlistlabel}}
}{\end{list}}
\newlength{\lineblockindentation}
\setlength{\lineblockindentation}{2.5em}
\newenvironment{lineblock}[1]
{\begin{list}{}
{\setlength{\partopsep}{\parskip}
\addtolength{\partopsep}{\baselineskip}
\topsep0pt\itemsep0.15\baselineskip\parsep0pt
\leftmargin#1}
\raggedright}
{\end{list}}
% Redefine includgraphics for avoiding images larger than the screen size
% If the size is not specified.
\let\py@Oldincludegraphics\includegraphics
\newbox\image@box%
\newdimen\image@width%
\renewcommand\includegraphics[2][\@empty]{%
\ifx#1\@empty%
\setbox\image@box=\hbox{\py@Oldincludegraphics{#2}}%
\image@width\wd\image@box%
\ifdim \image@width>\linewidth%
\setbox\image@box=\hbox{\py@Oldincludegraphics[width=\linewidth]{#2}}%
\fi%
\box\image@box%
\else%
\py@Oldincludegraphics[#1]{#2}%
\fi%
}
% Fix the index and bibliography environments to add an entry to the Table of
% Contents; this is much nicer than just having to jump to the end of the book
% and flip around, especially with multiple indexes.
%
\let\py@OldTheindex=\theindex
\renewcommand{\theindex}{
\cleardoublepage
\phantomsection
\py@OldTheindex
\addcontentsline{toc}{chapter}{\indexname}
}
\let\py@OldThebibliography=\thebibliography
\renewcommand{\thebibliography}[1]{
\cleardoublepage
\phantomsection
\py@OldThebibliography{1}
\addcontentsline{toc}{chapter}{\bibname}
}
% Include hyperref last.
\RequirePackage[colorlinks,breaklinks,
linkcolor=InnerLinkColor,filecolor=OuterLinkColor,
menucolor=OuterLinkColor,pagecolor=OuterLinkColor,
urlcolor=OuterLinkColor]{hyperref}
-200
View File
@@ -1,200 +0,0 @@
/* documentation section styles */
#topanchor {position:absolute;left:0px;top:0px;width:0px;height:0px;}
#pagecontrol {float:right;}
.topnav {
background-color: #fbfbee;
border: solid 1px #ccc;
padding:10px 10px 0px 10px;
margin:10px 0px 10px 0px;
}
pre {
margin:0px;
padding:0px;
}
.prevnext {
padding: 5px 0px 0px 0px;
}
.codetitle {
font-family: verdana, sans-serif;
font-size: 12px;
font-weight: bold;
text-decoration:underline;
padding:5px;
}
.codeline {
font-family: courier, "courier new", serif;
font-size:1.1em;
color: #960;
}
h1, h2, h3 {
font-family:arial,helvetica,sans-serif;
}
h1 {
font: normal 20px/22px arial,helvetica,sans-serif;
color: #222;
padding:0px;
margin:0px;
}
h2 {
font-family:arial,helvetica,sans-serif;
font-size:20px;
font-weight:normal;
line-height:24px;
margin:0px;
}
h3 {
font-family: arial, sans-serif;
font-size:16px;
font-weight:bold;
}
.topnav h3 {
font-weight: bold;
font-size: 16px;
margin:0px;
display:inline;
font-family:verdana,sans-serif;
}
.topnav h2 {
margin:26px 4px 0px 5px;
}
.sectionL1 {
line-height: 1.5em;
padding:8px 10px 20px 10px;
margin:10px 0px 0px;
}
.sectionL2 {
margin:0px 0px 0px 0px;
line-height: 1.5em;
}
.sectionL3 {
margin:0px 0px 0px 20px;
line-height: 1.5em;
}
.sectionL4 {
margin:0px 0px 0px 20px;
line-height: 1.5em;
}
.topnav li {
font-size:12px;
list-style-type:none;
padding:0px 0px 3px 8px;
margin:0px;
}
.topnav ul ul {
padding:0px 0px 0px 8px;
}
.topnav ul ul li {
font-size: 11px;
}
.bottomnav {
background-color:#FBFBEE;
border:1px solid #CCCCCC;
float:right;
margin:0px 0px 15px 5px;
padding:10px;
}
.toclink {
font-weight: bold;
font-size: 12px;
padding:0px 0px 3px 8px;
/*border:1px solid;*/
}
.smalltoclink {
font-size: 11px;
padding:0px 0px 3px 0px;
}
.docstring {
margin-left:15px;
margin-bottom:5px;
margin-top:5px;
}
.darkcell {
/*font-family: courier, "courier new", serif;*/
margin:0px 0px 10px 0px;
padding:4px 4px 4px 4px;
background-color: #f0f0f0;
border: solid 1px #ccc;
}
.sliding_code {
font-family: courier, "courier new", serif;
font-size:12px;
background-color: #f0f0f0;
border: solid 1px #ccc;
padding:10px;
margin: 5px 5px 5px 5px;
overflow:auto;
}
.code {
font-family: courier, "courier new", serif;
font-size:12px;
background-color: #f0f0f0;
border: solid 1px #ccc;
padding:10px; /*2px 2px 2px 10px;*/
margin: 5px 5px 5px 5px;
line-height:1.2em;
}
.codepop
{
font-family: courier, "courier new", serif;
color:#000;
background-color: #fbfbee;
border: 1px solid #d9d9d9;
border-right: 1px solid #999;
border-bottom: 1px solid #999;
padding:10px;
width:95%;
/*margin:5px 10px 5px 0px;*/
/*clear:right;*/
}
.codepoplink,
#docs a.codepoplink
{
font-weight:normal;
font-family: arial, sans-serif;
text-transform: uppercase;
font-size:11px;
color:#666;
border:1px solid;
padding:1px 2px 1px 2px;
margin:0px 10px 0px 15px;
float:right;
}
#docs a.codepoplink {
text-decoration: none;
}
#docs a.codepoplink:hover {
text-decoration: none;
color:#fff;
border:1px solid #900;
background-color: #900;
}
-274
View File
@@ -1,274 +0,0 @@
/*
:Author: David Goodger <goodger@python.org>
:Id: $Id: html4css1.css 4993 2007-03-04 21:21:49Z fwiemann $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin-left: 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left {
clear: left }
img.align-right {
clear: right }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font-family: serif ;
font-size: 100% }
pre.literal-block, pre.doctest-block {
margin-left: 2em ;
margin-right: 2em }
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }

Some files were not shown because too many files have changed in this diff Show More