Compare commits

...
2248 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
Mike Bayer e12075efa8 fix weakref issue seen on one buildbot test 2008-01-02 01:34:25 +00:00
Mike Bayer a8b62a02dd - further fix to new TypeDecorator, so that subclasses of TypeDecorators work properly
- _handle_dbapi_exception() usage changed so that unwrapped exceptions can be rethrown with the original stack trace
2008-01-02 01:29:38 +00:00
Mike Bayer 02a4176a65 happy new year 2008-01-01 17:42:17 +00:00
Mike Bayer 226a95cb4f fix to new TypeDecorator 2008-01-01 01:47:58 +00:00
Mike Bayer 65c618534f fix up oracle handling of LOB/string [ticket:902], slight fixes to defaults.py but we
will need to fix up result-type handling some more
2008-01-01 00:43:24 +00:00
Mike Bayer 85ab0501f4 filter() criterion takes mapper equivalent_columns into account when it adapts to select_table. more to come in [ticket:917] . 2007-12-31 10:23:21 +00:00
Mike Bayer 7cc356a701 remove "is None" from boolean tests 2007-12-29 20:52:50 +00:00
Mike Bayer c933619a0d limit scope of try/except 2007-12-29 20:50:32 +00:00
Mike Bayer 3689897d0d use long for query runid counter 2007-12-29 20:46:07 +00:00
Mike Bayer 57201ca878 - added is_disconnect() support for oracle
- fixed _handle_dbapi_error to detect endless loops, doesn't call rollback/cursor.close
etc. in case of disconnect
2007-12-29 20:13:50 +00:00
Mike Bayer a582fe3b26 - mapped classes which extend "object" and do not provide an
__init__() method will now raise TypeError if non-empty *args
or **kwargs are present at instance construction time (and are
not consumed by any extensions such as the scoped_session mapper),
consistent with the behavior of normal Python classes [ticket:908]
2007-12-29 19:20:38 +00:00
Mike Bayer 90d38c7407 - fixed Query bug when filter_by() compares a relation against None
[ticket:899]
2007-12-29 18:50:46 +00:00
Mike Bayer dc94cb7b10 - MapperExtension.before_update() and after_update() are now called
symmetrically; previously, an instance that had no modified column
attributes (but had a relation() modification) could be called with
before_update() but not after_update() [ticket:907]
2007-12-29 18:33:27 +00:00
Mike Bayer b528d58d49 - fixed session.refresh() with instance that has custom entity_name
[ticket:914]
2007-12-29 18:22:21 +00:00
Mike Bayer 0875860acd some rudimentary fixes to get instance-level deferreds/lazy loads to transfer over on merge() 2007-12-21 21:55:20 +00:00
Mike Bayer 1a9f98f4f6 cruft ! who knew 2007-12-21 16:58:18 +00:00
Mike Bayer 75d7d8d510 added assertion for expiry's current inability to detect a PK switch in the DB 2007-12-21 16:44:12 +00:00
Mike Bayer 90c3944dac - disabled the "populate expired/deferred attributes as we come across them" functionality in mapper._instance(), as its not completed, doesn't properly handle mutable scalar attributes, and has poor test coverage 2007-12-21 16:20:46 +00:00
Mike Bayer b9dd765d92 mass load wont overwrite modified expired attributes 2007-12-21 07:26:39 +00:00
Mike Bayer 2eea612f52 broke up various class-level mapper/instance fanfare into class_level_loader; load-time setup_loader just returns a loader object with no checks 2007-12-21 07:10:14 +00:00
Mike Bayer c114f096bd - reworked all lazy/deferred/expired callables to be
serializable class instances, added pickling tests
- cleaned up "deferred" polymorphic system so that the
mapper handles it entirely
- columns which are missing from a Query's select statement
now get automatically deferred during load.
2007-12-21 06:57:20 +00:00
Mike Bayer 2db36bf59c - inheriting mappers now inherit the MapperExtensions of their parent
mapper directly, so that all methods for a particular MapperExtension
are called for subclasses as well.  As always, any MapperExtension
can return either EXT_CONTINUE to continue extension processing
or EXT_STOP to stop processing.  The order of mapper resolution is:
<extensions declared on the classes mapper> <extensions declared on the
classes' parent mapper> <globally declared extensions>.

Note that if you instantiate the same extension class separately
and then apply it individually for two mappers in the same inheritance
chain, the extension will be applied twice to the inheriting class,
and each method will be called twice.

To apply a mapper extension explicitly to each inheriting class but
have each method called only once per operation, use the same
instance of the extension for both mappers.
[ticket:490]
2007-12-20 02:37:48 +00:00
Mike Bayer 267523e4da - sqlite SLDate type will not erroneously render "microseconds" portion
of a datetime or time object when sent to the DB.
2007-12-20 01:45:36 +00:00
Rick Morrison a9c7e6f774 forgot CHANGES 2007-12-19 23:15:25 +00:00
Rick Morrison 116e19f7f0 Fix for autoload of non-identity PK integer columns [824]
Better datetime checking/conversion for pyodbc adodbapi [842]
Fix for autoloading schema-qualified tables [901]
Convert_unicode support for all dialects [839]
2007-12-19 23:04:43 +00:00
Jason Kirtland 501447ada3 - Re-raise SystemExit et al in _ConnectionRecord.close
- Little code cleanup- decreased verbosity of string formatting.
2007-12-19 20:36:37 +00:00
Mike Bayer b9b0aca757 - auto-reconnect support improved; a Connection can now automatically
reconnect after its underlying connection is invalidated, without
needing to connect() again from the engine.  This allows an ORM session
bound to a single Connection to not need a reconnect.
Open transactions on the Connection must be rolled back after an invalidation
of the underlying connection else an error is raised.  Also fixed
bug where disconnect detect was not being called for cursor(), rollback(),
or commit().
2007-12-19 19:51:46 +00:00
Lele Gaifax e7f30cba78 Fix the unpacking of the refered table name under Firebird
This fixes a little glitch introduced in [3959], in case of "implicit FKs"
(that is, {{{ForeignKey("orders")}}}, where the field(s) is missing).
2007-12-19 08:03:57 +00:00
Mike Bayer ba0267a955 introductory docstring bonanza 2007-12-18 23:53:40 +00:00
Jason Kirtland 1dc4546aa9 Apply default cascade rules for firebird self-ref ForeignKeys. 2007-12-18 19:37:03 +00:00
Mike Bayer b5908484b1 a little pre-lunch decrufting and cleanup 2007-12-18 19:32:03 +00:00
Lele Gaifax fee8fe3391 Use an explicit ordering in the query 2007-12-18 13:06:24 +00:00
Lele Gaifax 7c1e57879d Add Firebird to the list of DBs that needs explicit sequences 2007-12-18 13:03:55 +00:00
Mike Bayer df68f30777 get most oracle tests in sql working again.... 2007-12-18 06:13:42 +00:00
Mike Bayer faf4aca165 - cleanup; lambdas removed from properties; properties mirror same-named functions (more like eventual decorator syntax); remove some old methods, factor out some "raiseerr" ugliness to outer lying functions.
- corresponding_column() integrates "require_embedded" flag with other set arithmetic
2007-12-18 05:40:06 +00:00
Mike Bayer f6068a3522 - select().as_scalar() will raise an exception if the select does not have
exactly one expression in its columns clause.
- added "helper exception" to select.type access, generic functions raise
the chance of this happening
- a slight behavioral change to attributes is, del'ing an attribute
does *not* cause the lazyloader of that attribute to fire off again;
the "del" makes the effective value of the attribute "None".  To
re-trigger the "loader" for an attribute, use
session.expire(instance, [attrname]).
- fix ormtutorial for IS NULL
2007-12-18 00:24:03 +00:00
Mike Bayer 420c098d56 fixed del history 2007-12-16 20:32:56 +00:00
Mike Bayer 30b6d53b6d after_update called with state.obj() 2007-12-16 20:11:36 +00:00
Mike Bayer abc33bd32d - more fixes to the LIMIT/OFFSET aliasing applied with Query + eagerloads,
in this case when mapped against a select statement [ticket:904]
- _hide_froms logic in expression totally localized to Join class, including search through previous clone sources
- removed "stop_on" from main visitors, not used
- "stop_on" in AbstractClauseProcessor part of constructor, ClauseAdapter sets it up based on given clause
- fixes to is_derived_from() to take previous clone sources into account, Alias takes self + cloned sources into account. this is ultimately what the #904 bug was.
2007-12-16 18:32:25 +00:00
Lele Gaifax 8ce3f5d699 Fix bad example of Firebird test DB
Use an absolute path rather than a relative one, and the out-of-the-box
sysdba password. This is just to avoid confusing new testers, that should
add a db.firebird entry in their ~/satest.cfg anyway.
2007-12-16 10:59:06 +00:00
Mike Bayer 6c179ff694 moved hide_froms and aggregate_hide_froms to be only on FromClause 2007-12-16 02:54:42 +00:00
Lele Gaifax 66e1cdd1d9 Revert to use default poolclass under Firebird
This partially reverts [3562] and instead documents the problem suggesting
a possible workaround. For the tests, the occurence of the problem is
largely reduced by using a TCP connection (that is, 'localhost:/some/file.fdb'
instead of '/some/file.fdb')
2007-12-15 16:14:31 +00:00
Lele Gaifax 8489bc23dd Remove some spurious spaces 2007-12-15 13:23:15 +00:00
Lele Gaifax e630b322f6 Fixed minor reST issue 2007-12-15 10:08:37 +00:00
Lele Gaifax 8e5cf37a70 Documentation markup and a few typos 2007-12-15 09:50:35 +00:00
Lele Gaifax 50ce0006a5 Implemented FBDialect.server_version_info() 2007-12-15 09:02:41 +00:00
Mike Bayer 8cf05355bb oof...unicode object still needs to return the value if it just warned... 2007-12-14 23:47:33 +00:00
Mike Bayer 379d333c31 oof, history on collections were wrong. fixed byroot_tree test as well 2007-12-14 23:28:10 +00:00
Mike Bayer 6c4ad36cc9 - simplified _mapper_registry further. its now just a weakkeydict of mapper->True, stores
all mappers including non primaries, and is strictly used for the list of "to compile/dispose".
- all global references are now weak referencing.  if you del a mapped class and any dependent classes,
its mapper and all dependencies fall out of scope.
- attributes.py still had issues which were barely covered by tests.  added way more tests (coverage.py still says 71%, doh)
fixed things, took out unnecessary commit to states.  attribute history is also asserted for ordering.
2007-12-14 23:11:13 +00:00
Mike Bayer d73d0f420c added an inheritance test 2007-12-14 18:08:55 +00:00
Lele Gaifax 1bf1f97f73 Firebird module documentation 2007-12-14 16:45:46 +00:00
Jason Kirtland f44a7f6ae9 correction... 2007-12-14 10:30:14 +00:00
Mike Bayer 1694e33838 try to bang mysql tests to work 2007-12-14 06:29:31 +00:00
Mike Bayer 0df750223a - merged instances_yields branch r3908:3934, minus the "yield" part which remains slightly problematic
- cleanup of mapper._instance, query.instances().  mapper identifies objects which are part of the
current load using a app-unique id on the query context.
- attributes refactor; attributes now mostly use copy-on-modify instead of copy-on-load behavior,
simplified get_history(), added a new set of tests
- fixes to OrderedSet such that difference(), intersection() and others can accept an iterator
- OrderedIdentitySet passes in OrderedSet to the IdentitySet superclass for usage in difference/intersection/etc. operations so that these methods actually work with ordering behavior.
- query.order_by() takes into account aliased joins, i.e.  query.join('orders', aliased=True).order_by(Order.id)
- cleanup etc.
2007-12-14 05:53:18 +00:00
Jason Kirtland 273e48c9a9 - Raise an error when assigning a bogusly keyed dictionary to one of the builtin dict-based collection types [ticket:886]
- Collections gain a @converter framework for flexible validation and adaptation of bulk assignment
- Bogus bulk assignments now raise TypeError instead of exceptions.ArgumentError
2007-12-14 00:13:18 +00:00
Jason Kirtland 94355aebe6 Fixed some __repr__'s attempting to %d their not-yet-assigned primary key ids. 2007-12-13 21:56:52 +00:00
Lele Gaifax c0f48e8383 Firebird does use qmark style params 2007-12-13 20:16:23 +00:00
Lele Gaifax 2c3c081fb0 Use the external strlen UDF for func.length() under Firebird 2007-12-13 15:53:35 +00:00
Jason Kirtland 8128a6378a - Removed @testing.supported. Dialects in development or maintained outside
the tree can now run the full suite of tests out of the box.
- Migrated most @supported to @fails_on, @fails_on_everything_but, or (last
  resort) @unsupported.  @fails_on revealed a slew of bogus test skippage,
  which was corrected.
- Added @fails_on_everything_but.  Yes, the first usage *was*
  "fails_on_everything_but('postgres')".  How did you guess!
- Migrated @supported in dialect/* to the new test-class attribute __only_on__.
- Test classes can also have __unsupported_on__ and __excluded_on__.
2007-12-13 09:59:14 +00:00
Lele Gaifax 2522534311 Disabled some tests with INTERSECT, not handled by Firebird 2007-12-12 23:06:22 +00:00
Lele Gaifax bdc12f2468 Use an external UDF to implement the mod operator under Firebird 2007-12-12 23:00:42 +00:00
Chris Mcdonough 6f6a098135 bug fixes
- the call to self.get() in get_committed_state was missing a required parameter, rendered sqlalchemy unusable in certain situations

- fixed a large bug in dynamic_loader() where the query criterion wasn't generated correctly if other relations existed to dynamic_loader's argument
2007-12-12 20:20:03 +00:00
Jason Kirtland bdd922ad0d Some code-level docs for r3916 2007-12-12 20:18:58 +00:00
Mike Bayer 5c14b20f9f implemented many-to-one comparisons to None generate <column> IS NULL, with column on the left side in all cases 2007-12-12 17:56:52 +00:00
Lele Gaifax 16810e4011 New simple test for Dialect.has_sequence() 2007-12-12 17:24:56 +00:00
Jason Kirtland bfb33f7959 - on mysql, emit inner joins as 'INNER JOIN ... ON' (for version 3.23) 2007-12-12 16:39:10 +00:00
Lele Gaifax f38e24e263 Reflect Firebird PassiveDefaults
- column's default values are properly reflected (also those coming from DOMAINs)
 - implemented .has_sequence()
 - fix type on FK reflection
2007-12-12 16:08:07 +00:00
Lele Gaifax 9a220fc769 Cosmetic changes to the Firebird reflection queries.
This brings them more consistent with the syntax of the statements generated by SA,
using lowercase field names.
2007-12-11 23:07:41 +00:00
Mike Bayer 86f12f93cc - eagerload(), lazyload(), eagerload_all() take an optional
second class-or-mapper argument, which will select the mapper
to apply the option towards.  This can select among other
mappers which were added using add_entity().

- eagerloading will work with mappers added via add_entity().
2007-12-11 19:33:36 +00:00
Mike Bayer edbfbf81f7 - fix to cascades on polymorphic relations, such that cascades
from an object to a polymorphic collection continue cascading
along the set of attributes specific to each element in the collection.
2007-12-11 17:29:08 +00:00
Mike Bayer c468a27444 comment 2007-12-10 18:42:39 +00:00
Mike Bayer cb8bf058b6 refresh_instance becomes an InstanceState so boolean tests are OK 2007-12-10 18:08:16 +00:00
Mike Bayer 41df778985 - more query tests
- trying to refine some of the adaptation stuff
- query.from_statement() wont allow further generative criterion
- added a warning to columncollection when selectable is formed with
conflicting columns (only in the col export phase)
- some method rearrangement on schema/columncollection....
- property conflicting relation warning doesnt raise for concrete
2007-12-10 04:31:17 +00:00
Mike Bayer 5cdb942791 - Query.select_from() now replaces all existing FROM criterion with
the given argument; the previous behavior of constructing a list
of FROM clauses was generally not useful as is required
filter() calls to create join criterion, and new tables introduced
within filter() already add themselves to the FROM clause.  The
new behavior allows not just joins from the main table, but select
statements as well.  Filter criterion, order bys, eager load
clauses will be "aliased" against the given statement.
2007-12-09 23:27:04 +00:00
Lele Gaifax 7cbfbba949 Better reflection of Firebird data types.
Instead of relying on internal numeric code, lookup the associated real
name. This has the extra benefit of properly handling of DOMAINs.
2007-12-09 23:08:59 +00:00
Mike Bayer 7758edfd74 fix... 2007-12-09 18:27:01 +00:00
Mike Bayer 120f13a500 add pydoc for from_statement(). 2007-12-09 17:50:24 +00:00
Mike Bayer 4f5aa12547 - added a warning when a relation() is added to an inheriting mapper that is present on a super-mapper; multiple DependencyProcessors are not expected during the flush process
- found an uncovered line in uow, was "covered" by one particular breaking test
2007-12-09 17:26:16 +00:00
Mike Bayer 8b1beee112 add some updates too 2007-12-09 16:13:27 +00:00
Mike Bayer 901829a7e3 - added a test for boolean saves/retrieves 2007-12-09 16:11:21 +00:00
Mike Bayer f1ee3d406f - fix up the fixtures comparator
- strengthened memory profiling test
2007-12-09 15:56:37 +00:00
Mike Bayer 362164c282 only report true for source change if added + deleted, dont pick up inserts 2007-12-09 15:04:14 +00:00
Mike Bayer d972b5e51b mapper uses attributes to get non-cached history 2007-12-09 05:24:07 +00:00
Mike Bayer 8fb0c0b4ce remove redundant identity map set 2007-12-09 05:14:40 +00:00
Mike Bayer acdb90784b - mutable primary key support is added. primary key columns can be
changed freely, and the identity of the instance will change upon
flush. In addition, update cascades of foreign key referents (primary
key or not) along relations are supported, either in tandem with the
database's ON UPDATE CASCADE (required for DB's like Postgres) or
issued directly by the ORM in the form of UPDATE statements, by setting
the flag "passive_cascades=False".
2007-12-09 05:00:12 +00:00
Mike Bayer c9b3f0bcef - added new methods to TypeDecorator, process_bind_param() and
process_result_value(), which automatically take advantage of the processing
of the underlying type.  Ideal for using with Unicode or Pickletype.
TypeDecorator should now be the primary way to augment the behavior of any
existing type including other TypeDecorator subclasses such as PickleType.
2007-12-08 23:03:22 +00:00
Mike Bayer 65b204c122 some updates, remove an old test (really crufty old stuff here) 2007-12-08 21:49:03 +00:00
Mike Bayer 56c544edab [ticket:888] continued, synonym add_prop 2007-12-08 19:56:11 +00:00
Mike Bayer f4e3240812 ok found it 2007-12-08 19:25:10 +00:00
Mike Bayer 1b1f8d78df fixed test which didnt pass along 'allitems' collection to the sort... 2007-12-08 19:15:12 +00:00
Mike Bayer 8693d4b287 - flush() refactor merged from uow_nontree branch r3871-r3885
- topological.py cleaned up, presents three public facing functions which
return list/tuple based structures, without exposing any internals.  only
the third function returns the "hierarchical" structure.  when results
include "cycles" or "child" items, 2- or 3- tuples are used to represent
results.
- unitofwork uses InstanceState almost exclusively now.  new and deleted lists
are now dicts which ref the actual object to provide a strong ref for the
duration that they're in those lists.  IdentitySet is only used for the public
facing versions of "new" and "deleted".
- unitofwork topological sort no longer uses the "hierarchical" version of the sort
for the base sort, only for the "per-object" secondary sort where it still
helps to group non-dependent operations together and provides expected insert
order.  the default sort deals with UOWTasks in a straight list and is greatly
simplified.  Tests all pass but need to see if svilen's stuff still works,
one block of code in _sort_cyclical_dependencies() seems to not be needed anywhere
but i definitely put it there for a reason at some point; if not hopefully we
can derive more test coverage from that.
- the UOWEventHandler is only applied to object-storing attributes, not
scalar (i.e. column-based) ones.  cuts out a ton of overhead when setting
non-object based attributes.
- InstanceState also used throughout the flush process, i.e. dependency.py,
mapper.save_obj()/delete_obj(), sync.execute() all expect InstanceState objects
in most cases now.
- mapper/property cascade_iterator() takes InstanceState as its argument,
but still returns lists of object instances so that they are not dereferenced.
- a few tricks needed when dealing with InstanceState, i.e. when loading a list
of items that are possibly fresh from the DB, you *have* to get the actual objects
into a strong-referencing datastructure else they fall out of scope immediately.
dependency.py caches lists of dependent objects which it loads now (i.e. history
collections).
- AttributeHistory is gone, replaced by a function that returns a 3-tuple of
added, unchanged, deleted.  these collections still reference the object
instances directly for the strong-referencing reasons mentiontioned, but
it uses less IdentitySet logic to generate.
2007-12-08 18:58:03 +00:00
Mike Bayer 78bb82a44b changed the anonymous numbering scheme to be more appealing
got tests running
2007-12-08 18:38:18 +00:00
Mike Bayer 377a57ea8d corrected for current output... 2007-12-08 18:04:46 +00:00
Mike Bayer 88518c203d added a mention about eagerload_all() [ticket:897] 2007-12-08 17:07:40 +00:00
Mike Bayer fbdc7d535d mapper.instances() is deprecated 2007-12-07 20:14:10 +00:00
Mike Bayer 6f5f86ad4a a little refinement to topological options, more to come 2007-12-07 17:27:21 +00:00
Mike Bayer 7bf90e2f4d fix to unique bind params, you *can* use the same unique bindparam multiple times
in a statement.  the collision check is strictly detecting non-unique's that happen to have
the same name.
2007-12-07 16:47:00 +00:00
Mike Bayer 3715e10bf8 - also with dynamic, implemented correct count() behavior as well
as other helper methods.
2007-12-07 16:31:12 +00:00
Mike Bayer 7ca4797537 - added "cascade delete" behavior to "dynamic" relations just like
that of regular relations.  if passive_deletes flag (also just added)
is not set, a delete of the parent item will trigger a full load of
the child items so that they can be deleted or updated accordingly.
2007-12-07 16:13:19 +00:00
Mike Bayer 33f62587cd fixed key error when no pks could be located 2007-12-07 15:28:20 +00:00
Mike Bayer 541b6772e9 - generation of "unique" bind parameters has been simplified to use the same
"unique identifier" mechanisms as everything else.  This doesn't affect
user code, except any code that might have been hardcoded against the generated
names.  Generated bind params now have the form "<paramname>_<num>",
whereas before only the second bind of the same name would have this form.

- bindparam() objects themselves can be used as keys for execute(), i.e.
statement.execute({bind1:'foo', bind2:'bar'})
2007-12-06 22:23:10 +00:00
Mike Bayer 3ac9c93e26 warn if query.get() used with existing criterion 2007-12-06 19:31:59 +00:00
Mike Bayer 4580e77da2 - query.get() and query.load() do not take existing filter or other
criterion into account; these methods *always* look up the given id
in the database or return the current instance from the identity map,
disregarding any existing filter, join, group_by or other criterion
which has been configured. [ticket:893]
2007-12-06 19:15:04 +00:00
Mike Bayer 594784a206 assert_unicode=True replaced with default of assert_unicode='warn' 2007-12-05 22:21:37 +00:00
Mike Bayer 1c65cb025a - ordering of cols in pks_by_table and cols_by_table is significant;
particularly for pks_by_table the ordering is expected to match the ordering
of pk columns in the table for usage in query.get() as well as identity key
generation
2007-12-05 22:04:27 +00:00
Mike Bayer c863f9fdec more assertion hints... 2007-12-05 21:51:16 +00:00
Mike Bayer 30a81eff3b improved assertions, test is failing on the buildbot only (not osx or linux) 2007-12-05 21:34:22 +00:00
Mike Bayer 261e76259a some more omit schemas for [ticket:890] 2007-12-05 21:16:02 +00:00
Mike Bayer a5f2aab9e0 - tables with schemas can still be used in sqlite, firebird,
schema name just gets dropped [ticket:890]
2007-12-05 21:14:09 +00:00
Mike Bayer 9629838f7d - fixed wrong varname in session exception throw
- fixed vertical example to just use a scoped session
2007-12-05 20:55:33 +00:00
Mike Bayer 9e4052dc8b - a major behavioral change to collection-based backrefs: they no
longer trigger lazy loads !  "reverse" adds and removes
are queued up and are merged with the collection when it is
actually read from and loaded; but do not trigger a load beforehand.
For users who have noticed this behavior, this should be much more
convenient than using dynamic relations in some cases; for those who
have not, you might notice your apps using a lot fewer queries than
before in some situations. [ticket:871]
2007-12-05 20:43:16 +00:00
Mike Bayer 238c2c8dbe - basic framework for generic functions, [ticket:615]
- changed the various "literal" generation functions to use an anonymous
bind parameter.  not much changes here except their labels now look
like ":param_1", ":param_2" instead of ":literal"
- from_obj keyword argument to select() can be a scalar or a list.
2007-12-05 03:07:21 +00:00
Mike Bayer c6bda7dcc8 - fixed backref bug where you could not del instance.attr if attr
was None
2007-12-04 17:55:25 +00:00
Mike Bayer cb46ae4fe9 - relaxed rules on column_property() expressions having labels; any
ColumnElement is accepted now, as the compiler auto-labels non-labeled
ColumnElements now.  a selectable, like a select() statement, still
requires conversion to ColumnElement via as_scalar() or label().
2007-12-04 17:06:55 +00:00
Gaëtan de Menten f5a4554c52 typo 2007-12-04 16:22:44 +00:00
Mike Bayer aa71eed4e2 fixed replacement of existing column properties with synonyms, [ticket:888] 2007-12-04 15:53:23 +00:00
Mike Bayer 0ec4e7d6b3 - moved class-level attributes placed by the attributes package into a _class_state
variable attached to the class.
- mappers track themselves primarily using the "mappers" collection on _class_state.
ClassKey is gone and mapper lookup uses regular dict keyed to entity_name; removes
a fair degree of WeakKeyDictionary overhead as well as ClassKey overhead.
- mapper_registry renamed to _mapper_registry; is only consulted by the
compile_mappers(), mapper.compile() and clear_mappers() functions/methods.
2007-12-02 00:31:26 +00:00
Mike Bayer c6d01a56e1 - several ORM attributes have been removed or made private:
mapper.get_attr_by_column(), mapper.set_attr_by_column(),
mapper.pks_by_table, mapper.cascade_callable(),
MapperProperty.cascade_callable(), mapper.canload()
- refinements to mapper PK/table column organization, session cascading,
some naming convention work
2007-12-01 23:00:05 +00:00
Mike Bayer ebb4b02c21 added test to ensure two conflicting m2m + backrefs raise an error 2007-11-30 21:25:42 +00:00
Mike Bayer 8bf75013ac remove a little cruft 2007-11-30 03:34:06 +00:00
Mike Bayer 2b306350e2 - added support for version_id_col in conjunction with inheriting mappers.
version_id_col is typically set on the base mapper in an inheritance
relationship where it takes effect for all inheriting mappers.
[ticket:883]
- a little rearrangement of save_obj()
2007-11-30 00:40:56 +00:00
Mike Bayer e9abaaef9f - adjustment to the previous checkin regarding inheritance to not conflict with globals
- fix to self-referential eager loading such that if the same mapped
instance appears in two or more distinct sets of columns in the same
result set, its eagerly loaded collection will be populated regardless
of whether or not all of the rows contain a set of "eager" columns for
that collection.  this would also show up as a KeyError when fetching
results with join_depth turned on.
2007-11-29 19:37:05 +00:00
Mike Bayer 74baa86b86 - fixed bug where Query would not apply a subquery to the SQL when LIMIT
was used in conjunction with an inheriting mapper where the eager
loader was only in the parent mapper.
2007-11-29 15:36:13 +00:00
Mike Bayer 2a94e3e6c5 cleanup 2007-11-28 23:44:14 +00:00
Mike Bayer 4d7f279d14 fixed unicode-ness for Unicode values 2007-11-28 22:09:57 +00:00
Mike Bayer efdd638cd2 - fixed bug which could arise when using session.begin_nested() in conjunction
with more than one level deep of enclosing session.begin() statements
2007-11-28 22:03:14 +00:00
Mike Bayer 4ee7070223 new synonym() behavior, including auto-attribute gen, attribute decoration,
and auto-column mapping implemented; [ticket:801]
2007-11-28 21:13:35 +00:00
Mike Bayer d56e11ffe2 default value of assert_unicode is None on String, False on create_engine(), and True on Unicode type. 2007-11-28 00:44:16 +00:00
Mike Bayer 3f6e94e818 - column labels in the form "tablename.columname", i.e. with a dot, are now
supported.
2007-11-27 16:57:56 +00:00
Mike Bayer 6ed4645f02 opened up the test for "reflection with convert_unicode=True". this is since convert_unicode by default has assert_unicode, want to ensure that other dialects (at least oracle) support this (i.e. not unicode schema names themselves, just that they dont sent thru bytestrings to a String).
if maxdb or sybase *should* be able to handle this too though I can't test on this end.
2007-11-27 16:23:02 +00:00
Mike Bayer ccdb725766 added None to support zope __provides__, [ticket:882] 2007-11-27 16:15:32 +00:00
Mike Bayer 65abc78010 added a test to validate ResultProxy truncation behavior 2007-11-27 16:13:45 +00:00
Mike Bayer 329703a371 un-screw up the attribute manager checkin 2007-11-27 05:44:16 +00:00
Mike Bayer 4cc7b51e20 fixed reflection of unicode, [ticket:881] 2007-11-27 05:37:57 +00:00
Mike Bayer 378c02348c AttributeManager class and "cached" state removed....attribute listing
is tracked from _sa_attrs class collection
2007-11-27 05:15:13 +00:00
Paul Johnston e89f31e0df Fix: MSSQL set identity_insert and errors [ticket:538] 2007-11-25 23:56:38 +00:00
Mike Bayer 168bab1cf1 - check for NoneType too with unicode....
- fixed ORM tests to have proper unicode
2007-11-25 23:46:05 +00:00
Paul Johnston 6753cb7f72 MSSQL doesn't support subqueries in insert values; disable test 2007-11-25 23:36:20 +00:00
Paul Johnston 2c873f0ee4 Fix: test_decimal on MSSQL - use a value that is accurately represented as a float, and make when asdecimal=False, convert Decimal to float 2007-11-25 23:34:39 +00:00
Mike Bayer 37bffd8f2b edits 2007-11-25 23:34:26 +00:00
Paul Johnston 954f7b289b Fix: MSSQL concatenate operator is + not || [ticket:879] 2007-11-25 23:17:02 +00:00
Mike Bayer e4056a1787 - added new flag to String and create_engine(), assert_unicode=(True|False|None).
When convert_unicode=True, this flag also defaults to `True`, and results in all
unicode conversion operations raising an exception when a non-unicode bytestring
is passed as a bind parameter.  It is strongly advised that all unicode-aware
applications make proper use of Python unicode objects (i.e. u'hello' and
not 'hello').
2007-11-25 23:14:03 +00:00
Paul Johnston 99827142d6 Avoid doubling quoting of identifier in MSSQL reflection 2007-11-25 23:10:17 +00:00
Paul Johnston c0b8665735 Make function a reserved word in MSSQL 2007-11-25 22:58:14 +00:00
Paul Johnston eda2fc7c04 MSSQL/PyODBC no longer has a global set nocount on 2007-11-25 22:46:22 +00:00
Paul Johnston f5701297b1 Change to make PyODBC result fetching a bit more reliable 2007-11-25 17:40:49 +00:00
Paul Johnston 39b89bd05b column.foreign_key -> foreign_keys in MSSQL 2007-11-25 17:32:06 +00:00
Mike Bayer e83eefafbb bump for py2.4 2007-11-25 04:00:44 +00:00
Mike Bayer d5f9f122f6 OrderedSet to appease the unit tests....need to find a way to get rid of this 2007-11-25 03:45:20 +00:00
Mike Bayer cf18eecd70 - named_with_column becomes an attribute
- cleanup within compiler visit_select(), column labeling
- is_select() removed from dialects, replaced with returns_rows_text(), returns_rows_compiled()
- should_autocommit() removed from dialects, replaced with should_autocommit_text() and
should_autocommit_compiled()
- typemap and column_labels collections removed from Compiler, replaced with single "result_map" collection.
- ResultProxy uses more succinct logic in combination with result_map to target columns
2007-11-25 03:28:49 +00:00
Mike Bayer 6f604f9116 - decruftify old visitors used by orm, convert to functions that
use a common traversal function.
- TranslatingDict is finally gone, thanks to column.proxy_set simpleness...hooray !
- shoved "slice" use case on RowProxy into an exception case.  knocks noticeable time off of large result set operations.
2007-11-24 01:59:29 +00:00
Mike Bayer 3f93103a5e - all kinds of cleanup, tiny-to-slightly-significant speed improvements 2007-11-24 00:55:39 +00:00
Mike Bayer 238dc916fa more changes to merge(dont_load); since we now have a guarantee that
all merged instances aren't dirty, we can copy the attribues without using
any append/replace events, and therefore don't have any concern of lazy loaders
firing off.  added tests to ensure that '_is_orphan()' doesn't get screwed up,
and also that the 'dirty' list on the new session stays empty, which is an
extra bonus of this approach.
2007-11-23 19:01:40 +00:00
Mike Bayer 41403b7930 - some clarifications and fixes to merge(instance, dont_load=True).
fixed bug where lazy loaders were getting disabled on returned instances.
Also, we currently do not support merging an instance which has uncommitted
changes on it, in the case that dont_load=True is used....this will
now raise an error.  This is due to complexities in merging the
"committed state" of the given instance to correctly correspond to the
newly copied instance.  Since the use case for dont_load=True is
caching, the given instances shouldn't have any uncommitted changes on them
anyway.
2007-11-23 05:24:32 +00:00
Jason Kirtland 56d14df0eb Typo fix 2007-11-20 18:32:01 +00:00
Mike Bayer 6fa74de09d - clarified the error message which occurs when you try to update()
an instance with the same identity key as an instance already present
in the session.
- opened up the recursive checks in session.merge() a little bit
2007-11-20 15:55:36 +00:00
Mike Bayer 569c9fa65a - fixed endless loop issue when using lazy="dynamic" on both
sides of a bi-directional relationship [ticket:872]
2007-11-19 08:30:52 +00:00
Mike Bayer 6bb629d76b add a polymorphic get() test 2007-11-19 04:08:48 +00:00
Mike Bayer 051e43245d doctest fixups 2007-11-18 22:47:55 +00:00
Mike Bayer aaaac9be47 - added tests for [ticket:768] 2007-11-18 22:35:19 +00:00
Mike Bayer 64d0ffee96 - added op() operator to instrumented attributes; i.e.
User.name.op('ilike')('%somename%') [ticket:767]
2007-11-18 22:19:05 +00:00
Mike Bayer b3ec17153b - MSSQL anonymous labels for selection of functions made deterministic
- propagate correct **kwargs through mssql methods
2007-11-18 21:32:13 +00:00
Mike Bayer 4e12db8295 repaired FB functions, [ticket:862] 2007-11-18 21:20:32 +00:00
Jason Kirtland f96a514f43 Tests for mysql casts and a couple adjustments. 2007-11-18 20:44:20 +00:00
Jason Kirtland d3f6ae2332 - Clarified use of python's Decimal 2007-11-18 19:01:42 +00:00
Jason Kirtland ea627ebe5c Migrated Connection.properties to Connection.info ('info' is the new standard name for user-writable property collections that came out of [ticket:573]). 'properties' is now an alias, will be removed in 0.5. 2007-11-18 18:19:52 +00:00
Mike Bayer 08a434881f - added a little more checking for garbage-collection dereferences in
InstanceState.__cleanup() to reduce "gc ignored" errors on app
shutdown
2007-11-18 17:31:09 +00:00
Mike Bayer 68027f623a added some ORDER BYs to appease the ever picky postgres 2007-11-18 17:17:04 +00:00
Mike Bayer 268834989c - PickleType will compare using == when set up with mutable=False,
and not the `is` operator.  To use `is` or any other comparator, send
in a custom comparison function using PickleType(comparator=my_custom_comparator).
2007-11-18 16:32:47 +00:00
Mike Bayer 177c2219c6 test cases were not fully testing contains_eager() with regards to [ticket:777], fixed contains_eager() for more than one level deep 2007-11-18 16:18:54 +00:00
Mike Bayer 95807fd287 some wide zoomark ranges... 2007-11-18 07:23:00 +00:00
Mike Bayer 31b32896e8 cut down a good deal of Join construction overhead 2007-11-18 06:51:17 +00:00
Mike Bayer 61edeeae4c logging fixes 2007-11-18 05:46:45 +00:00
Mike Bayer 79aa1a6b25 fixed both group-deferred attributes and expired attributes to not
blow away changes made on attributes before the load takes place
2007-11-18 05:16:52 +00:00
Mike Bayer 43d476ce60 - oracle will now reflect "DATE" as an OracleDateTime column, not
OracleDate

- added awareness of schema name in oracle table_names() function,
fixes metadata.reflect(schema='someschema') [ticket:847]
2007-11-18 02:44:15 +00:00
Mike Bayer 89da016272 removed is_expired() method since the meaning of "expired" is per-attribute now 2007-11-18 02:22:41 +00:00
Mike Bayer d86eb9ba4f oops, print statements... 2007-11-18 02:18:52 +00:00
Mike Bayer be50f4f13a typos 2007-11-18 02:15:34 +00:00
Mike Bayer 622a26a655 - session.refresh() and session.expire() now support an additional argument
"attribute_names", a list of individual attribute keynames to be refreshed
or expired, allowing partial reloads of attributes on an already-loaded
instance.
- finally simplified the behavior of deferred attributes, deferred polymorphic
load, session.refresh, session.expire, mapper._postfetch to all use a single
codepath through query._get(), which now supports a list of individual attribute names
to be refreshed.  the *one* exception still remaining is mapper._get_poly_select_loader(),
which may stay that way since its inline with an already processing load operation.
otherwise, query._get() is the single place that all "load this instance's row" operation
proceeds.
- cleanup all over the place
2007-11-18 02:13:56 +00:00
Mike Bayer e076169d39 fixed further issues with row translation [ticket:868] 2007-11-15 15:10:05 +00:00
Jason Kirtland d5da9a56bc Restored Python 2.3 compatibility (in IdentitySet) 2007-11-14 22:46:14 +00:00
Mike Bayer 64b7a0cd6e modified last commit; the eager loader only undefers columns from the primary mapped table. 2007-11-14 16:59:49 +00:00
Mike Bayer a03aa84c31 - DeferredColumnLoader checks row for column, if present sends it to
ColumnLoader to create the row processor
- eager loaders ensure deferred foreign key cols are present in the primary list of columns (and secondary...).  because eager loading with LIMIT/OFFSET doesn't re-join to the parent table anymore this is now necessary. [ticket:864]
2007-11-14 16:43:21 +00:00
Jason Kirtland 7f0b5edc92 Removed some distractions, ala r3770. 2007-11-14 01:26:50 +00:00
Jason Kirtland f4a9e4512b Removed some distractions. 2007-11-14 00:43:49 +00:00
Mike Bayer f4ee7a1bcb - added having() method to Query, applies HAVING to the generated statement
in the same way as filter() appends to the WHERE clause.
2007-11-13 23:07:01 +00:00
Jason Kirtland fb36643893 More column type __repr__ corrections. 2007-11-13 19:29:16 +00:00
Jason Kirtland e31665b82f AbstractType __repr__ robustification. 2007-11-13 16:50:16 +00:00
Paul Johnston e684b93a69 Remove usage of deprecated in_ syntax 2007-11-11 18:23:59 +00:00
Jason Kirtland 7f85c1e443 - Pool listeners preserved on pool.recreate()
- Docstring rampage
2007-11-10 19:31:06 +00:00
Jason Kirtland df3cbebe3c Removed out of date identity map language (thanks, Jiten) 2007-11-10 15:47:19 +00:00
Mike Bayer b6b720609d added self referential test 2007-11-10 14:36:03 +00:00
Mike Bayer ea46e556f9 - anonymous column expressions are automatically labeled.
e.g. select([x* 5]) produces "SELECT x * 5 AS anon_1".
    This allows the labelname to be present in the cursor.description
    which can then be appropriately matched to result-column processing
    rules. (we can't reliably use positional tracking for result-column
    matches since text() expressions may represent multiple columns).

  - operator overloading is now controlled by TypeEngine objects - the
    one built-in operator overload so far is String types overloading
    '+' to be the string concatenation operator.
    User-defined types can also define their own operator overloading
    by overriding the adapt_operator(self, op) method.

  - untyped bind parameters on the right side of a binary expression
    will be assigned the type of the left side of the operation, to better
    enable the appropriate bind parameter processing to take effect
    [ticket:819]
2007-11-10 03:02:16 +00:00
Jason Kirtland 681c8fc51c - mysql float types now do an end run around the base class and respect precision=None and length=None
- Added the mysteriously missing mysql cast support
- Added mysql REAL synonym for schema generation
2007-11-09 19:55:31 +00:00
Mike Bayer c1d5cdb940 more searching for equiv columns 2007-11-09 16:54:47 +00:00
Mike Bayer 476d395f5e - fixed error where Query.add_column() would not accept a class-bound
attribute as an argument; Query also raises an error if an invalid
argument was sent to add_column() (at instances() time) [ticket:858]
2007-11-09 16:36:46 +00:00
Mike Bayer 9d21a7f080 - query doesn't throw an error if you use distinct() and an order_by()
containing UnaryExpressions (or other) together [ticket:848]
2007-11-09 16:25:28 +00:00
Mike Bayer d54ca6a62a a tweak, still requiring test coverage, to add more "foreign key equivalents" to the equivalent_columns collection 2007-11-09 15:11:38 +00:00
Mike Bayer 2cbb133567 more changes to traverse-and-clone; a particular element will only be cloned once and is
then re-used.  the FROM calculation of a Select normalizes the list of hide_froms against all
previous incarnations of each FROM clause, using a tag attached from cloned clause to
previous.
2007-11-08 18:06:21 +00:00
Mike Bayer 7a25be0f42 - identified some cases where Alias needs to be cloned; but still cant clone
when its an alias of a Table; added some test coverage for one particular
case from the doctests
- fixed "having" example in doctests, updated eager load example
2007-11-08 00:26:23 +00:00
Ants Aasma 004345d7c9 updated zoomark test_1a_populate callcounts. if the calls keep being removed like this then soon there will be none left. 2007-11-08 00:11:46 +00:00
Mike Bayer 750439ac5f - fixed remainder of [ticket:853]
- bindparam 'shortname' is deprecated
- fixed testing.assert_compile() to actually generate bind param dict before asserting
- added bind param assertions to CRUDTest.test_update
2007-11-07 22:25:01 +00:00
Mike Bayer a914fae9db - <Engine|Connection>._execute_clauseelement becomes a public method
Connectable.execute_clauseelement
2007-11-07 21:56:23 +00:00
Mike Bayer 4b6881af55 - fix to compiled bind parameters to not mistakenly populate None
[ticket:853]
2007-11-07 21:48:16 +00:00
Mike Bayer f9c8d1cf34 fixed the previous TLTransaction checkin 2007-11-07 21:46:48 +00:00
Mike Bayer af070d037b - fixed the close() method on Transaction when using strategy='threadlocal' 2007-11-07 18:31:35 +00:00
Mike Bayer c1a9a96ba5 adjusted "blank out primary key" rule to check for "allow_null_pks" on target mapper. this was revealed by
recent attributes.py change in r3695 that causes a value of "None" to register as part of the attribute history's
added_items() collection (i.e. since AttributeHistory compares against NO_VALUE instead of None).
2007-11-07 01:36:16 +00:00
Mike Bayer e09fed05c7 - fixed very hard-to-reproduce issue where by the FROM clause of Query
could get polluted by certain generative calls [ticket:852]
2007-11-07 00:52:49 +00:00
Mike Bayer 7cd2c7d299 fixed/added coverage for list extension; [ticket:855] 2007-11-06 19:19:30 +00:00
Jason Kirtland 45d23d96c4 Added missing InternalError exception wrapper [ticket:854] 2007-11-06 18:21:28 +00:00
Paul Johnston 8f5d0cc4a2 Create a storage field for arbitrary info on tables/columns; ticket #573 2007-11-06 10:41:40 +00:00
Rick Morrison 5c060ab22a Added note about EXT_CONTINUE to 3.x -> 4.x migration guide 2007-11-05 22:52:21 +00:00
Jason Kirtland bcb586a93c Migrated maxdb behavioral assumptions from unsupported to fails_on 2007-11-05 22:11:30 +00:00
Jason Kirtland d36a3940a0 Added testing.fails_on('db') failure-asserter. 2007-11-05 22:02:00 +00:00
Mike Bayer 724eb54efd - figured out a way to get previous oracle behavior back. the ROWID thing
is still a pretty thorny issue.
2007-11-05 19:38:01 +00:00
Mike Bayer 5a70cb7fa5 - adjustments to oracle ROWID logic...recent oid changes mean we have to
use "rowid" against the select itself (i.e. its just...'rowid', no table name).
seems to work OK but not sure if issues will arise
- fixes to oracle bind param stuff to account for recent removal of ClauseParameters object.
2007-11-05 19:23:08 +00:00
Mike Bayer 00c2077cbc - oid_column proxies more intelligently off of Select, CompoundSelect - fixes platform-affected bugs in missing the correct "oid" column
- locate_all_froms() is expensive; added an attribute-level cache for it
- put a huge warning on all select.append_XXX() methods stating that derived collections like locate_all_froms() may become invalid if
already initialized
2007-11-05 18:30:30 +00:00
Jason Kirtland 5172668531 Whitespace cleanup 2007-11-05 17:26:17 +00:00
Mike Bayer 977e86dc9b - base_columns on ColumnElement becomes a list; as usual, because columns in CompoundSelects
may extend from more than one root column.
- keys_ok argument from corresponding_column() removed.  no more name-based matching of columns anywhere.
- DictDecorator is gone.  all row translators provided by orm.util.create_row_adapter().  Mapper
and contains_alias() cache the adapters on target mapper to avoid re-computation of adapters.
- create_row_adapter() accepts an "equivalent_columns" map as produced by Mapper, so that
row adapters can take join conditions into account (as usual again, to help with the CompoundSelects
produced by polymorphic_union).
- simplified TableSingleton to just provide lookup; moved all initialization into Table.
- the "properties" accessor on Mapper is removed; it now throws an informative
exception explaining the usage of mapper.get_property() and
mapper.iterate_properties
2007-11-05 17:18:21 +00:00
Jason Kirtland 43498def13 - SHOW CREATE TABLE output is slightly different if msyql is in ANSI mode 2007-11-05 17:15:41 +00:00
Mike Bayer 0e25c2d9e8 - rewrote and simplified the system used to "target" columns across
selectable expressions.  On the SQL side this is represented by the
"corresponding_column()" method. This method is used heavily by the ORM
to "adapt" elements of an expression to similar, aliased expressions,
as well as to target result set columns originally bound to a
table or selectable to an aliased, "corresponding" expression.  The new
rewrite features completely consistent and accurate behavior.
- the "orig_set" and "distance" elements as well as all associated
fanfare are gone (hooray !)
- columns now have an optional "proxies" list which is a list of all
columns they are a "proxy" for; only CompoundSelect cols proxy more than one column
(just like before).  set operations are used to determine lineage.
- CompoundSelects (i.e. unions) only create one public-facing proxy column per
column name.  primary key collections come out with just one column per embedded
PK column.
- made the alias used by eager load limited subquery anonymous.
2007-11-05 00:59:19 +00:00
Mike Bayer 303e753b2d - func. objects can be pickled/unpickled [ticket:844] 2007-11-04 22:04:22 +00:00
Mike Bayer 8ebc8ee5ba - eager loading with LIMIT/OFFSET applied no longer adds the primary
table joined to a limited subquery of itself; the eager loads now
join directly to the subquery which also provides the primary table's
columns to the result set.  This eliminates a JOIN from all eager loads
with LIMIT/OFFSET.  [ticket:843]
2007-11-03 23:17:34 +00:00
Mike Bayer 0af3f8f35b - rewritten ClauseAdapter merged from the eager_minus_join branch; this is a much simpler
and "correct" version which will copy all elements exactly once, except for those which were
replaced with target elements.  It also can match a wider variety of target elements including
joins and selects on identity alone.
2007-11-03 22:13:17 +00:00
Jason Kirtland 784eaa108a Added a profiled benchmark for orm attribute modification & flush 2007-11-03 22:03:18 +00:00
Jason Kirtland ede4b6c279 - Swap operator.eq for lambda x,y: x==y
- Some docstring re-formatting
2007-11-03 21:59:39 +00:00
Jason Kirtland 4f24608846 Formatting for 0.4.1 2007-11-03 20:53:37 +00:00
Jason Kirtland a6d121b6a8 Added some more notes for maxdb 2007-11-03 20:42:07 +00:00
Jason Kirtland 429e69db67 - Removed equality, truth and hash() testing of mapped instances. Mapped
classes can now implement arbitrary __eq__ and friends. [ticket:676]
2007-11-03 20:23:26 +00:00
Mike Bayer 4210a1ef23 - removed "name" attribute from FromClause, Join, Select, CompoundSelect. its needless
and led to some very strange anonymous label names
- removed what was apparently cruft in some column-targeting code
2007-11-03 20:12:59 +00:00
Rick Morrison 8dd51943a0 added small clarification on single-table inheritance mapper def 2007-11-03 16:11:41 +00:00
Mike Bayer 9cdc083ed1 doc updates for save_on_init=False, merge(...dont_save=True) 2007-11-02 21:02:22 +00:00
Mike Bayer d355ea021d - extra merge test
- merge with dont_load also propagates _state.modified
2007-11-02 18:05:06 +00:00
Mike Bayer 4161271a8c - merge() includes a keyword argument "dont_load=True". setting this flag will cause
the merge operation to not load any data from the database in response to incoming
detached objects, and will accept the incoming detached object as though it were
already present in that session.  Use this to merge detached objects from external
caching systems into the session.
2007-11-02 17:58:20 +00:00
Jason Kirtland ceb782ba49 More improvements to testlib's mapper decorator 2007-11-02 00:27:40 +00:00
Jason Kirtland 55c9b05cdb Tweaks for assert_unordered_result 2007-11-01 23:13:30 +00:00
Jason Kirtland c87295cdfd Added AssertMixin.assert_unordered_result 2007-11-01 20:59:30 +00:00
Mike Bayer 2e9501683d - session checks more carefully when determining "object X already in another session";
e.g. if you pickle a series of objects and unpickle (i.e. as in a Pylons HTTP session
or similar), they can go into a new session without any conflict
- added stricter checks around session.delete() similar to update()
- shored up some old "validate" stuff in session/uow
2007-11-01 20:12:36 +00:00
Jason Kirtland 1bf0aee385 Added rowset() testing helper. 2007-11-01 19:40:08 +00:00
Jason Kirtland ca73fbab87 Fixed truth-evaluation of mapped objects (part of [ticket:676] 2007-11-01 16:36:50 +00:00
Mike Bayer 66cd772094 - merged factor_down_bindparams branch.
- removed ClauseParameters object; compiled.params returns a regular dictionary
  now, as well as result.last_inserted_params()/last_updated_params().
- various code trimming, method removals.
2007-11-01 03:58:21 +00:00
Jason Kirtland ad9f8b8158 Added command line options to add tripwires for __hash__, __eq__ and __nonzero__ on mapped classes. 2007-10-31 23:49:09 +00:00
Jason Kirtland d5aa08160a - A more efficient IdentitySet 2007-10-31 19:53:27 +00:00
Mike Bayer 3f4d34b42c - split ScalarInstrumentedAttribute into a "scalar" and an "object" version.
The "object" version loads the existing value on set/del, fires events,
 and handles trackparent operations; the "scalar" version does not.
- column loaders now use the "scalar" version of InstrumentedAttribute, so that
event handlers etc. don't fire off for regular column attribute operations.
- some adjustments to AttributeHistory to work properly for non-loaded attributes
- deferred column attributes no longer trigger a load operation when the
attribute is assigned to.  in those cases, the newly assigned
value will be present in the flushes' UPDATE statement unconditionally.
2007-10-31 19:11:22 +00:00
Mike Bayer 60c3a1e8a2 dont call up new session unless save_on_init 2007-10-31 17:15:30 +00:00
Ants Aasma 5a1863d092 - also added proxying of save_or_update to scoped sessions.
- session.update() raises an error when updating an instance that is already in the session with a different identity
- adjusted zoomarks lower limits so I can get a nice clean run
2007-10-31 16:30:06 +00:00
Mike Bayer 3426d50d0c added REAL to reflection list 2007-10-31 15:52:33 +00:00
Ants Aasma b005aa4e60 Added proxying of __contains__ and __iter__ methods for scoped sessions 2007-10-31 12:14:22 +00:00
Paul Johnston 7ff65cd035 Fixup sp_columns call 2007-10-31 10:48:30 +00:00
Jason Kirtland 2d3f907ac0 Fixed a truncation error when re-assigning a subset of a collection
(obj.relation = obj.relation[1:]) [ticket:834]
2007-10-31 09:21:22 +00:00
Jason Kirtland 661774055c Added util.IdentitySet to support [ticket:676] and [ticket:834] 2007-10-31 09:13:12 +00:00
Jason Kirtland 0462f95f80 Formatting tweaks. 2007-10-31 05:44:17 +00:00
Jason Kirtland 52d5151aa4 - Removed unused util.hash()
- Fixed __hash__ for association proxy collections
2007-10-31 03:25:04 +00:00
Jason Kirtland 259b256759 - Don't re-save objects in these tests (post r3681) 2007-10-31 03:17:51 +00:00
Jason Kirtland c2092c7254 - Refinements for maxdb's handling of SERIAL and FIXED columns
- Expanded maxdb's set of paren-less functions
2007-10-31 00:28:53 +00:00
Jason Kirtland 8edda5baf6 - Added the NUMERIC sql type alias 2007-10-31 00:21:36 +00:00
Mike Bayer 206c0b9792 - fix to "row switch" behavior, i.e. when an INSERT/DELETE is combined into a
single UPDATE; many-to-many relations on the parent object update properly.
  [ticket:841]
- it's an error to session.save() an object which is already persistent
  [ticket:840]
- changed a bunch of repr(obj) calls in session.py exceptions to use mapperutil.instance_str()
2007-10-30 18:04:00 +00:00
Mike Bayer adcf8ea00d - restored MapperExtension functionality for [ticket:829], added test coverage
- changed naming convention in mapper.py tests to test_<testname>
2007-10-29 16:12:29 +00:00
Mike Bayer bbebcdf8f5 - fixed INSERT statements w.r.t. primary key columns that have SQL-expression
based default generators on them; SQL expression executes inline as normal
  but will not trigger a "postfetch" condition for the column, for those DB's
  who provide it via cursor.lastrowid
2007-10-28 21:28:53 +00:00
Mike Bayer 19fcf37483 - fixed expression translation of text() clauses; this repairs various
ORM scenarios where literal text is used for SQL expressions
2007-10-28 18:25:37 +00:00
Mike Bayer 103c867cf3 - a little cleanup to deferred inheritance loading 2007-10-28 04:36:36 +00:00
Mike Bayer 0282dc2738 - merged path_based_options branch
- behavior of query.options() is now fully based on paths, i.e. an option
  such as eagerload_all('x.y.z.y.x') will apply eagerloading to only
  those paths, i.e. and not 'x.y.x'; eagerload('children.children') applies
  only to exactly two-levels deep, etc. [ticket:777]
- removes old compiler()/schemagenerator()/schemadropper() methods from mysql dialect
2007-10-28 01:51:43 +00:00
Mike Bayer 1fee09fd07 - inlined a couple of context variables
- PG two phase was calling text() without the correct bind param format, previous compiler checkin revealed issue
2007-10-27 18:45:20 +00:00
Mike Bayer 4a4daad81a - removed regular expression step from most statement compilations.
also fixes [ticket:833]
- inlining on PG with_returning() call
- extra options added for profiling
2007-10-27 17:41:30 +00:00
Mike Bayer cba1e9a01e clear MapperStub ArgSingletons on clear_mappers() too... 2007-10-27 01:19:25 +00:00
Mike Bayer d5b86bbb8a - fixed eager calc endless loop, introduced by r3646? this issue should have been present before 3646 though. 2007-10-27 00:50:20 +00:00
Mike Bayer 4127d22196 - fixed clear_mappers() behavior to better clean up after itself 2007-10-27 00:24:43 +00:00
Paul Johnston d6568b4829 Make access dao detecting more reliable #828 2007-10-26 18:17:14 +00:00
Mike Bayer 1e7510645c remove unused method 2007-10-25 19:30:43 +00:00
Mike Bayer aa3c4067b7 - removed needless 'continue' 2007-10-25 16:58:02 +00:00
Mike Bayer b4e3ba70ce - refactored _compile_properties/_compile_property, removed redundant code.
still a little squirrely but much less complex.
- improved behavior of add_property() etc., fixed [ticket:831] involving
  synonym/deferred
2007-10-25 16:57:04 +00:00
Mike Bayer 763a70d531 - de-cruftified backref configuration code, backrefs which step on existing
properties now raise an error [ticket:832]
2007-10-24 23:25:47 +00:00
Jason Kirtland f64aac55cc A --db alias for max. 2007-10-24 07:55:18 +00:00
Jason Kirtland 6378c34799 - Added initial version of MaxDB dialect.
- All optional test Sequences are now optional=True
2007-10-23 07:38:07 +00:00
Jason Kirtland 21c6fa79b1 Fixed assert_sql_count exception logic. 2007-10-23 07:03:07 +00:00
Jason Kirtland 469c687381 Updated for r3655. 2007-10-23 01:57:40 +00:00
Jason Kirtland d89b2acdd7 Added support for dialects that have both sequences and autoincrementing PKs. 2007-10-23 01:47:21 +00:00
Jason Kirtland 8dfff3888f - The post_exec() hook now gets invoked before autocommit fires. 2007-10-23 01:16:27 +00:00
Jason Kirtland d433ec4f0d Changed some columns from TEXT to VARCHAR for sapdb. 2007-10-22 23:01:02 +00:00
Jason Kirtland 9d7c027cb7 - Now guarding against broken DB-APIs when wrapping their exceptions.
- Added an explicit test for exception wrapping.
2007-10-22 19:24:02 +00:00
Mike Bayer 20a7a172af - sqlite will reflect "DECIMAL" as a numeric column 2007-10-22 15:23:08 +00:00
Mike Bayer f3af574650 exception message, [ticket:826] 2007-10-22 15:20:15 +00:00
Mike Bayer 726bb2a24c bumped sqlite version for empty insert test to 3.4 2007-10-21 16:45:58 +00:00
Mike Bayer 0a3913c61a - the whole OperationContext/QueryContext/SelectionContext thing greatly scaled back;
all MapperOptions process the Query and that's it, one very simpliied QueryContext object gets passed
around at query.compile() and query.instances() time
- slight optimization to MapperExtension allowing the mapper to check for the presence of an extended method, takes 3000 calls off of masseagerload.py test (only a slight increase in speed though)
- attempting to centralize the notion of a "path" along mappers/properties, need to define what that is better.  heading towards [ticket:777]...
2007-10-21 16:36:34 +00:00
Jason Kirtland 565dfd3790 Excluding older sqlite versions from the new insert tests. Currently conservative- 2.8 definitely fails, 3.3 works. The 3.0 and 3.1 binaries from sqlite.org segfault for me so the version check should be revisited when possible. 2007-10-20 17:14:46 +00:00
Gaëtan de Menten ae553db3cd - Added contains operator (which generate a "LIKE %<other>%" clause).
- Added test coverage for endswith operator
2007-10-19 10:27:06 +00:00
Jason Kirtland 974f59f091 Fixed empty (zero column) sqlite inserts, allowing inserts on
autoincrementing single column tables.
2007-10-19 07:02:48 +00:00
Mike Bayer f32131dac2 - added test coverage for unknown type reflection, fixed
sqlite/mysql handling of type reflection for unknown types
2007-10-18 17:56:18 +00:00
Mike Bayer ed2d7dc805 typos, PassiveDefault likes "text()" otherwise the argument is a literal 2007-10-17 19:10:16 +00:00
Jason Kirtland 3e7f1cc9f3 Bump! 2007-10-17 18:04:33 +00:00
Mike Bayer a5e01cafdb put a little note at the top directing users to the more *exciting* changes 2007-10-17 17:53:12 +00:00
Jason Kirtland ccaa530fcb Update for 0.4.0 2007-10-17 17:41:59 +00:00
Mike Bayer cc41f0d3cf typo 2007-10-17 17:40:04 +00:00
Mike Bayer cb9321116f - removed __len__ from "dynamic" collection as it would require issuing
a SQL "count()" operation, thus forcing all list evaluations to issue
  redundant SQL [ticket:818]
2007-10-17 16:49:15 +00:00
Jason Kirtland 14c9d63672 Included 0.3.11 changelog 2007-10-17 16:04:58 +00:00
Paul Johnston a0748b3d66 Make ActiveMapper support viewonly property 2007-10-17 11:23:39 +00:00
Jason Kirtland 5154d30418 - Added support for UPDATE with LIMIT on mysql.
- Added mysql dialect tests for SET columns and the in_ operator.
2007-10-16 23:59:05 +00:00
Ants Aasma 6bbc7dd157 change the in_ API to accept a sequence or a selectable [ticket:750] 2007-10-16 22:57:05 +00:00
Mike Bayer cc0e7c796f - string-based query param parsing/config file parser understands
wider range of string values for booleans [ticket:817]
2007-10-16 21:33:21 +00:00
Jason Kirtland a54deba01d Typo fix (thanks Ben!) 2007-10-16 21:11:39 +00:00
Mike Bayer 9b22fc0a9b - Fixed SQL compiler's awareness of top-level column labels as used
in result-set processing; nested selects which contain the same column
  names don't affect the result or conflict with result-column metadata.

- query.get() and related functions (like many-to-one lazyloading)
  use compile-time-aliased bind parameter names, to prevent
  name conflicts with bind parameters that already exist in the
  mapped selectable.
2007-10-16 16:03:59 +00:00
Jason Kirtland 9226871456 Fixed a couple of typos & hardened against future similar errors. 2007-10-16 01:17:39 +00:00
Mike Bayer 7fac6542c5 - inline optimizations added to locate_dirty() which can greatly speed up
repeated calls to flush(), as occurs with autoflush=True [ticket:816]
2007-10-15 17:24:08 +00:00
Gaëtan de Menten dac7ab4a0f fix typo in examples 2007-10-15 08:51:23 +00:00
Mike Bayer f64768658d - much more query verbiage
- contains() operator doesn't need to generate negation criterion unless
many-to-many
2007-10-15 01:54:04 +00:00
Mike Bayer e57c94c64f documenting PropComparator behavior in orm tutorial 2007-10-14 20:34:39 +00:00
Mike Bayer 5b92c3d097 - PG reflection, upon seeing the default schema name being used explicitly
as the "schema" argument in a Table, will assume that this is the the
  user's desired convention, and will explicitly set the "schema" argument
  in foreign-key-related reflected tables, thus making them match only
  with Table constructors that also use the explicit "schema" argument
  (even though its the default schema).
  In other words, SA assumes the user is being consistent in this usage.
2007-10-14 19:15:09 +00:00
Jason Kirtland 79dafdf3d4 Restored unicode foreign key tests for [ticket:729]. 2007-10-14 18:35:51 +00:00
Mike Bayer b56ed8ccb8 - backref remove object operation doesn't fail if the other-side
collection doesn't contain the item, supports noload collections
[ticket:813]
2007-10-14 18:06:13 +00:00
Jason Kirtland dda6a5f5a8 Added a missing @supported. 2007-10-14 17:20:13 +00:00
Mike Bayer 9f856b88f6 - attempted to add a test for #622 / #751, but cant reproduce the failing condition
- fixed major oracle bug introduced by r3561, since colnames come back as unicode now,
need to encode keys in setinputsizes() step
2007-10-14 16:56:32 +00:00
Jason Kirtland be6f2a9c5b Added test for DBAPIError exception wrapping. 2007-10-14 16:31:21 +00:00
Jason Kirtland 0f49157bc9 Adjusted zoomark ranges for 2.4 and 2.5. 2007-10-13 22:18:35 +00:00
Jason Kirtland f63a3826bd Moved author comment preventing python 2.3 from importing the module... 2007-10-13 21:42:08 +00:00
Jason Kirtland 0c31d3bd1f Snipped another generator expression. 2007-10-13 21:37:49 +00:00
Jason Kirtland 302b10253c Converted errant generator expression. 2007-10-13 21:22:13 +00:00
Jason Kirtland 666997c8fa Assorted unused imports from pyflakes, docstring tweaks, formatting. 2007-10-13 21:03:49 +00:00
Jason Kirtland 059b939dba Winnowed __all__ for 'import * from sqlalchemy.engine' 2007-10-13 20:50:04 +00:00
Jason Kirtland 810e29e340 Unused imports. 2007-10-13 20:40:05 +00:00
Jason Kirtland 224ff658e0 set svn:eol-style native 2007-10-13 20:35:42 +00:00
Jason Kirtland 9f525a0145 Fixed reference bug in Connect, switched docstring format 2007-10-13 20:26:24 +00:00
Jason Kirtland 05b07dfb17 Removed a tab. 2007-10-13 20:11:36 +00:00
Jason Kirtland 0b73be7b46 - sybase cleanups: unused imports, adjusted docstrings, trailing whitespace
- added sybase dialect test
- added sybase and access to generated docs
2007-10-13 20:10:14 +00:00
Paul Johnston a0ed498ff5 Fix ActiveMapper unit tests 2007-10-13 00:01:11 +00:00
Paul Johnston aafe57ab5d A few fixes to the access dialect 2007-10-12 23:39:28 +00:00
Paul Johnston 2585a470c0 Make ActiveMapper use scoped_session instead of SessionContext 2007-10-12 20:53:20 +00:00
Paul Johnston 2821adc57c Fix broken update/delete queries on MSSQL when tables have a schema 2007-10-12 20:08:54 +00:00
Jason Kirtland 833503ed23 More ORDER BY, now with use_labels. 2007-10-12 18:08:11 +00:00
Jason Kirtland df929d364d - Added small DESC exercise to test_order_by. 2007-10-12 17:46:42 +00:00
Roger Demetrescu 7f36007477 firebird: Fixed reflection for Integer and Numeric (after introducing Float column type). Need to review 'column_func' from reflecttable() and write some unit tests... 2007-10-12 12:53:33 +00:00
Roger Demetrescu 69ba01265e Firebird: added Float and Time types (FBFloat and FBTime). Fixed BLOB SUB_TYPE for TEXT and Binary types.
Firebird's string types are tested in testtypes.py
2007-10-12 06:02:15 +00:00
Jason Kirtland d174b3bd72 Added query coverage for ORDER BY over regular, aliased and DISTINCT columns. 2007-10-11 22:21:33 +00:00
Jason Kirtland c1a8db6791 - sqlite housekeeping- added dialect test & moved tests there, pruned the dialect's reserved words. 2007-10-11 17:35:13 +00:00
Rick Morrison ed0bf8fce7 applied patch for pymssql 30-char limit 2007-10-10 22:25:24 +00:00
Jason Kirtland 14563004a4 - Expanded JoinTest further, exercising joins the ORM depends on explicitly
in the 'sql' tests.
2007-10-10 20:42:58 +00:00
Jason Kirtland d3fb340bc2 - Fixed oracle 'use_ansi'-via-engine-url handling, added support for 'mode=sysdba' et al. 2007-10-10 18:49:36 +00:00
Gaëtan de Menten ba1f5c45ef similar type optimization for the Interval type 2007-10-10 16:15:29 +00:00
Gaëtan de Menten 1d05d76cad Made the PickleType slightly faster. 2007-10-10 15:58:10 +00:00
Ants Aasma 6278f14da1 make two-phase transactions work a bit better by letting psycopg do whatever it does for implicit
transaction starts
2007-10-10 15:38:01 +00:00
Mike Bayer 1f53023f54 changelog 2007-10-10 15:28:45 +00:00
Mike Bayer ddf875dd0c - initial sybase support checkin, [ticket:785] 2007-10-10 15:19:28 +00:00
Mike Bayer 74064ce9fd - fixed Oracle non-ansi join syntax 2007-10-10 02:30:00 +00:00
Jason Kirtland da45a65a48 - Expanded insert speed test to cover execute as well as executemany 2007-10-08 18:51:51 +00:00
Jason Kirtland 57a7a52b56 - Cleaned up visit_insert a bit, inserts are ~3% faster now. 2007-10-08 18:19:02 +00:00
Ants Aasma c931b9d9d1 - a better fix for [ticket:810]. The cause was two phase commit/rollback not opening a new transaction as the dbapi implementations do. 2007-10-08 16:11:08 +00:00
Ants Aasma 47d3f45d58 - fix multiple consequent two phase transactions not working with postgres. For some reason implicit transactions are not enough. [ticket:810]
- add an option to scoped session mapper extension to not automatically save new objects to session.
2007-10-08 15:25:51 +00:00
Roger Demetrescu 0010807253 Firebird now uses dialect.preparer to format sequences names.
UnicodeTest (reflection.py) runs ok now.
2007-10-08 05:07:54 +00:00
Jason Kirtland 2b084b76c0 - Expanded the outer join tests, now covering a situation that looked like it would be wonky in oracle. 2007-10-08 03:31:52 +00:00
Jason Kirtland afa713d9e8 - Loosened up test_cant_execute_join for oracle (probably) + bonus typo fix
- Some docstring formatting waiting for pg to finish the tests...  waiting...
2007-10-08 03:01:04 +00:00
Jason Kirtland 849743acac - Added some outerjoin() execution exercises to the query tests. 2007-10-08 02:17:07 +00:00
Jason Kirtland 0d9d87560b - No longer using reserved-ish column names in MutableTypesTest. 2007-10-07 23:55:59 +00:00
Jason Kirtland 157e43bfad - Actually fixed that testcascade issue. And friends- looks like a cut-n-paste-o. 2007-10-07 23:10:14 +00:00
Jason Kirtland 9db4d1f8f5 - Fixed bogus testcascade, also made fixture sequences optional. 2007-10-07 22:43:27 +00:00
Jason Kirtland 585b425850 - Squashed assumption of transparent type coercion support in defaults test 2007-10-07 21:57:42 +00:00
Jason Kirtland 982b622a37 - Rewrote autoincrement tests: added new scenarios, changed the orm fetchid to explicit test of last_row_ids(), and now testing transactional/autocommit modes separately to help catch any subtle issues that may exist due to assumed cursor state during post_exec(). 2007-10-07 18:54:05 +00:00
Jason Kirtland a014d289f6 - Tweaked unordered select tests to not be sensitive to result set order, also split apart some tests (aliases) that sapdb has problems with. 2007-10-07 02:01:49 +00:00
Mike Bayer b27ddac20a - fix to anonymous label generation of long table/column names [ticket:806] 2007-10-07 01:58:40 +00:00
Roger Demetrescu 11c70f9d8d Firebird dialect now uses SingletonThreadPool as its poolclass.
(this fixes all "unsuccessful metadata update\n  object XXXXX is in use" test errors)

Minor fixes in tests
2007-10-07 00:06:05 +00:00
Mike Bayer 29291f4c32 - oracle does not implicitly convert to unicode for non-typed result
sets (i.e. when no TypeEngine/String/Unicode type is even being used;
  previously it was detecting DBAPI types and converting regardless).
  should fix [ticket:800]
- fixed oracle out_parameters, likely broke in beta6
- fixed oracle _normalize_case for encoded names, gets unicode reflection test to work
- a few extra tests tweaked/unsupported for oracle
2007-10-06 16:12:58 +00:00
Ants Aasma d87ecbdd00 a typo on postgres returning test version check - change <8.4 -> <8.2 2007-10-06 01:31:22 +00:00
Mike Bayer f97414b520 - null foreign key on a m2o doesn't trigger a lazyload [ticket:803]
- slight simpliication to mapper.populate_instance()
- lamenting the different codepaths between query._get() and DeferredLoader.lazyload()
- query._get() uses all()[0] for single-row load to avoid complexity of first() (same as LazyLoader)
2007-10-06 01:12:19 +00:00
Mike Bayer 6695831403 - fixed sqlite reflection of BOOL/BOOLEAN [ticket:808] 2007-10-05 18:14:11 +00:00
Jason Kirtland af0ca25d10 - Sequences gain a basic dialect-specific kwargs bucket, like Tables. 2007-10-04 21:03:29 +00:00
Jason Kirtland 6fb5f1047c Adjusted reserved word reflection test for oracle-style identifier dialects. But probably the CheckConstraint part of this test should just be removed, as it's testing a non-extant feature. 2007-10-04 18:31:31 +00:00
Jason Kirtland d154266a65 - Fixed convert_result_value/bind_param compatibility for types without processors. 2007-10-04 17:47:29 +00:00
Roger Demetrescu 2ca3c38c99 Firebird now passes all tests from /test/engine/reflection.py (except UnicodeTest).
* FBDialect now mimics OracleDialect, regarding case-sensitivity of TABLE and COLUMN names
 * FBDialect.table_names() doesn't bring system tables (ticket #796)
 * FB now reflects Column's nullable property correctly.
2007-10-04 04:26:57 +00:00
Mike Bayer f67fcd1822 - move PG RETURNING tests to postgres dialect test
- added server_version_info() support for PG dialect
- exclude PG versions < 8.4 for RETURNING tests
2007-10-04 03:19:38 +00:00
Ants Aasma c25fa30feb Made the regexp detecting the returning token more readable and fixed a couple of corner cases 2007-10-03 01:06:07 +00:00
Ants Aasma e82ca71cc5 add support for returning results from inserts and updates for postgresql 8.2+. [ticket:797] 2007-10-02 23:57:54 +00:00
Jason Kirtland 73b0c40946 - Tweaked the sql.text date test 2007-10-02 20:58:58 +00:00
Jason Kirtland 1880e6d8c3 - Unraveled DateTest, removed bogus coverage omission for MySQL TIME types 2007-10-02 20:51:17 +00:00
Mike Bayer ee2247f78f removed unused _fold_identifier_case method 2007-10-02 15:49:12 +00:00
Roger Demetrescu 35ff8ed25e Some fixes on reflection tests (firebird):
* firebird doesn't support create table tablename (columnname type NULL)" syntax (only NOT NULL)
 * firebird doesn't support schemas
2007-10-02 04:44:20 +00:00
Jason Kirtland f0c535403f - The no-arg ResultProxy._row_processor() is now the class attribute
`_process_row`.
- Further tiny cleanups in RoxProxy.
2007-10-02 01:40:43 +00:00
Jason Kirtland bddc370079 - Removed duplicate RowProxy.__len__ definition and some range(0, 1) lint 2007-10-02 00:02:02 +00:00
Mike Bayer 6d0cf81f74 - ident passed to id_chooser in shard.py always a list 2007-10-01 19:55:50 +00:00
Ants Aasma fe3178e224 Make the postgres_where attribute to Index private to postgres module by using a kwargs attribute on the Index. 2007-10-01 16:29:26 +00:00
Mike Bayer 0dde728591 - fixed three- and multi-level select and deferred inheritance
loading (i.e. abc inheritance with no select_table), [ticket:795]
2007-09-29 20:17:40 +00:00
Ants Aasma 4924007317 - added partial index support for postgres
- fixed create and drop methods on MockConnection
2007-09-29 06:21:34 +00:00
Mike Bayer 51b32cd809 - more docstrings
- Selectable is only used as a marker for FromClause (probably should be
removed/both classes merged)
2007-09-28 17:20:24 +00:00
Mike Bayer e4d7b12c41 - doc updates. generated sql docs are against sql.expression now.
- added SessionExtension docs.
- removed old sqlconstruction doc.
- changed 'copy_collections' flag in Select to '_copy_collections'; its
not really "public".
2007-09-28 16:37:52 +00:00
Roger Demetrescu a32ad7b448 firebird doesn't support sane rowcount. 2007-09-28 02:43:41 +00:00
Jason Kirtland 30f0f0cf5d - Explicitly close connections in the FOR UPDATE tests.
- Minor housekeeping.
2007-09-27 23:23:27 +00:00
Jason Kirtland af248f41cc Dropped the leading '__' from generated savepoint names, '_' isn't universally allowed as an initial character for identifiers. 2007-09-27 21:27:53 +00:00
Mike Bayer 475bfc04ac bump to beta7, but might become 0.4.0 2007-09-27 17:30:52 +00:00
Jason Kirtland c1221b7cd3 - The IdentifierPreprarer's _requires_quotes test is now regex based.
Any out-of-tree dialects that provide custom sets of legal_characters
  or illegal_initial_characters will need to move to regexes or override
  _requires_quotes.
2007-09-27 17:23:41 +00:00
Mike Bayer e3b06dc83e zoomark adjustment for the pybot... 2007-09-27 00:28:58 +00:00
Mike Bayer 86a93ee7db test coverage has been added... 2007-09-27 00:20:17 +00:00
Mike Bayer 9f7526df52 - adjust server side logic to work with standalone default execution
- a little bit of inlining of same
2007-09-27 00:17:07 +00:00
Mike Bayer 5d93c8256b - some docstrings for select()
- fixed tutorial doctests to adjust for execution changes, session being weak-referencing
(reloads objects more frequently which get u'' applied to their __repr__())
2007-09-26 23:47:55 +00:00
Jason Kirtland 6f3f85e48c Changed MySQL dialect to use the older LIMIT <offset>, <limit> syntax instead
of LIMIT <l> OFFSET <o> for folks using 3.23. ([ticket:794], thanks for the
patch!)
2007-09-26 23:37:11 +00:00
Jason Kirtland 3a04b7e4d4 Avoid tickling the MySQL-python 1.2.2 executemany parsing bug on a couple tests. 2007-09-26 23:35:14 +00:00
Mike Bayer 32ee73460d add micro-doc for sequence standalone execution 2007-09-26 23:29:11 +00:00
Mike Bayer c13eb665aa - added "FETCH" to the keywords detected by Postgres to indicate a result-row holding
statement (i.e. in addition to "SELECT").
2007-09-26 20:32:19 +00:00
Jason Kirtland ff2a8f24f5 Formatting tweaks 2007-09-26 17:40:46 +00:00
Mike Bayer 1d081a8dec fix comment 2007-09-26 17:25:23 +00:00
Mike Bayer 3e672bbfc8 - created a link between QueryContext and SelectionContext; the attribute
dictionary of QueryContext is now passed to SelectionContext inside
of Query.instances(), allowing messages to be passed between the two stages.
- removed the recent "exact match" behavior of Alias objects, they're back to
their usual behavior.
- tightened up the relationship between the Query's generation
  of "eager load" aliases, and Query.instances() which actually grabs the
  eagerly loaded rows.  If the aliases were not specifically generated for
  that statement by EagerLoader, the EagerLoader will not take effect
  when the rows are fetched.  This prevents columns from being grabbed accidentally
  as being part of an eager load when they were not meant for such, which can happen
  with textual SQL as well as some inheritance situations.  It's particularly important
  since the "anonymous aliasing" of columns uses simple integer counts now to generate
  labels.
2007-09-26 17:08:19 +00:00
Jason Kirtland 9d16ae440b Tightened up time measurement. 2007-09-26 16:13:43 +00:00
Mike Bayer e37a3a961c - the behavior of String/Unicode types regarding that they auto-convert
to TEXT/CLOB when no length is present now occurs *only* for an exact type
  of String or Unicode with no arguments.  If you use VARCHAR or NCHAR
  (subclasses of String/Unicode) with no length, they will be interpreted
  by the dialect as VARCHAR/NCHAR; no "magic" conversion happens there.
  This is less surprising behavior and in particular this helps Oracle keep
  string-based bind parameters as VARCHARs and not CLOBs [ticket:793].
2007-09-26 14:55:44 +00:00
Mike Bayer a0838e0c47 - columns from Alias objects, when used to target result-row columns, must match exactly
to the label used in the generated statement.  This is so searching for columns in a
  result row which match aliases won't accidentally match non-aliased columns.
  fixes errors which can arise in eager loading scenarios.
2007-09-24 19:27:52 +00:00
Mike Bayer 6b5543995b - added session.is_modified(obj) method; performs the same "history" comparison operation
as occurs within a flush operation; setting include_collections=False gives the same
  result as is used when the flush determines whether or not to issue an UPDATE for the
  instance's row.
2007-09-24 14:51:23 +00:00
Mike Bayer 8c9795d9e4 added test coverage for r3512 2007-09-24 13:51:44 +00:00
Mike Bayer a4be5e0996 found an errant 2-tuple... 2007-09-24 13:43:38 +00:00
Mike Bayer 94aa03c63f fixed session extension bug [ticket:757] 2007-09-22 18:32:59 +00:00
Mike Bayer 4be3b2664d fixed firebird visit_alias [ticket:779] 2007-09-22 18:13:23 +00:00
Jason Kirtland 75c2230476 Added. 2007-09-22 18:03:27 +00:00
Mike Bayer 32dcfdf808 - added "schema" argument to Sequence; use this with Postgres /Oracle when the sequence is
located in an alternate schema.  Implements part of [ticket:584], should fix [ticket:761].
2007-09-22 17:14:15 +00:00
Mike Bayer 6b0a907fbd - merged sa_entity branch. the big change here is the attributes system
deals primarily with the InstanceState and almost never with the instrumented object
directly.  This reduces lookups and complexity since we need the state for just about
everything, now its the one place for everything internally.
we also merged the new weak referencing identity map, which will go out in beta6 and
we'll see how that goes !
2007-09-22 16:55:36 +00:00
Mike Bayer 7f6bf93da8 - added 'comparator' keyword argument to PickleType. By default, "mutable"
PickleType does a "deep compare" of objects using their dumps() representation.
  But this doesn't work for dictionaries.  Pickled objects which provide an
  adequate __eq__() implementation can be set up with "PickleType(comparator=operator.eq)"
  [ticket:560]
2007-09-22 15:55:02 +00:00
Paul Johnston 927d5d8929 Don't use unicode with pyodbc on UCS-4 platforms [ticket:787] 2007-09-19 21:46:24 +00:00
Ants Aasma 68ce4b1636 oops, forgot to commit CHANGES 2007-09-17 15:46:42 +00:00
Ants Aasma 9472f3bdba add sqlite reserved words list 2007-09-17 15:43:02 +00:00
Jason Kirtland 50d86d3cf8 Yet more formatting updates 2007-09-11 14:20:29 +00:00
Jason Kirtland e84db3d632 Formatting tweaks. 2007-09-10 15:59:58 +00:00
Mike Bayer 9d5264f9f9 - added 'passive_deletes="all"' flag to relation(), disables all
nulling-out of foreign key attributes during a flush where the parent
  object is deleted.

- fix to FK compile fix from yesterday
2007-09-09 17:01:38 +00:00
Mike Bayer fbd0a0c725 [ticket:728] foreign key checks for existing reflected FK and replaces itself 2007-09-08 21:09:50 +00:00
Jason Kirtland dd162c4b30 Tweaked changelog 2007-09-08 20:32:14 +00:00
Mike Bayer 8c14291b6d - adjusted operator precedence of NOT to match '==' and others, so that
~(x <operator> y) produces NOT (x <op> y), which is better compatible with MySQL.
   [ticket:764].  this doesn't apply to "~(x==y)" as it does in 0.3 since ~(x==y)
   compiles to "x != y", but still applies to operators like BETWEEN.
2007-09-08 20:28:26 +00:00
Mike Bayer 5ba8a25afe added assertion case for [ticket:764] 2007-09-08 20:13:54 +00:00
Mike Bayer 0ebccb4abd [ticket:768] dont assume join criterion consists only of column objects 2007-09-08 20:08:41 +00:00
Mike Bayer fc753a0647 - fixes to ShardedSession to work with deferred columns [ticket:771].
- user-defined shard_chooser() function must accept "clause=None"
  argument; this is the ClauseElement passed to session.execute(statement)
  and can be used to determine correct shard id (since execute() doesn't
  take an instance)
2007-09-08 19:51:35 +00:00
Jason Kirtland bf35590e15 Removed DefaultDialect.ischema and information_schema's ISchema (which incidentally had a 'toengine' in it...) 2007-09-07 23:13:16 +00:00
Mike Bayer 179da3ecd9 - merged the unit test for the column_prefix fix that was established in the 0.3
trunk in r2795.  the actual "fix" part of it I just happened to spot manually
the other day and fixed without testing (forgot what the original failure condition was) in r3449.
2007-09-07 19:57:01 +00:00
Jason Kirtland 0718103370 Fixed repr() of mysql floats [ticket:775]
Added repr testing to mysql dialect
2007-09-07 17:58:47 +00:00
Jason Kirtland 64b0267d28 Added 'collection_iter', like 'iter', for anything that implements the @collection.iterator or __iter__ interface. 2007-09-07 00:30:53 +00:00
Jason Kirtland 9206d7ed5a Doc updates. 2007-09-07 00:21:20 +00:00
Jason Kirtland 22f48d6925 Updated ignores. 2007-09-07 00:20:51 +00:00
Jason Kirtland a072e50c9d mysql SETs and ENUMs now unescape embedded quotes before storage in .enums and .values. An ancient bug. 2007-09-06 18:46:53 +00:00
Mike Bayer 4245d75885 associationproxy relies upon a "sweep" through the attributes at the class level,
restored the equivalent functionality from previous releases
2007-09-06 16:58:02 +00:00
Mike Bayer 986e82700e - column defaults and onupdates, executing inline, will add parenthesis
for subqueries and other parenthesis-requiring expressions
2007-09-06 16:37:37 +00:00
Mike Bayer 5edafbe955 do the cheaper check first.... 2007-09-05 23:05:39 +00:00
Jason Kirtland c3c79b997b Fixed reflection of the empty string for mysql enums. 2007-09-05 19:39:07 +00:00
Mike Bayer 9717c71709 merged current entity_management brach r3457-r3462. cleans up
'_state' mamangement in attributes, moves __init__() instrumntation into attributes.py,
and reduces method call overhead by removing '_state' property.
future enhancements may include _state maintaining a weakref to the instance and a
strong ref to its __dict__ so that garbage-collected instances can get added to 'dirty',
when weak-referenced identity map is used.
2007-09-05 17:25:32 +00:00
Paul Johnston 43a927f6e0 Small change in ActiveMapper to make it work with Python 2.3 2007-09-04 22:35:00 +00:00
Mike Bayer 07fad643a6 remove unused method 2007-09-04 18:43:53 +00:00
Mike Bayer a504b78e6c buildbot reported a lower number for test 1a... 2007-09-04 18:20:48 +00:00
Mike Bayer 5800950add - took out method calls for oid_column
- reduced complexity of parameter handling during execution; __distill_params does all
parameter munging, executioncontext.parameters always holds a list of parameter structures
(lists, tuples, or dicts).
2007-09-04 18:07:16 +00:00
Paul Johnston a783ebea36 Set supports_sane_multi_rowcount for MSSQL 2007-09-04 14:45:36 +00:00
Mike Bayer 3126d464e7 - removed "parameters" argument from clauseelement.compile(), replaced with
"column_keys".  the parameters sent to execute() only interact with the
  insert/update statement compilation process in terms of the column names
  present but not the values for those columns.
  produces more consistent execute/executemany behavior, simplifies things a
  bit internally.
2007-09-04 00:08:57 +00:00
Mike Bayer c9924a4a14 - various cruft removal and optimizations to load process.
removes about 15K method calls from masseagerload.py test.
2007-09-03 18:18:09 +00:00
Paul Johnston e75ec97c2b Minor fixes to MSSQL reflection 2007-09-03 13:57:08 +00:00
Mike Bayer e92d5cff7e - mapper compilation has been reorganized such that most compilation
occurs upon mapper construction.  this allows us to have fewer
  calls to mapper.compile() and also to allow class-based properties
  to force a compilation (i.e. User.addresses == 7 will compile all
  mappers; this is [ticket:758]).  The only caveat here is that
  an inheriting mapper now looks for its inherited mapper upon construction;
  so mappers within inheritance relationships need to be constructed in
  inheritance order (which should be the normal case anyway).
2007-09-02 19:55:33 +00:00
Mike Bayer 9c76fa6cb5 lowered value, fewer calls on pybot ? 2007-09-01 23:10:13 +00:00
Mike Bayer da8d7c730c entity refs 2007-09-01 23:08:30 +00:00
Mike Bayer f6819fa9ae edits 2007-09-01 23:07:46 +00:00
Mike Bayer 46cf32fc10 added zoomark profile 2007-09-01 22:48:07 +00:00
Mike Bayer fab4615d6d adjusting firebird, obviously needs someone to test 2007-09-01 22:46:16 +00:00
Mike Bayer d6c6f72274 fix to oracle sequence exec 2007-09-01 22:44:14 +00:00
Mike Bayer 8386fc6dc5 sequence pre-executes dont create an ExecutionContext, use straight cursor 2007-09-01 22:42:51 +00:00
Mike Bayer e04535a79a factored out uses_sequences_for_inserts() into
preexecute_sequence dialect attribute
2007-09-01 21:25:46 +00:00
Mike Bayer 5df1759e15 - got all examples working
- inline default execution occurs for *all* non-PK columns
unconditionally - preexecute only for non-executemany PK cols on
PG, Oracle, etc.
- new default docs
2007-09-01 21:21:29 +00:00
Mike Bayer 61f2ca2c9d whats a big commit without some errant print statements ? :) 2007-09-01 19:51:25 +00:00
Mike Bayer 69f7084c9b - merged inline inserts branch
- all executemany() style calls put all sequences and SQL defaults inline into a single SQL statement
and don't do any pre-execution
- regular Insert and Update objects can have inline=True, forcing all executions to be inlined.
- no last_inserted_ids(), lastrow_has_defaults() available with inline execution
- calculation of pre/post execute pushed into compiler; DefaultExecutionContext greatly simplified
- fixed postgres reflection of primary key columns with no sequence/default generator, sets autoincrement=False
- fixed postgres executemany() behavior regarding sequences present, not present, passivedefaults, etc.
- all tests pass for sqlite, mysql, postgres; oracle tests pass as well as they did previously including all
insert/update/default functionality
2007-09-01 19:49:26 +00:00
Mike Bayer 15ab87994c update on pool status 2007-08-31 20:57:12 +00:00
Mike Bayer ea13716acc - fixed bugs in determining proper sync clauses from custom inherit
conditions [ticket:769]
2007-08-30 15:55:41 +00:00
Mike Bayer 49afdf64d8 remove() should issue a close() on existing session 2007-08-30 00:22:17 +00:00
Jason Kirtland 3d9d21cc14 Extended 'engine_from_config' coercion for QueuePool size / overflow. [ticket:763]
Added a set of coercion tests.
2007-08-29 23:17:36 +00:00
Jason Kirtland 83d05aa5af engine.url cleanups [ticket:742]
- translate_connect_args can now take kw args or the classic list
- in-tree dialects updated to supply their overrides as keywords
- tweaked url parsing in the spirit of the #742 patch, more or less
2007-08-29 22:27:45 +00:00
Jason Kirtland cdde69e7bd Fix for scoped_session's mapper(extension=<scalar>) [ticket:760] 2007-08-29 20:23:02 +00:00
Jason Kirtland 59ace15998 Catch-up entries for b5. 2007-08-28 23:52:48 +00:00
Jason Kirtland 731dd1e96f - Restored reflection for mysql VIEWs [ticket:748]
- Fixed anonymous pk reflection for mysql 5.1
- Tested table and view reflection against the 'sakila' database from
  MySQL AB on 3.23 - 6.0. (with some schema adjustments, obviously)
  Maybe this will go into the SA test suite someday.
- Tweaked mysql server version tuplification, now also splitting on hyphens
- Light janitorial
2007-08-28 23:44:00 +00:00
Jason Kirtland b11fde9cff Fixed OrderedProperties pickling [ticket:762] 2007-08-28 15:17:26 +00:00
Jason Kirtland 9c7a423f6c Fixed signature for orm's BETWEEN operator. 2007-08-27 19:14:26 +00:00
Gaëtan de Menten c64e0e6be3 fix typos in assoc_proxy doc 2007-08-27 10:15:36 +00:00
Jason Kirtland 6ebcdbd6d8 Expand custom assocproxy getter/setter support to scalar proxies 2007-08-23 15:48:51 +00:00
Jason Kirtland 51dc8b088d Allow custom getter/setters to be specified for a standard AssociationProxy 2007-08-23 15:35:03 +00:00
Jason Kirtland 433b34f036 Fix for reflecting mysql keys that have USING 2007-08-22 22:20:56 +00:00
Mike Bayer 8034ccd28c bump 2007-08-22 20:39:32 +00:00
Mike Bayer 7f296cb94b - ugh ! beta4 is double logging....fixed that....
- added test/fixed eager aliasizing for self-referential m2m relations
2007-08-22 20:35:40 +00:00
Mike Bayer 8b80f9132d changeset about connection pool 2007-08-22 19:36:54 +00:00
Mike Bayer e755e90681 restored WeakValueDict for threadlocal connections + profiler test, addressing
[ticket:754]
2007-08-22 19:14:30 +00:00
Mike Bayer d6fcdb5da0 - a "collection-holding" InstrumentedAttribute is now identified
by the presence of a "get_collection" method.
- added "get_collection" to DynamicCollectionAttribute so its
treated as a collection.
2007-08-22 18:21:25 +00:00
Mike Bayer d3917532bc - tightened down the screws on logging a little bit 2007-08-22 18:08:10 +00:00
Mike Bayer 37b9857f4d - added **modifiers to _get_from_objects
- fixed up PG distinct flag
2007-08-22 17:53:01 +00:00
Mike Bayer e2bb1503bf - restored engine.echo flag
- changelog
2007-08-22 16:58:26 +00:00
Jason Kirtland ea44deff14 Adjusted ColumnDefault default function fitness check to only insure that a given function had no more than one non-defaulted positional arg. 2007-08-22 15:15:52 +00:00
Jason Kirtland 06881d849d Housekeeping. 2007-08-22 08:33:09 +00:00
Jason Kirtland f981981235 Added a test for the SELECT DISTINCT ON postgresqlism.
Test currently fails due to two problems in postgres.py, but I'm leaving
it uncorrected for now as its not clear what the original intent was
for lists.
2007-08-22 07:33:03 +00:00
Jason Kirtland d8f2ca2dc9 Deleting an entity having a dynamic loader with cascade="all" has some issues at the moment. 2007-08-22 05:40:50 +00:00
Jason Kirtland d80751ceb3 Association example updates, round two. 2007-08-22 04:20:14 +00:00
Jason Kirtland 6ccb0f9f57 basic 0.4 update 2007-08-22 03:25:09 +00:00
Jason Kirtland 372e69ee08 For sqlite NUMERIC, send Decimal bind values as strings instead of converting to floats. 2007-08-22 00:21:06 +00:00
Mike Bayer 8ebbfae0a4 fixed imports 2007-08-21 23:42:34 +00:00
Mike Bayer f9e4af9830 -removed echo_property() function, moved logging checks to
static variables
2007-08-21 23:06:12 +00:00
Jason Kirtland fba14f6047 from foo import (name, name) isn't valid syntax for 2.3. ah well.
omitting modules from sqlalchemy.__all__...
2007-08-21 22:20:52 +00:00
Jason Kirtland 0051349d09 Updated adjencytree examples 2007-08-21 21:05:23 +00:00
Mike Bayer 72efa9db3a fixed generative behavior to copy collections, [ticket:752] 2007-08-21 20:43:54 +00:00
Mike Bayer b8e92e6692 added first profile tests per [ticket:753] 2007-08-21 19:15:55 +00:00
Mike Bayer 04e70c708f changing Pool to use weakref callback for auto-cleanup, instead of __del__.
Still leaving the RLock in Queue however since I see no guarantee that the weakref callback
isn't called at an arbitrary time.
2007-08-21 18:18:21 +00:00
Jason Kirtland 05e8f8a919 Inlined ClauseParameters.set_parameter (simple assignment) in construct_params
Big drop in function count for inserts (22%) with about a 3% wall clock improvement.
2007-08-21 16:58:49 +00:00
Mike Bayer 7f46c8b8d9 fixed "SmallInteger" 2007-08-21 16:56:22 +00:00
Mike Bayer 3b08545b24 - implemented __len__() accessor on RowProxy
- implemented jek's ClauseParameters optimization for named params
2007-08-21 15:50:17 +00:00
Jason Kirtland 66428d908f tweak that construct_params optimization, one of the adjustments wasn't needed 2007-08-21 08:19:21 +00:00
Jason Kirtland 47670faa30 A couple critical path optimizations
(some sql operations faster by nearly 10% wallclock, general orm around 3%)
2007-08-21 07:55:43 +00:00
Jason Kirtland 6228e72cb1 - omitted 'table' and 'column' from 'from sqlalchemy import *'
- also omitted all modules and classes that aren't expicitly public
- omitted 'Smallinteger' (small i), but it's still in schema
- omitted NullType-related items from types.__all__
- patched up a few tests to use sql.table and sql.column, other related.
2007-08-21 01:31:23 +00:00
Jason Kirtland 83eb243124 added stub/import tests for all dialects
post-refactor fix for access dialect
2007-08-21 00:47:48 +00:00
Jason Kirtland e9015b2d05 fix line ending svn prop 2007-08-21 00:43:47 +00:00
Mike Bayer 6e3f8e9f49 pool_threadlocal on by default 2007-08-20 23:34:58 +00:00
Mike Bayer 2d91940d0f removed unnecessary _branch calls 2007-08-20 22:06:31 +00:00
Mike Bayer 109d535961 - method call removal 2007-08-20 21:50:59 +00:00
Mike Bayer 531faf0e18 - Engine and TLEngine assume "threadlocal" behavior on Pool; both use connect()
for contextual connection, unique_connection() for non-contextual.
- Pool use_threadlocal defaults to True, can be set to false at create_engine()
level with pool_threadlocal=False
- made all logger statements in pool conditional based on a flag calcualted once.
- chagned WeakValueDictionary() used for "threadlocal" pool to be a regular dict
referencing weakref objects.  WVD had a lot of overhead, apparently.  *CAUTION* -
im pretty confident about this change, as the threadlocal dict gets explicitly managed
anyway, tests pass with PG etc., but keep a close eye on this one regardless.
2007-08-20 19:07:07 +00:00
Ants Aasma be16b15f2c an early out processing insert/update column parameters was a bit too early. 2007-08-19 23:01:44 +00:00
Jason Kirtland d1020a8d4e light docstring tweaks to the pool
more pedantic DBAPI -> DB-API changes in docstrings
2007-08-19 18:34:24 +00:00
Jason Kirtland 551512c561 CHANGES for r3372 2007-08-19 18:32:55 +00:00
Jason Kirtland b714ebf491 - Connection.begin() no longer accepts nested=True, a possible source of confusion as two forms of nesting are supported. SAVEPOINT-style nesting logic is now contained soley in begin_nested().
- Docstring love for the engine package.  More is needed.
2007-08-19 17:57:51 +00:00
Jason Kirtland 5a1db66588 ReST docstring fix 2007-08-19 17:48:46 +00:00
Paul Johnston 1b1da969eb mssql unit test fixes 2007-08-19 15:30:02 +00:00
Paul Johnston 53ff3d297c One more change of preparer() to identifier_preparer 2007-08-19 14:51:36 +00:00
Jason Kirtland 56fdb8b296 pyflakes mop-up 2007-08-19 14:48:04 +00:00
Paul Johnston c62c878810 Use identifier_preparer instead of preparer() 2007-08-19 14:26:53 +00:00
Paul Johnston ab540ec601 Fix broken imports 2007-08-19 14:13:29 +00:00
Mike Bayer 56f0fe1b46 docstring compile fixup 2007-08-18 22:07:33 +00:00
Mike Bayer 8f9b4bc427 adding an "already exists" catch for CREATE DOMAIN 2007-08-18 21:43:53 +00:00
Mike Bayer 7c6c1b99c2 1. Module layout. sql.py and related move into a package called "sql".
2. compiler names changed to be less verbose, unused classes removed.
3. Methods on Dialect which return compilers, schema generators, identifier preparers
have changed to direct class references, typically on the Dialect class itself
or optionally as attributes on an individual Dialect instance if conditional behavior is needed.
This takes away the need for Dialect subclasses to know how to instantiate these
objects, and also reduces method overhead by one call for each one.
4. as a result of 3., some internal signatures have changed for things like compiler() (now statement_compiler()), preparer(), etc., mostly in that the dialect needs to be passed explicitly as the first argument (since they are just class references now).  The compiler() method on Engine and Connection is now also named statement_compiler(), but as before does not take the dialect as an argument.

5. changed _process_row function on RowProxy to be a class reference, cuts out 50K method calls from insertspeed.py
2007-08-18 21:37:48 +00:00
Jason Kirtland 534cf5fdbd - fixed prefixes= argument to select()
- mysql can now generate DISTINCT or ALL for queries, select(..., distinct='ALL')
- documented 'prefixes' arg to select()
- rearranged doc order for select args to mirror that of a generated statement
- went nutty and fixed wrapping and line length on most docstrings in sql.py
2007-08-18 18:15:11 +00:00
Mike Bayer 820346549b - modified SQL operator functions to be module-level operators, allowing
SQL expressions to be pickleable [ticket:735]

- small adjustment to mapper class.__init__ to allow for Py2.6 object.__init__()
  behavior
2007-08-18 01:00:44 +00:00
Mike Bayer 74595d900c - Added a "legacy" adapter to types, such that user-defined TypeEngine
and TypeDecorator classes which define convert_bind_param()/convert_result_value()
  will continue to function.  Also supports calling the super() version of
  those methods.
2007-08-17 23:45:29 +00:00
Mike Bayer 721f291406 add a test for dupe tables in MetaData 2007-08-17 22:20:55 +00:00
Mike Bayer de1712a211 - added close() method to Transaction. closes out a transaction using rollback
if it's the outermost transaction, otherwise just ends without affecting
  the outer transaction.

- transactional and non-transactional Session integrates better with bound
  connection; a close() will ensure that connection transactional state is
  the same as that which existed on it before being bound to the Session.
2007-08-17 21:20:49 +00:00
Mike Bayer 99b1bee20e transactional session rolls back bound connection 2007-08-17 19:34:40 +00:00
Mike Bayer 27cf3a232d - threadlocal TLConnection, when closes for real, forces parent TLSession
to rollback/dispose of transaction
2007-08-17 19:13:51 +00:00
Jason Kirtland 3e3e6d0c19 update activemapper backrefs for r3340 2007-08-17 19:13:12 +00:00
Mike Bayer 6d0d482cdf dont commandeer warnings into logger 2007-08-17 18:19:56 +00:00
Mike Bayer 75f69f85a2 - turned twophase=True on in test
- TLEngine raises notimplemented for two-phase
2007-08-17 18:14:11 +00:00
Mike Bayer 56ec973cd4 reinstated two_phase test. currently it passes on PG with and without threadlocal. 2007-08-17 18:01:52 +00:00
Paul Johnston 93961ff3c7 merge changset [3347] into trunk 2007-08-17 18:00:30 +00:00
Mike Bayer 95748cfc02 - added extra argument con_proxy to ConnectionListener interface checkout/checkin methods
- changed testing connection closer to work on _ConnectionFairy instances, resulting in
pool checkins, not actual closes
- disabled session two phase test for now, needs work
- added some two-phase support to TLEngine, not tested
- TLTransaction is now a wrapper
2007-08-17 17:59:08 +00:00
Paul Johnston 3469724a42 Merge [3345] into trunk. Unit test still TODO 2007-08-17 16:44:33 +00:00
Paul Johnston c23f82d989 Disable MSSQL unicode statements on UCS-4 platforms, ticket #731 2007-08-17 15:10:46 +00:00
Jason Kirtland 9793d1b567 - session transaction closing tweak for threadlocal
- connection-rollback decorator only fires on unhandled testing exceptions
2007-08-17 15:01:20 +00:00
Jason Kirtland 3ca07cd370 added a testing decorator that mops up wayward connections 2007-08-17 01:24:46 +00:00
Mike Bayer 8fc979f455 removed ridiculous LOrderedProp object 2007-08-16 22:57:57 +00:00
Mike Bayer 08bf368bfb revert the _DefaultExtension thing, it added function calls 2007-08-16 22:20:45 +00:00
Mike Bayer bc04505b8c - moved test/orm/fixtures.py to testlib
- flattened mapper calls in _instance() to operate directly
through a default MapperExtension
- more tests for ScopedSession, fixed [ticket:746]
- threadlocal engine propagates **kwargs through begin()
2007-08-16 22:15:15 +00:00
Jason Kirtland 1f41755ecf it's a non-stop formatting fiesta 2007-08-16 21:22:44 +00:00
Jason Kirtland c86c9be78b formatting fiesta 2007-08-16 20:53:41 +00:00
Jason Kirtland 0339042815 Added session.prune(), releases unused objects in strong-ref identity maps. 2007-08-16 19:58:24 +00:00
Mike Bayer 13af7230dc - fix to bind param processing such that "False" values (like blank strings)
still get processed/encoded
2007-08-16 18:25:11 +00:00
Mike Bayer 6e6f4c0c94 new changelog with betas 2007-08-16 15:58:54 +00:00
Mike Bayer 7c13541139 bump 2007-08-16 15:22:17 +00:00
Mike Bayer f6306cc604 needed orm import 2007-08-16 15:20:20 +00:00
Mike Bayer a8946d3682 bump 2007-08-16 15:12:57 +00:00
Mike Bayer 666a62141f added remove() coverage.... 2007-08-16 15:00:51 +00:00
Mike Bayer ac4c3f99db add length to str for pk usage 2007-08-16 14:50:24 +00:00
Mike Bayer dd99a207d6 added support for string date passthru in sqlite 2007-08-16 14:48:48 +00:00
Mike Bayer 354f899be6 added 'inherit_foreign_keys' arg to mapper() 2007-08-16 14:29:34 +00:00
Jason Kirtland 46535b25a6 use threading.local if available
speed up ThreadLocal for python 2.3 [ticket:743]
clean in topo (in patch from [ticket:743])
2007-08-15 23:11:47 +00:00
Jason Kirtland fddc687348 fix hasattr typo [ticket:744] 2007-08-15 22:10:27 +00:00
Jason Kirtland 9350ec4827 stopgap, need a general strategy for raising readable exceptions for unicode content 2007-08-15 22:07:35 +00:00
Mike Bayer cf7a01a90c -removed print statements
- removeld errant classmethod on remove()
2007-08-15 17:31:17 +00:00
Mike Bayer e02a48ed24 mass has_key->__contains__ migration, [ticket:738] 2007-08-15 17:28:52 +00:00
Mike Bayer 2708a3489e - moved unicode schema ORM tests to unitofwork.py tests. mostly
is to test mappers so limited DB support (really hard to get these unicode schemas
to work...)
- fixed [ticket:739]
2007-08-15 16:48:57 +00:00
Mike Bayer 01d0d7b85d - cleanup, converted unitofwork.py to standard fixtures 2007-08-15 03:49:32 +00:00
Mike Bayer 651e9ff673 removed init_attr() call, which has shown to not increase performance 2007-08-15 01:25:13 +00:00
Mike Bayer 087f235c33 - merged "fasttypes" branch. this branch changes the signature
of convert_bind_param() and convert_result_value() to callable-returning
bind_processor() and result_processor() methods.  if no callable is
returned, no pre/post processing function is called.
- hooks added throughout base/sql/defaults to optimize the calling
of bind param/result processors so that method call overhead is minimized.
special cases added for executemany() scenarios such that unneeded "last row id"
logic doesn't kick in, parameters aren't excessively traversed.
- new performance tests show a combined mass-insert/mass-select test as having 68%
fewer function calls than the same test run against 0.3.
- general performance improvement of result set iteration is around 10-20%.
2007-08-14 21:53:32 +00:00
Mike Bayer e58063aa91 cleanup 2007-08-14 20:26:43 +00:00
Paul Johnston 9ab0947f16 revert previous change; had misunderstood context 2007-08-14 18:25:09 +00:00
Paul Johnston d0e78b044e fudge to make SQL asserts work reliably with MSSQL 2007-08-14 18:16:27 +00:00
Paul Johnston d93c7c3b8f Make versioningtest.test_basic only assert when the dbapi support sane_rowcount 2007-08-14 18:03:25 +00:00
Mike Bayer 38a55648a1 added sqlite/sa "unprofiled" raw time tests 2007-08-14 17:51:15 +00:00
Mike Bayer ce9828ff07 added full fetching of result columns, cut overall size to 50000 2007-08-14 17:43:03 +00:00
Mike Bayer 55e80ace66 a mass insert/ select benchmarking test, from
http://pyinsci.blogspot.com/2007/07/fastest-python-database-interface.html
2007-08-14 17:34:17 +00:00
Mike Bayer 5be79a6e32 adjustment to table_names test such that the DB can have extra tables around 2007-08-14 15:10:42 +00:00
Mike Bayer 054e76df44 removed assertion for "no tables in db" 2007-08-14 14:32:50 +00:00
Mike Bayer a7a837489d commented out unicode foriegn keys for now, not working on mysql or postgres 2007-08-14 04:17:44 +00:00
Mike Bayer f0a383f8bf attempt to get SessionTest to close transactions better 2007-08-14 03:47:15 +00:00
Mike Bayer 6c6cd47c02 - fixed endless loop
- fixed perf imports in masseagerload
2007-08-14 03:34:38 +00:00
Mike Bayer b9ed823528 - base_mapper() becomes a plain attribute
- session.execute() and scalar() can search for a Table with which to bind
from using the given ClauseElement
- session automatically extrapolates tables from mappers with binds,
also uses base_mapper so that inheritance hierarchies bind automatically
- moved ClauseVisitor traversal back to inlined non-recursive
2007-08-14 03:19:46 +00:00
Mike Bayer 0285ec6beb added engine_from_config() function for helping to create_engine()
from an .ini style config
2007-08-14 00:40:23 +00:00
Mike Bayer e623c179da docstirng... 2007-08-13 20:52:33 +00:00
Mike Bayer eca4685897 added scoped session test independent of Session.mapper test 2007-08-13 20:48:46 +00:00
Mike Bayer c1a4befc68 - a rudimental SessionExtension class has been added, allowing user-defined
functionality to take place at flush(), commit(), and rollback() boundaries.
2007-08-13 20:14:04 +00:00
Mike Bayer 6614209895 - generalized a SQLCompileTest out of select.py, installed
into dialect/mssql.py, dialect/oracle.py, sql/generative.py
- fixed oracle issues [ticket:732], [ticket:733], [ticket:734]
2007-08-13 20:00:38 +00:00
Jason Kirtland 799f458667 adjusted mysql autoload from a named schema, esp. for windows 2007-08-13 18:57:05 +00:00
Jason Kirtland 05eb3073df Removed unused imports, other import adjustments per pyflakes 2007-08-13 07:09:56 +00:00
Jason Kirtland a19fa666ba Centralized some try: import foo except: import other as foo imports in util 2007-08-13 06:35:32 +00:00
Jason Kirtland 58f8db9955 Added more unicode foreign key tests for [ticket:729] 2007-08-12 23:43:49 +00:00
Jason Kirtland 872a882d0d auto-commit after LOAD DATA INFILE for mysql
caught a couple more uncompiled regexps
2007-08-12 22:05:30 +00:00
Mike Bayer 6e477b750e - got is_subquery() working in the case of compound selects, test for ms-sql 2007-08-12 22:01:30 +00:00
Mike Bayer 6b6443f82c removed ms-sql unsupporteds 2007-08-12 21:42:07 +00:00
Mike Bayer 62410adeb9 - fixed compiler bug in mssql
- marked as unsupported for mssql all two-phase and nested transcation tests
- marked as unsupported for mssql various transactional/session tests which require two connections looking at uncommitted/external data at the same time (ms-sql cant handle it)
- put better explicit closeout step in unitofwork.py tests to appease ms-sqls hard locking
2007-08-12 21:36:33 +00:00
Paul Johnston fb5cd747e8 Close SQLite databases before deleting file, so the lock is released, important on Windows 2007-08-12 21:24:27 +00:00
Paul Johnston 0013b84f8b MSSQL: disable new 0.4 tests that cause hangs 2007-08-12 20:52:07 +00:00
Mike Bayer 970ecb31c5 edits 2007-08-12 20:43:21 +00:00
Mike Bayer 2bc1608641 - scoped_session docs
- added remove() method to scoped_session
2007-08-12 20:34:38 +00:00
Paul Johnston 98166b253e Fix missing import of 'operator' 2007-08-12 20:18:04 +00:00
Mike Bayer 8b004dc7af added "should_commit()" hook to ExecutionContext. dialects can override with specific tests 2007-08-12 19:37:47 +00:00
Jason Kirtland d06fba535c Bump. 2007-08-12 19:30:49 +00:00
Mike Bayer 0d2189a993 typos 2007-08-12 16:37:07 +00:00
493 changed files with 124213 additions and 59781 deletions
+3009 -2022
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 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.0beta1
-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
-437
View File
@@ -1,437 +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:
* **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 `&lt;column1&gt;(+)=&lt;column2&gt;` 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)}
-160
View File
@@ -1,160 +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) | | |
| | +------------------------------------+ | |
| +-----------------------+ +--------------+ |
| 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 genreative {@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)
-1403
View File
File diff suppressed because it is too large Load Diff
-512
View File
@@ -1,512 +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 Defaults and OnUpdates {@name=defaults}
SQLAlchemy includes flexible constructs in which to create default values for columns upon the insertion of rows, as well as upon update. These defaults can take several forms: a constant, a Python callable to be pre-executed before the SQL is executed, a SQL expression or function to be pre-executed before the SQL is executed, a pre-executed Sequence (for databases that support sequences), or a "passive" default, which is a default function triggered by the database itself upon insert, the value of which can then be post-fetched by the engine, provided the row provides a primary key in which to call upon.
#### Pre-Executed Insert Defaults {@name=oninsert}
A basic default is most easily specified by the "default" keyword argument to Column. This defines a value, function, or SQL expression that will be pre-executed to produce the new value, before the row is inserted:
{python}
# a function to create primary key ids
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")
)
The "default" keyword can also take 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))
)
The "default" keyword argument is shorthand for using a ColumnDefault object in a column definition. This syntax is optional, but is required for other types of defaults, futher described below:
{python}
Column('mycolumn', String(30), ColumnDefault(func.get_data()))
#### Pre-Executed OnUpdate Defaults {@name=onupdate}
Similar to an on-insert default is an on-update default, which is most easily specified by the "onupdate" keyword to Column, which also can be a constant, plain Python function or SQL expression:
{python}
t = Table("mytable", meta,
Column('id', Integer, primary_key=True),
# define 'last_updated' to be populated with current_timestamp (the ANSI-SQL version of now())
Column('last_updated', DateTime, onupdate=func.current_timestamp()),
)
To use an explicit ColumnDefault object to specify an on-update, use the "for_update" keyword argument:
{python}
Column('mycolumn', String(30), ColumnDefault(func.get_data(), for_update=True))
#### Inline Default Execution: PassiveDefault {@name=passive}
A PassiveDefault indicates an column default that is executed upon INSERT by the database. This construct is used to specify a SQL function that will be specified as "DEFAULT" when creating tables.
{python}
t = Table('test', meta,
Column('mycolumn', DateTime, PassiveDefault("sysdate"))
)
A create call for the above table will produce:
{code}
CREATE TABLE test (
mycolumn datetime default sysdate
)
PassiveDefault also sends a message to the `Engine` that data is available after an insert. The object-relational mapper system uses this information to post-fetch rows after the insert, so that instances can be refreshed with the new data. Below is a simplified version:
{python}
# table with passive defaults
mytable = Table('mytable', engine,
Column('my_id', Integer, primary_key=True),
# an on-insert database-side default
Column('data1', Integer, PassiveDefault("d1_func()")),
)
# insert a row
r = mytable.insert().execute(name='fred')
# check the result: were there defaults fired off on that row ?
if r.lastrow_has_defaults():
# postfetch the row based on primary key.
# this only works for a table with primary key columns defined
primary_key = r.last_inserted_ids()
row = table.select(table.c.id == primary_key[0])
When Tables are reflected from the database using `autoload=True`, any DEFAULT values set on the columns will be reflected in the Table object as PassiveDefault instances.
##### The Catch: Postgres Primary Key Defaults always Pre-Execute {@name=postgres}
Current Postgres support does not rely upon OID's to determine the identity of a row. This is because the usage of OIDs has been deprecated with Postgres and they are disabled by default for table creates as of PG version 8. Pyscopg2's "cursor.lastrowid" function only returns OIDs. Therefore, when inserting a new row which has passive defaults set on the primary key columns, the default function is <b>still pre-executed</b> since SQLAlchemy would otherwise have no way of retrieving the row just inserted.
#### 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 is used with Postgres or Oracle to indicate the name of a database sequence that will be used to create default values for a column. When a table with a Sequence on a column is created in the database by SQLAlchemy, the database sequence object is also created. Similarly, the database sequence is dropped when the table is dropped. Sequences are typically used with primary key columns. When using Postgres, if an integer primary key column defines no explicit Sequence or other default method, SQLAlchemy will create the column with the SERIAL keyword, and will pre-execute a sequence named "tablename_columnname_seq" in order to retrieve new primary key values, if they were not otherwise explicitly stated. Oracle, which has no "auto-increment" keyword, requires that a Sequence be specified for a table if automatic primary key generation is desired.
A Sequence object can be defined on a Table that is then also used with a non-sequence-supporting database. In that case, the Sequence object is simply ignored. Note that a Sequence object is **entirely optional for all databases except Oracle**, as other databases offer options for auto-creating primary key values, such as AUTOINCREMENT, SERIAL, etc. SQLAlchemy will use these default methods for creating primary key values if no Sequence is present on the table metadata.
A sequence can also be specified with `optional=True` which indicates the Sequence should only be used on a database that requires an explicit sequence, and not those that supply some other method of providing integer values. At the moment, it essentially means "use this sequence only with Oracle and not Postgres".
### 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&gt;5')),
Column('col2', Integer),
Column('col3', Integer),
# table level CHECK constraint. 'name' is optional.
CheckConstraint('col2 &gt; 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)
-931
View File
@@ -1,931 +0,0 @@
[alpha_api]: javascript:alphaApi()
[alpha_implementation]: javascript:alphaImplementation()
Object Relational Tutorial {@name=datamapping}
============
In this tutorial we will cover a basic SQLAlchemy object-relational mapping scenario, where we store and retrieve Python objects from a database representation. The database schema will begin with one table, and will later develop into several. The tutorial is in doctest format, meaning each `>>>` line represents something you can type at a Python command prompt, and the following text represents the expected return value. The tutorial has no prerequisites.
## Version Check
A quick check to verify that we are on at least **version 0.4** of SQLAlchemy:
{python}
>>> import sqlalchemy
>>> sqlalchemy.__version__ # doctest:+SKIP
0.4.0
## Connecting
For this tutorial we will use an in-memory-only SQLite database. This is an easy way to test things without needing to have an actual database defined anywhere. To connect we use `create_engine()`:
{python}
>>> from sqlalchemy import create_engine
>>> engine = create_engine('sqlite:///:memory:', echo=True)
The `echo` flag is a shortcut to setting up SQLAlchemy logging, which is accomplished via Python's standard `logging` module. With it enabled, we'll see all the generated SQL produced. If you are working through this tutorial and want less output generated, set it to `False`. This tutorial will format the SQL behind a popup window so it doesn't get in our way; just click the "SQL" links to see whats being generated.
## Define and Create a Table {@name=tables}
Next we want to tell SQLAlchemy about our tables. We will start with just a single table called `users`, which will store records for the end-users using our application (lets assume its a website). We define our tables all within a catalog called `MetaData`, using the `Table` construct, which resembles regular SQL CREATE TABLE syntax:
{python}
>>> from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey
>>> metadata = MetaData()
>>> users_table = Table('users', metadata,
... Column('id', Integer, primary_key=True),
... Column('name', String(40)),
... Column('fullname', String(100)),
... Column('password', String(15))
... )
All about how to define `Table` objects, as well as how to create them from an existing database automatically, is described in [metadata](rel:metadata).
Next, to tell the `MetaData` we'd actually like to create our `users_table` for real inside the SQLite database, we use `create_all()`, passing it the `engine` instance which points to our database. This will check for the presence of a table first before creating, so its safe to call multiple times:
{python}
{sql}>>> metadata.create_all(engine) # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE
PRAGMA table_info("users")
{}
CREATE TABLE users (
id INTEGER NOT NULL,
name VARCHAR(40),
fullname VARCHAR(100),
password VARCHAR(15),
PRIMARY KEY (id)
)
None
COMMIT
So now our database is created, our initial schema is present, and our SQLAlchemy application knows all about the tables and columns in the database; this information is to be re-used by the Object Relational Mapper, as we'll see now.
## Define a Python Class to be Mapped {@name=mapping}
So lets create a rudimental `User` object to be mapped in the database. This object will for starters have three attributes, `name`, `fullname` and `password`. It only need subclass Python's built-in `object` class (i.e. its a new style class). We will give it a constructor so that it may conveniently be instantiated with its attributes at once, as well as a `__repr__` method so that we can get a nice string representation of it:
{python}
>>> class User(object):
... def __init__(self, name, fullname, password):
... self.name = name
... self.fullname = fullname
... self.password = password
...
... def __repr__(self):
... return "<User(%r,%r, %r)>" % (self.name, self.fullname, self.password)
## Setting up the Mapping
With our `users_table` and `User` class, we now want to map the two together. That's where the SQLAlchemy ORM package comes in. We'll use the `mapper` function to create a **mapping** between `users_table` and `User`:
{python}
>>> from sqlalchemy.orm import mapper
>>> mapper(User, users_table) # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE
<sqlalchemy.orm.mapper.Mapper object at 0x...>
The `mapper()` function creates a new `Mapper` object and stores it away for future reference. It also **instruments** the attributes on our `User` class, corresponding to the `users_table` table. The `id`, `name`, `fullname`, and `password` columns in our `users_table` are now instrumented upon our `User` class, meaning it will keep track of all changes to these attributes, and can save and load their values to/from the database. Lets create our first user, 'Ed Jones', and ensure that the object has all three of these attributes:
{python}
>>> ed_user = User('ed', 'Ed Jones', 'edspassword')
>>> ed_user.name
'ed'
>>> ed_user.password
'edspassword'
>>> str(ed_user.id)
'None'
What was that last `id` attribute? That was placed there by the `Mapper`, to track the value of the `id` column in the `users_table`. Since our `User` doesn't exist in the database, it's id is `None`. When we save the object, it will get populated automatically with its new id.
## Creating a Session
We're now ready to start talking to the database. The ORM's "handle" to the database is the `Session`. When we first set up the application, at the same level as our `create_engine()` statement, we define a second object called `Session` (or whatever you want to call it, `create_session`, etc.) which is configured by the `sessionmaker()` function. This function is configurational and need only be called once.
{python}
>>> from sqlalchemy.orm import sessionmaker
>>> Session = sessionmaker(bind=engine, autoflush=True, transactional=True)
In the case where your application does not yet have an `Engine` when you define your module-level objects, just set it up like this:
{python}
>>> Session = sessionmaker(autoflush=True, transactional=True)
Later, when you create your engine with `create_engine()`, connect it to the `Session` using `configure()`:
{python}
>>> Session.configure(bind=engine) # once engine is available
This `Session` class will create new `Session` objects which are bound to our database and have the transactional characteristics we've configured. Whenever you need to have a conversation with the database, you instantiate a `Session`:
{python}
>>> session = Session()
The above `Session` is associated with our SQLite `engine`, but it hasn't opened any connections yet. When it's first used, it retrieves a connection from a pool of connections maintained by the `engine`, and holds onto it until we commit all changes and/or close the session object. Because we configured `transactional=True`, theres also a transaction in progress (one notable exception to this is MySQL, when you use its default table style of MyISAM). There's options available to modify this behavior but we'll go with this straightforward version to start.
## Saving Objects
So saving our `User` is as easy as issuing `save()`:
{python}
>>> session.save(ed_user)
But you'll notice nothing has happened yet. Well, lets pretend something did, and try to query for our user. This is done using the `query()` method on `Session`. We create a new query representing the set of all `User` objects first. Then we narrow the results by "filtering" down to the user we want; that is, the user whose `name` attribute is `"ed"`. Finally we call `first()` which tells `Query`, "we'd like the first result in this list".
{python}
{sql}>>> session.query(User).filter_by(name='ed').first() # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE
BEGIN
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
['ed', 'Ed Jones', 'edspassword']
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name = ? ORDER BY users.oid
LIMIT 1 OFFSET 0
['ed']
{stop}<User('ed','Ed Jones', 'edspassword')>
And we get back our new user. If you view the generated SQL, you'll see that the `Session` issued an `INSERT` statement before querying. The `Session` stores whatever you put into it in memory, and at certain points it issues a **flush**, which issues SQL to the database to store all pending new objects and changes to existing objects. You can manually invoke the flush operation using `flush()`; however when the `Session` is configured to `autoflush`, its usually not needed.
OK, let's do some more operations. We'll create and save three more users:
{python}
>>> session.save(User('wendy', 'Wendy Williams', 'foobar'))
>>> session.save(User('mary', 'Mary Contrary', 'xxg527'))
>>> session.save(User('fred', 'Fred Flinstone', 'blah'))
Also, Ed has already decided his password isn't too secure, so lets change it:
{python}
>>> ed_user.password = 'f8s7ccs'
Then we'll permanently store everything thats been changed and added to the database. We do this via `commit()`:
{python}
{sql}>>> session.commit()
UPDATE users SET password=? WHERE users.id = ?
['f8s7ccs', 1]
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
['wendy', 'Wendy Williams', 'foobar']
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
['mary', 'Mary Contrary', 'xxg527']
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
['fred', 'Fred Flinstone', 'blah']
COMMIT
`commit()` flushes whatever remaining changes remain to the database, and commits the transaction. The connection resources referenced by the session are now returned to the connection pool. Subsequent operations with this session will occur in a **new** transaction, which will again re-acquire connection resources when first needed.
If we look at Ed's `id` attribute, which earlier was `None`, it now has a value:
{python}
>>> ed_user.id
1
After each `INSERT` operation, the `Session` assigns all newly generated ids and column defaults to the mapped object instance. For column defaults which are database-generated and are not part of the table's primary key, they'll be loaded when you first reference the attribute on the instance.
One crucial thing to note about the `Session` is that each object instance is cached within the Session, based on its primary key identitifer. The reason for this cache is not as much for performance as it is for maintaining an **identity map** of instances. This map guarantees that whenever you work with a particular `User` object in a session, **you always get the same instance back**. As below, reloading Ed gives us the same instance back:
{python}
{sql}>>> ed_user is session.query(User).filter_by(name='ed').one() # doctest: +NORMALIZE_WHITESPACE
BEGIN
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name = ? ORDER BY users.oid
LIMIT 2 OFFSET 0
['ed']
{stop}True
The `get()` method, which queries based on primary key, will not issue any SQL to the database if the given key is already present:
{python}
>>> ed_user is session.query(User).get(ed_user.id)
True
## Querying
A whirlwind tour through querying.
A `Query` is created from the `Session`, relative to a particular class we wish to load.
{python}
>>> query = session.query(User)
Once we have a query, we can start loading objects. The Query object, when first created, represents all the instances of its main class. You can iterate through it directly:
{python}
{sql}>>> for user in session.query(User):
... print user.name
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users ORDER BY users.oid
[]
{stop}ed
wendy
mary
fred
...and the SQL will be issued at the point where the query is evaluated as a list. If you apply array slices before iterating, LIMIT and OFFSET are applied to the query:
{python}
{sql}>>> for u in session.query(User)[1:3]: #doctest: +NORMALIZE_WHITESPACE
... print u
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users ORDER BY users.oid
LIMIT 2 OFFSET 1
[]
{stop}<User('wendy','Wendy Williams', 'foobar')>
<User('mary','Mary Contrary', 'xxg527')>
Narrowing the results down is accomplished either with `filter_by()`, which uses keyword arguments:
{python}
{sql}>>> for user in session.query(User).filter_by(name='ed', fullname='Ed Jones'):
... print user
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.fullname = ? AND users.name = ? ORDER BY users.oid
['Ed Jones', 'ed']
{stop}<User('ed','Ed Jones', 'f8s7ccs')>
...or `filter()`, which uses SQL expression language constructs. These allow you to use regular Python operators with the class-level attributes on your mapped class:
{python}
{sql}>>> for user in session.query(User).filter(User.name=='ed'):
... print user
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name = ? ORDER BY users.oid
['ed']
{stop}<User('ed','Ed Jones', 'f8s7ccs')>
You can also use the `Column` constructs attached to the `users_table` object to construct SQL expressions:
{python}
{sql}>>> for user in session.query(User).filter(users_table.c.name=='ed'):
... print user
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name = ? ORDER BY users.oid
['ed']
{stop}<User('ed','Ed Jones', 'f8s7ccs')>
Most common SQL operators are available, such as `LIKE`:
{python}
{sql}>>> session.query(User).filter(User.name.like('%ed'))[1] # doctest: +NORMALIZE_WHITESPACE
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name LIKE ? ORDER BY users.oid
LIMIT 1 OFFSET 1
['%ed']
{stop}<User('fred','Fred Flinstone', 'blah')>
Note above our array index of `1` placed the appropriate LIMIT/OFFSET and returned a scalar result immediately.
The `all()`, `one()`, and `first()` methods immediately issue SQL without using an iterative context or array index. `all()` returns a list:
{python}
>>> query = session.query(User).filter(User.name.like('%ed'))
{sql}>>> query.all()
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name LIKE ? ORDER BY users.oid
['%ed']
{stop}[<User('ed','Ed Jones', 'f8s7ccs')>, <User('fred','Fred Flinstone', 'blah')>]
`first()` applies a limit of one and returns the first result as a scalar:
{python}
{sql}>>> query.first()
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name LIKE ? ORDER BY users.oid
LIMIT 1 OFFSET 0
['%ed']
{stop}<User('ed','Ed Jones', 'f8s7ccs')>
and `one()`, applies a limit of *two*, and if not exactly one row returned (no more, no less), raises an error:
{python}
{sql}>>> try:
... user = query.one()
... except Exception, e:
... print e
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name LIKE ? ORDER BY users.oid
LIMIT 2 OFFSET 0
['%ed']
{stop}Multiple rows returned for one()
All `Query` methods that don't return a result instead return a new `Query` object, with modifications applied. Therefore you can call many query methods successively to build up the criterion you want:
{python}
{sql}>>> session.query(User).filter(User.id<2).filter_by(name='ed').\
... filter(User.fullname=='Ed Jones').all()
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.id < ? AND users.name = ? AND users.fullname = ? ORDER BY users.oid
[2, 'ed', 'Ed Jones']
{stop}[<User('ed','Ed Jones', 'f8s7ccs')>]
If you need to use other conjunctions besides `AND`, all SQL conjunctions are available explicitly within expressions, such as `and_()` and `or_()`, when using `filter()`:
{python}
>>> from sqlalchemy import and_, or_
{sql}>>> session.query(User).filter(
... and_(User.id<224, or_(User.name=='ed', User.name=='wendy'))
... ).all()
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.id < ? AND (users.name = ? OR users.name = ?) ORDER BY users.oid
[224, 'ed', 'wendy']
{stop}[<User('ed','Ed Jones', 'f8s7ccs')>, <User('wendy','Wendy Williams', 'foobar')>]
You also have full ability to use literal strings to construct SQL. For a single criterion, use a string with `filter()`:
{python}
{sql}>>> for user in session.query(User).filter("id<224").all():
... print user.name
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE id<224 ORDER BY users.oid
[]
{stop}ed
wendy
mary
fred
Bind parameters can be specified with string-based SQL, using a colon. To specify the values, use the `params()` method:
{python}
{sql}>>> session.query(User).filter("id<:value and name=:name").\
... params(value=224, name='fred').one() # doctest: +NORMALIZE_WHITESPACE
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE id<? and name=? ORDER BY users.oid
LIMIT 2 OFFSET 0
[224, 'fred']
{stop}<User('fred','Fred Flinstone', 'blah')>
Note that when we use constructed SQL expressions, bind parameters are generated for us automatically; we don't need to worry about them.
To use an entirely string-based statement, using `from_statement()`; just ensure that the columns clause of the statement contains the column names normally used by the mapper (below illustrated using an asterisk):
{python}
{sql}>>> session.query(User).from_statement("SELECT * FROM users where name=:name").params(name='ed').all()
SELECT * FROM users where name=?
['ed']
{stop}[<User('ed','Ed Jones', 'f8s7ccs')>]
`from_statement()` can also accomodate full `select()` constructs. These are described in the [sql](rel:sql):
{python}
>>> from sqlalchemy import select, func
{sql}>>> session.query(User).from_statement(
... select(
... [users_table],
... select([func.max(users_table.c.name)]).label('maxuser')==users_table.c.name)
... ).all()
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE (SELECT max(users.name)
FROM users) = users.name
[]
{stop}[<User('wendy','Wendy Williams', 'foobar')>]
There's also a way to combine scalar results with objects, using `add_column()`. This is often used for functions and aggregates. When `add_column()` (or its cousin `add_entity()`, described later) is used, tuples are returned:
{python}
{sql}>>> for r in session.query(User).\
... add_column(select([func.max(users_table.c.name)]).label('maxuser')):
... print r
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password, (SELECT max(users.name)
FROM users) AS maxuser
FROM users ORDER BY users.oid
[]
{stop}(<User('ed','Ed Jones', 'f8s7ccs')>, u'wendy')
(<User('wendy','Wendy Williams', 'foobar')>, u'wendy')
(<User('mary','Mary Contrary', 'xxg527')>, u'wendy')
(<User('fred','Fred Flinstone', 'blah')>, u'wendy')
## Building a One-to-Many Relation {@name=onetomany}
We've spent a lot of time dealing with just one class, and one table. Let's now look at how SQLAlchemy deals with two tables, which have a relationship to each other. Let's say that the users in our system also can store any number of email addresses associated with their username. This implies a basic one to many association from the `users_table` to a new table which stores email addresess, which we will call `addresses`. We will also create a relationship between this new table to the users table, using a `ForeignKey`:
{python}
>>> from sqlalchemy import ForeignKey
>>> addresses_table = Table('addresses', metadata,
... Column('id', Integer, primary_key=True),
... Column('email_address', String(100), nullable=False),
... Column('user_id', Integer, ForeignKey('users.id')))
Another call to `create_all()` will skip over our `users` table and build just the new `addresses` table:
{python}
{sql}>>> metadata.create_all(engine) # doctest: +NORMALIZE_WHITESPACE
PRAGMA table_info("users")
{}
PRAGMA table_info("addresses")
{}
CREATE TABLE addresses (
id INTEGER NOT NULL,
email_address VARCHAR(100) NOT NULL,
user_id INTEGER,
PRIMARY KEY (id),
FOREIGN KEY(user_id) REFERENCES users (id)
)
None
COMMIT
For our ORM setup, we're going to start all over again. We will first close out our `Session` and clear all `Mapper` objects:
{python}
>>> from sqlalchemy.orm import clear_mappers
>>> session.close()
>>> clear_mappers()
Our `User` class, still around, reverts to being just a plain old class. Lets create an `Address` class to represent a user's email address:
{python}
>>> class Address(object):
... def __init__(self, email_address):
... self.email_address = email_address
...
... def __repr__(self):
... return "<Address(%r)>" % self.email_address
Now comes the fun part. We define a mapper for each class, and associate them using a function called `relation()`. We can define each mapper in any order we want:
{python}
>>> from sqlalchemy.orm import relation
>>> mapper(User, users_table, properties={ # doctest: +ELLIPSIS
... 'addresses':relation(Address, backref='user')
... })
<sqlalchemy.orm.mapper.Mapper object at 0x...>
>>> mapper(Address, addresses_table) # doctest: +ELLIPSIS
<sqlalchemy.orm.mapper.Mapper object at 0x...>
Above, the new thing we see is that `User` has defined a relation named `addresses`, which will reference a list of `Address` objects. How does it know it's a list ? SQLAlchemy figures it out for you, based on the foreign key relationship between `users_table` and `addresses_table`.
## Working with Related Objects and Backreferences {@name=relation_backref}
Now when we create a `User`, it automatically has this collection present:
{python}
>>> jack = User('jack', 'Jack Bean', 'gjffdd')
>>> jack.addresses
[]
We are free to add `Address` objects, and the `session` will take care of everything for us.
{python}
>>> jack.addresses.append(Address(email_address='jack@google.com'))
>>> jack.addresses.append(Address(email_address='j25@yahoo.com'))
Before we save into the `Session`, lets examine one other thing that's happened here. The `addresses` collection is present on our `User` because we added a `relation()` with that name. But also within the `relation()` function is the keyword `backref`. This keyword indicates that we wish to make a **bi-directional relationship**. What this basically means is that not only did we generate a one-to-many relationship called `addresses` on the `User` class, we also generated a **many-to-one** relationship on the `Address` class. This relationship is self-updating, without any data being flushed to the database, as we can see on one of Jack's addresses:
{python}
>>> jack.addresses[1]
<Address('j25@yahoo.com')>
>>> jack.addresses[1].user
<User('jack','Jack Bean', 'gjffdd')>
Let's save into the session, then close out the session and create a new one...so that we can see how `Jack` and his email addresses come back to us:
{python}
>>> session.save(jack)
{sql}>>> session.commit()
BEGIN
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
['jack', 'Jack Bean', 'gjffdd']
INSERT INTO addresses (email_address, user_id) VALUES (?, ?)
['jack@google.com', 5]
INSERT INTO addresses (email_address, user_id) VALUES (?, ?)
['j25@yahoo.com', 5]
COMMIT
>>> session = Session()
Querying for Jack, we get just Jack back. No SQL is yet issued for for Jack's addresses:
{python}
{sql}>>> jack = session.query(User).filter_by(name='jack').one()
BEGIN
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name = ? ORDER BY users.oid
LIMIT 2 OFFSET 0
['jack']
>>> jack
<User(u'jack',u'Jack Bean', u'gjffdd')>
Let's look at the `addresses` collection. Watch the SQL:
{python}
{sql}>>> jack.addresses
SELECT addresses.id AS addresses_id, addresses.email_address AS addresses_email_address, addresses.user_id AS addresses_user_id
FROM addresses
WHERE ? = addresses.user_id ORDER BY addresses.oid
[5]
{stop}[<Address(u'jack@google.com')>, <Address(u'j25@yahoo.com')>]
When we accessed the `addresses` collection, SQL was suddenly issued. This is an example of a **lazy loading relation**.
If you want to reduce the number of queries (dramatically, in many cases), we can apply an **eager load** to the query operation. We clear out the session to ensure that a full reload occurs:
{python}
>>> session.clear()
Then apply an **option** to the query, indicating that we'd like `addresses` to load "eagerly". SQLAlchemy then constructs a join between the `users` and `addresses` tables:
{python}
>>> from sqlalchemy.orm import eagerload
{sql}>>> jack = session.query(User).options(eagerload('addresses')).filter_by(name='jack').one() #doctest: +NORMALIZE_WHITESPACE
SELECT addresses_1.id AS addresses_1_id, addresses_1.email_address AS addresses_1_email_address,
addresses_1.user_id AS addresses_1_user_id, users.id AS users_id, users.name AS users_name,
users.fullname AS users_fullname, users.password AS users_password
FROM (SELECT users.id AS users_id, users.oid AS users_oid
FROM users
WHERE users.name = ? ORDER BY users.oid LIMIT 2 OFFSET 0) AS tbl_row_count,
users LEFT OUTER JOIN addresses AS addresses_1 ON users.id = addresses_1.user_id
WHERE users.id = tbl_row_count.users_id ORDER BY tbl_row_count.oid, addresses_1.oid
['jack']
>>> jack
<User(u'jack',u'Jack Bean', u'gjffdd')>
>>> jack.addresses
[<Address(u'jack@google.com')>, <Address(u'j25@yahoo.com')>]
If you think that query is elaborate, it is ! But SQLAlchemy is just getting started. Note that when using eager loading, *nothing* changes as far as the ultimate results returned. The "loading strategy", as its called, is designed to be completely transparent in all cases, and is for optimization purposes only. Any query criterion you use to load objects, including ordering, limiting, other joins, etc., should return identical results regardless of the combination of lazily- and eagerly- loaded relationships present.
## Querying with Joins {@name=joins}
Which brings us to the next big topic. What if we want to create joins that *do* change the results ? For that, another `Query` tornado is coming....
One way to join two tables together is just to compose a SQL expression. Below we make one up using the `id` and `user_id` attributes on our mapped classes:
{python}
{sql}>>> session.query(User).filter(User.id==Address.user_id).\
... filter(Address.email_address=='jack@google.com').all()
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users, addresses
WHERE users.id = addresses.user_id AND addresses.email_address = ? ORDER BY users.oid
['jack@google.com']
{stop}[<User(u'jack',u'Jack Bean', u'gjffdd')>]
Or we can make a real JOIN construct; below we use the `join()` function available on `Table` to create a `Join` object, then tell the `Query` to use it as our FROM clause:
{python}
{sql}>>> session.query(User).select_from(users_table.join(addresses_table)).\
... filter(Address.email_address=='jack@google.com').all()
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users JOIN addresses ON users.id = addresses.user_id
WHERE addresses.email_address = ? ORDER BY users.oid
['jack@google.com']
{stop}[<User(u'jack',u'Jack Bean', u'gjffdd')>]
Note that the `join()` construct has no problem figuring out the correct join condition between `users_table` and `addresses_table`..the `ForeignKey` we constructed says it all.
The easiest way to join is automatically, using the `join()` method on `Query`. Just give this method the path from A to B, using the name of a mapped relationship directly:
{python}
{sql}>>> session.query(User).join('addresses').\
... filter(Address.email_address=='jack@google.com').all()
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users JOIN addresses ON users.id = addresses.user_id
WHERE addresses.email_address = ? ORDER BY users.oid
['jack@google.com']
{stop}[<User(u'jack',u'Jack Bean', u'gjffdd')>]
By "A to B", we mean a single relation name or a path of relations. In our case we only have `User->addresses->Address` configured, but if we had a setup like `A->bars->B->bats->C->widgets->D`, a join along all four entities would look like:
{python}
session.query(Foo).join(['bars', 'bats', 'widgets']).filter(...)
Each time `join()` is called on `Query`, the **joinpoint** of the query is moved to be that of the endpoint of the join. As above, when we joined from `users_table` to `addresses_table`, all subsequent criterion used by `filter_by()` are against the `addresses` table. When you `join()` again, the joinpoint starts back from the root. We can also backtrack to the beginning explicitly using `reset_joinpoint()`. This instruction will place the joinpoint back at the root `users` table, where subsequent `filter_by()` criterion are again against `users`:
{python}
{sql}>>> session.query(User).join('addresses').\
... filter_by(email_address='jack@google.com').\
... reset_joinpoint().filter_by(name='jack').all()
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users JOIN addresses ON users.id = addresses.user_id
WHERE addresses.email_address = ? AND users.name = ? ORDER BY users.oid
['jack@google.com', 'jack']
{stop}[<User(u'jack',u'Jack Bean', u'gjffdd')>]
In all cases, we can get the `User` and the matching `Address` objects back at the same time, by telling the session we want both. This returns the results as a list of tuples:
{python}
{sql}>>> session.query(User).add_entity(Address).join('addresses').\
... filter(Address.email_address=='jack@google.com').all()
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password, addresses.id AS addresses_id, addresses.email_address AS addresses_email_address, addresses.user_id AS addresses_user_id
FROM users JOIN addresses ON users.id = addresses.user_id
WHERE addresses.email_address = ? ORDER BY users.oid
['jack@google.com']
{stop}[(<User(u'jack',u'Jack Bean', u'gjffdd')>, <Address(u'jack@google.com')>)]
Another common scenario is the need to join on the same table more than once. For example, if we want to find a `User` who has two distinct email addresses, both `jack@google.com` as well as `j25@yahoo.com`, we need to join to the `Addresses` table twice. SQLAlchemy does provide `Alias` objects which can accomplish this; but far easier is just to tell `join()` to alias for you:
{python}
{sql}>>> session.query(User).\
... join('addresses', aliased=True).filter(Address.email_address=='jack@google.com').\
... join('addresses', aliased=True).filter(Address.email_address=='j25@yahoo.com').all()
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users JOIN addresses AS addresses_1 ON users.id = addresses_1.user_id JOIN addresses AS addresses_2 ON users.id = addresses_2.user_id
WHERE addresses_1.email_address = ? AND addresses_2.email_address = ? ORDER BY users.oid
['jack@google.com', 'j25@yahoo.com']
{stop}[<User(u'jack',u'Jack Bean', u'gjffdd')>]
The key thing which occured above is that our SQL criterion were **aliased** as appropriate corresponding to the alias generated in the most recent `join()` call.
## Deleting
Let's try to delete `jack` and see how that goes. We'll mark as deleted in the session, then we'll issue a `count` query to see that no rows remain:
{python}
>>> session.delete(jack)
{sql}>>> session.query(User).filter_by(name='jack').count()
UPDATE addresses SET user_id=? WHERE addresses.id = ?
[None, 1]
UPDATE addresses SET user_id=? WHERE addresses.id = ?
[None, 2]
DELETE FROM users WHERE users.id = ?
[5]
SELECT count(users.id)
FROM users
WHERE users.name = ?
['jack']
{stop}0
So far, so good. How about Jack's `Address` objects ?
{python}
{sql}>>> session.query(Address).filter(
... Address.email_address.in_('jack@google.com', 'j25@yahoo.com')
... ).count()
SELECT count(addresses.id)
FROM addresses
WHERE addresses.email_address IN (?, ?)
['jack@google.com', 'j25@yahoo.com']
{stop}2
Uh oh, they're still there ! Anaylzing the flush SQL, we can see that the `user_id` column of each addresss was set to NULL, but the rows weren't deleted. SQLAlchemy doesn't assume that deletes cascade, you have to tell it so.
So let's rollback our work, and start fresh with new mappers that express the relationship the way we want:
{python}
{sql}>>> session.rollback() # roll back the transaction
ROLLBACK
>>> session.clear() # clear the session
>>> clear_mappers() # clear mappers
We need to tell the `addresses` relation on `User` that we'd like session.delete() operations to cascade down to the child `Address` objects. Further, we also want `Address` objects which get detached from their parent `User`, whether or not the parent is deleted, to be deleted. For these behaviors we use two **cascade options** `delete` and `delete-orphan`, using the string-based `cascade` option to the `relation()` function:
{python}
>>> mapper(User, users_table, properties={ # doctest: +ELLIPSIS
... 'addresses':relation(Address, backref='user', cascade="all, delete, delete-orphan")
... })
<sqlalchemy.orm.mapper.Mapper object at 0x...>
>>> mapper(Address, addresses_table) # doctest: +ELLIPSIS
<sqlalchemy.orm.mapper.Mapper object at 0x...>
Now when we load Jack, removing an address from his `addresses` collection will result in that `Address` being deleted:
{python}
# load Jack by primary key
{sql}>>> jack = session.query(User).get(jack.id) #doctest: +NORMALIZE_WHITESPACE
BEGIN
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.id = ? ORDER BY users.oid
LIMIT 1 OFFSET 0
[5]
{stop}
# remove one Address (lazy load fires off)
{sql}>>> del jack.addresses[1]
SELECT addresses.id AS addresses_id, addresses.email_address AS addresses_email_address, addresses.user_id AS addresses_user_id
FROM addresses
WHERE ? = addresses.user_id ORDER BY addresses.oid
[5]
{stop}
# only one address remains
{sql}>>> session.query(Address).filter(
... Address.email_address.in_('jack@google.com', 'j25@yahoo.com')
... ).count()
DELETE FROM addresses WHERE addresses.id = ?
[2]
SELECT count(addresses.id)
FROM addresses
WHERE addresses.email_address IN (?, ?)
['jack@google.com', 'j25@yahoo.com']
{stop}1
Deleting Jack will delete both Jack and his remaining `Address`:
{python}
>>> session.delete(jack)
{sql}>>> session.commit()
DELETE FROM addresses WHERE addresses.id = ?
[1]
DELETE FROM users WHERE users.id = ?
[5]
COMMIT
{stop}
{sql}>>> session.query(User).filter_by(name='jack').count()
BEGIN
SELECT count(users.id)
FROM users
WHERE users.name = ?
['jack']
{stop}0
{sql}>>> session.query(Address).filter(
... Address.email_address.in_('jack@google.com', 'j25@yahoo.com')
... ).count()
SELECT count(addresses.id)
FROM addresses
WHERE addresses.email_address IN (?, ?)
['jack@google.com', 'j25@yahoo.com']
{stop}0
## Building a Many To Many Relation {@name=manytomany}
We're moving into the bonus round here, but lets show off a many-to-many relationship. We'll sneak in some other features too, just to take a tour. We'll make our application a blog application, where users can write `BlogPost`s, which have `Keywords` associated with them.
First some new tables:
{python}
>>> post_table = Table('posts', metadata,
... Column('id', Integer, primary_key=True),
... Column('user_id', Integer, ForeignKey('users.id')),
... Column('headline', String(255), nullable=False),
... Column('body', String)
... )
>>> post_keywords = Table('post_keywords', metadata,
... Column('post_id', Integer, ForeignKey('posts.id')),
... Column('keyword_id', Integer, ForeignKey('keywords.id')))
>>> keywords_table = Table('keywords', metadata,
... Column('id', Integer, primary_key=True),
... Column('keyword', String(50), nullable=False, unique=True))
{sql}>>> metadata.create_all(engine) # doctest: +NORMALIZE_WHITESPACE
PRAGMA table_info("users")
{}
PRAGMA table_info("posts")
{}
PRAGMA table_info("keywords")
{}
PRAGMA table_info("post_keywords")
{}
PRAGMA table_info("addresses")
{}
CREATE TABLE posts (
id INTEGER NOT NULL,
user_id INTEGER,
headline VARCHAR(255) NOT NULL,
body TEXT,
PRIMARY KEY (id),
FOREIGN KEY(user_id) REFERENCES users (id)
)
None
COMMIT
CREATE TABLE keywords (
id INTEGER NOT NULL,
keyword VARCHAR(50) NOT NULL,
PRIMARY KEY (id),
UNIQUE (keyword)
)
None
COMMIT
CREATE TABLE post_keywords (
post_id INTEGER,
keyword_id INTEGER,
FOREIGN KEY(post_id) REFERENCES posts (id),
FOREIGN KEY(keyword_id) REFERENCES keywords (id)
)
None
COMMIT
Then some classes:
{python}
>>> class BlogPost(object):
... def __init__(self, headline, body, author):
... self.author = author
... self.headline = headline
... self.body = body
... def __repr__(self):
... return "BlogPost(%r, %r, %r)" % (self.headline, self.body, self.author)
>>> class Keyword(object):
... def __init__(self, keyword):
... self.keyword = keyword
And the mappers. `BlogPost` will reference `User` via its `author` attribute:
{python}
>>> from sqlalchemy.orm import backref
>>> mapper(Keyword, keywords_table) # doctest: +ELLIPSIS
<sqlalchemy.orm.mapper.Mapper object at 0x...>
>>> mapper(BlogPost, post_table, properties={ # doctest: +ELLIPSIS
... 'author':relation(User, backref=backref('posts', lazy='dynamic')),
... 'keywords':relation(Keyword, secondary=post_keywords)
... })
<sqlalchemy.orm.mapper.Mapper object at 0x...>
There's three new things in the above mapper:
* the `User` relation has a backref, like we've used before, except this time it references a function called `backref()`. This function is used when you'd like to specify keyword options for the backwards relationship.
* the keyword option we specified to `backref()` is `lazy="dynamic"`. This sets a default **loader strategy** on the attribute, in this case a special strategy that allows partial loading of results.
* The `keywords` relation uses a keyword argument `secondary` to indicate the **association table** for the many to many relationship from `BlogPost` to `Keyword`.
Usage is not too different from what we've been doing. Let's give Wendy some blog posts:
{python}
{sql}>>> wendy = session.query(User).filter_by(name='wendy').one()
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name = ? ORDER BY users.oid
LIMIT 2 OFFSET 0
['wendy']
>>> post = BlogPost("Wendy's Blog Post", "This is a test", wendy)
>>> session.save(post)
We're storing keywords uniquely in the database, but we know that we don't have any yet, so we can just create them:
{python}
>>> post.keywords.append(Keyword('wendy'))
>>> post.keywords.append(Keyword('firstpost'))
We can now look up all blog posts with the keyword 'firstpost'. We'll use a special collection operator `any` to locate "blog posts where any of its keywords has the keyword string 'firstpost'":
{python}
{sql}>>> session.query(BlogPost).filter(BlogPost.keywords.any(keyword='firstpost')).all()
INSERT INTO keywords (keyword) VALUES (?)
['wendy']
INSERT INTO keywords (keyword) VALUES (?)
['firstpost']
INSERT INTO posts (user_id, headline, body) VALUES (?, ?, ?)
[2, "Wendy's Blog Post", 'This is a test']
INSERT INTO post_keywords (post_id, keyword_id) VALUES (?, ?)
[[1, 1], [1, 2]]
SELECT posts.id AS posts_id, posts.user_id AS posts_user_id, posts.headline AS posts_headline, posts.body AS posts_body
FROM posts
WHERE EXISTS (SELECT 1
FROM post_keywords, keywords
WHERE posts.id = post_keywords.post_id AND keywords.id = post_keywords.keyword_id AND keywords.keyword = ?) ORDER BY posts.oid
['firstpost']
{stop}[BlogPost("Wendy's Blog Post", 'This is a test', <User(u'wendy',u'Wendy Williams', u'foobar')>)]
If we want to look up just Wendy's posts, we can tell the query to narrow down to her as a parent:
{python}
{sql}>>> session.query(BlogPost).with_parent(wendy).\
... filter(BlogPost.keywords.any(keyword='firstpost')).all()
SELECT posts.id AS posts_id, posts.user_id AS posts_user_id, posts.headline AS posts_headline, posts.body AS posts_body
FROM posts
WHERE ? = posts.user_id AND (EXISTS (SELECT 1
FROM post_keywords, keywords
WHERE posts.id = post_keywords.post_id AND keywords.id = post_keywords.keyword_id AND keywords.keyword = ?)) ORDER BY posts.oid
[2, 'firstpost']
{stop}[BlogPost("Wendy's Blog Post", 'This is a test', <User(u'wendy',u'Wendy Williams', u'foobar')>)]
Or we can use Wendy's own `posts` relation, which is a "dynamic" relation, to query straight from there:
{python}
{sql}>>> wendy.posts.filter(BlogPost.keywords.any(keyword='firstpost')).all()
SELECT posts.id AS posts_id, posts.user_id AS posts_user_id, posts.headline AS posts_headline, posts.body AS posts_body
FROM posts
WHERE ? = posts.user_id AND (EXISTS (SELECT 1
FROM post_keywords, keywords
WHERE posts.id = post_keywords.post_id AND keywords.id = post_keywords.keyword_id AND keywords.keyword = ?)) ORDER BY posts.oid
[2, 'firstpost']
{stop}[BlogPost("Wendy's Blog Post", 'This is a test', <User(u'wendy',u'Wendy Williams', u'foobar')>)]
## Further Reference
Generated Documentation for Query: [docstrings_sqlalchemy.orm.query_Query](rel:docstrings_sqlalchemy.orm.query_Query)
ORM Generated Docs: [docstrings_sqlalchemy.orm](rel:docstrings_sqlalchemy.orm)
Further information on mapping setups are in [advdatamapping](rel:advdatamapping).
Further information on working with Sessions: [unitofwork](rel:unitofwork).
-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, properties={
'kw': relation(Keyword, secondary=userkeywords)
})
mapper(Keyword, keywords)
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)
-615
View File
@@ -1,615 +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.
Also, 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 `dict` by default. This means that objects stay in the session until they are explicitly removed, or the session is cleared. Some people prefer objects to be automatically garbage collected instead. For this, you can specify the flag `weak_identity_map=True` to the `create_session` or `sessionmaker` functions so that the `Session` will use a `weakref.WeakValueDictionary`. But note that an object that has changes marked on it (i.e. "dirty"), and is then discarded, will not have its changes saved to the database. It's because of this misleading behavior that SQLAlchemy's identity map is by default strong-referencing.
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
Note that if a session is created with the `weak_identity_map` flag, an item which is marked as "dirty" will be silently removed from the session if the item falls out of scope in the user application. This is because the unit of work does not look for "dirty" changes except for within a flush operation (or any time the session.dirty collection is accessed).
However, the `new` and `deleted` collections are strong-referencing sets. Even if you're using `weak_identity_map`, if you abandon all references to new or deleted objects within a session, *they are still present* in either of those two lists, and will be saved on the next flush operation, unless they are removed from the Session explicitly or the session is cleared.
### 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)
### 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)
## 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}
[this section is TODO]
{python}
Session = scoped_session(sessionmaker(autoflush=True, transactional=True))
[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)
-957
View File
@@ -1,957 +0,0 @@
SQL Expression Language Tutorial {@name=sql}
===============================================
**TUTORIAL FORMAT IS TODO**
For this section, we will mostly use the implcit style of execution, meaning the `Table` objects are associated with a bound instance of `MetaData`, and constructed `ClauseElement` objects support self-execution. Assume the following configuration:
{python}
from sqlalchemy import *
metadata = MetaData('sqlite:///mydb.db', echo=True)
# a table to store users
users = Table('users', metadata,
Column('user_id', Integer, primary_key = True),
Column('user_name', String(40)),
Column('password', String(80))
)
# a table that stores mailing addresses associated with a specific user
addresses = Table('addresses', metadata,
Column('address_id', Integer, primary_key = True),
Column('user_id', Integer, ForeignKey("users.user_id")),
Column('street', String(100)),
Column('city', String(80)),
Column('state', String(2)),
Column('zip', String(10))
)
# a table that stores keywords
keywords = Table('keywords', metadata,
Column('keyword_id', Integer, primary_key = True),
Column('name', VARCHAR(50))
)
# a table that associates keywords with users
userkeywords = Table('userkeywords', metadata,
Column('user_id', INT, ForeignKey("users")),
Column('keyword_id', INT, ForeignKey("keywords"))
)
### Simple Select {@name=select}
A select is done by constructing a `Select` object with the proper arguments [[api](rel:docstrings_sqlalchemy.sql_modfunc_select)], adding any extra arguments if desired, then calling its `execute()` method.
{python title="Basic Select"}
from sqlalchemy import *
# use the select() function defined in the sql package
s = select([users])
# or, call the select() method off of a Table object
s = users.select()
# then, call execute on the Select object:
{sql}result = s.execute()
SELECT users.user_id, users.user_name, users.password FROM users
{}
# the SQL text of any clause object can also be viewed via the str() call:
>>> str(s)
SELECT users.user_id, users.user_name, users.password FROM users
#### Explicit Execution {@name=explicit}
As mentioned above, `ClauseElement` structures can also be executed with a `Connection` object explicitly:
{python}
engine = create_engine('sqlite:///myfile.db')
conn = engine.connect()
{sql}result = conn.execute(users.select())
SELECT users.user_id, users.user_name, users.password FROM users
{}
conn.close()
#### Binding ClauseElements to Engines {@name=binding}
For queries that don't contain any "bound" tables, `ClauseElement`s that represent a fully executeable statement support an `bind` keyword parameter which can bind the object to an `Engine` or `Connection`, thereby allowing implicit execution:
{python}
# select using a table
{sql}select([users], bind=myengine).execute()
SELECT users.user_id, users.user_name, users.password FROM users
{}
# select a literal
{sql}select(["current_time"], bind=myengine).execute()
SELECT current_time
{}
# select a function
{sql}select([func.now()], bind=db).execute()
SELECT now()
{}
#### Getting Results {@name=resultproxy}
The object returned by `execute()` is a `sqlalchemy.engine.ResultProxy` object, which acts much like a DBAPI `cursor` object in the context of a result set, except that the rows returned can address their columns by ordinal position, column name, or even column object:
{python title="Using the ResultProxy"}
# select rows, get resulting ResultProxy object
{sql}result = users.select().execute()
SELECT users.user_id, users.user_name, users.password FROM users
{}
# get one row
row = result.fetchone()
# get the 'user_id' column via integer index:
user_id = row[0]
# or column name
user_name = row['user_name']
# or column object
password = row[users.c.password]
# or column accessor
password = row.password
# ResultProxy object also supports fetchall()
rows = result.fetchall()
# or get the underlying DBAPI cursor object
cursor = result.cursor
# after an INSERT, return the last inserted primary key value
# returned as a list of primary key values for *one* row
# (a list since primary keys can be composite)
id = result.last_inserted_ids()
# close the result. If the statement was implicitly executed
# (i.e. without an explicit Connection), this will
# return the underlying connection resources back to
# the connection pool. de-referencing the result
# will also have the same effect. if an explicit Connection was
# used, then close() just closes the underlying cursor object.
result.close()
#### Using Column Labels {@name=labels}
A common need when writing statements that reference multiple tables is to create labels for columns, thereby separating columns from different tables with the same name. The Select construct supports automatic generation of column labels via the `use_labels=True` parameter:
{python title="use_labels Flag"}
{sql}c = select([users, addresses],
users.c.user_id==addresses.c.address_id,
use_labels=True).execute()
SELECT users.user_id AS users_user_id, users.user_name AS users_user_name,
users.password AS users_password, addresses.address_id AS addresses_address_id,
addresses.user_id AS addresses_user_id, addresses.street AS addresses_street,
addresses.city AS addresses_city, addresses.state AS addresses_state,
addresses.zip AS addresses_zip
FROM users, addresses
WHERE users.user_id = addresses.address_id
{}
The table name part of the label is affected if you use a construct such as a table alias:
{python title="use_labels with an Alias"}
person = users.alias('person')
{sql}c = select([person, addresses],
person.c.user_id==addresses.c.address_id,
use_labels=True).execute()
SELECT person.user_id AS person_user_id, person.user_name AS person_user_name,
person.password AS person_password, addresses.address_id AS addresses_address_id,
addresses.user_id AS addresses_user_id, addresses.street AS addresses_street,
addresses.city AS addresses_city, addresses.state AS addresses_state,
addresses.zip AS addresses_zip FROM users AS person, addresses
WHERE person.user_id = addresses.address_id
Labels are also generated in such a way as to never go beyond 30 characters. Most databases support a limit on the length of symbols, such as Postgres, and particularly Oracle which has a rather short limit of 30:
{python title="use_labels Generates Abbreviated Labels"}
long_named_table = users.alias('this_is_the_person_table')
{sql}c = select([long_named_table], use_labels=True).execute()
SELECT this_is_the_person_table.user_id AS this_is_the_person_table_b36c,
this_is_the_person_table.user_name AS this_is_the_person_table_f76a,
this_is_the_person_table.password AS this_is_the_person_table_1e7c
FROM users AS this_is_the_person_table
{}
You can also specify custom labels on a per-column basis using the `label()` function:
{python title="label() Function on Column"}
{sql}c = select([users.c.user_id.label('id'),
users.c.user_name.label('name')]).execute()
SELECT users.user_id AS id, users.user_name AS name
FROM users
{}
#### Table/Column Specification {@name=columns}
Calling `select` off a table automatically generates a column clause which includes all the table's columns, in the order they are specified in the source Table object.
But in addition to selecting all the columns off a single table, any set of columns can be specified, as well as full tables, and any combination of the two:
{python title="Specify Columns to Select"}
# individual columns
{sql}c = select([users.c.user_id, users.c.user_name]).execute()
SELECT users.user_id, users.user_name FROM users
{}
# full tables
{sql}c = select([users, addresses]).execute()
SELECT users.user_id, users.user_name, users.password,
addresses.address_id, addresses.user_id,
addresses.street, addresses.city, addresses.state, addresses.zip
FROM users, addresses
{}
# combinations
{sql}c = select([users, addresses.c.zip]).execute()
SELECT users.user_id, users.user_name, users.password,
addresses.zip FROM users, addresses
{}
### WHERE Clause {@name=whereclause}
The WHERE condition is the named keyword argument `whereclause`, or the second positional argument to the `select()` constructor and the first positional argument to the `select()` method of `Table`.
WHERE conditions are constructed using column objects, literal values, and functions defined in the `sqlalchemy.sql` module. Column objects override the standard Python operators to provide clause compositional objects, which compile down to SQL operations:
{python title="Basic WHERE Clause"}
{sql}c = users.select(users.c.user_id == 7).execute()
SELECT users.user_id, users.user_name, users.password,
FROM users WHERE users.user_id = :users_user_id
{'users_user_id': 7}
Notice that the literal value "7" was broken out of the query and placed into a bind parameter. Databases such as Oracle must parse incoming SQL and create a "plan" when new queries are received, which is an expensive process. By using bind parameters, the same query with various literal values can have its plan compiled only once, and used repeatedly with less overhead.
More where clauses:
{python}
# another comparison operator
{sql}c = select([users], users.c.user_id>7).execute()
SELECT users.user_id, users.user_name, users.password,
FROM users WHERE users.user_id > :users_user_id
{'users_user_id': 7}
# OR keyword
{sql}c = users.select(or_(users.c.user_name=='jack', users.c.user_name=='ed')).execute()
SELECT users.user_id, users.user_name, users.password
FROM users WHERE users.user_name = :users_user_name
OR users.user_name = :users_user_name_1
{'users_user_name_1': 'ed', 'users_user_name': 'jack'}
# AND keyword
{sql}c = users.select(and_(users.c.user_name=='jack', users.c.password=='dog')).execute()
SELECT users.user_id, users.user_name, users.password
FROM users WHERE users.user_name = :users_user_name
AND users.password = :users_password
{'users_user_name': 'jack', 'users_password': 'dog'}
# NOT keyword
{sql}c = users.select(not_(
or_(users.c.user_name=='jack', users.c.password=='dog')
)).execute()
SELECT users.user_id, users.user_name, users.password
FROM users
WHERE NOT (users.user_name = :users_user_name
OR users.password = :users_password)
{'users_user_name': 'jack', 'users_password': 'dog'}
# IN clause
{sql}c = users.select(users.c.user_name.in_('jack', 'ed', 'fred')).execute()
SELECT users.user_id, users.user_name, users.password
FROM users WHERE users.user_name
IN (:users_user_name, :users_user_name_1, :users_user_name_2)
{'users_user_name': 'jack', 'users_user_name_1': 'ed',
'users_user_name_2': 'fred'}
# join users and addresses together
{sql}c = select([users, addresses], users.c.user_id==addresses.c.address_id).execute()
SELECT users.user_id, users.user_name, users.password,
addresses.address_id, addresses.user_id, addresses.street, addresses.city,
addresses.state, addresses.zip
FROM users, addresses
WHERE users.user_id = addresses.address_id
{}
# join users and addresses together, but dont specify "addresses" in the
# selection criterion. The WHERE criterion adds it to the FROM list
# automatically.
{sql}c = select([users], and_(
users.c.user_id==addresses.c.user_id,
users.c.user_name=='fred'
)).execute()
SELECT users.user_id, users.user_name, users.password
FROM users, addresses WHERE users.user_id = addresses.user_id
AND users.user_name = :users_user_name
{'users_user_name': 'fred'}
Select statements can also generate a WHERE clause based on the parameters you give it. If a given parameter, which matches the name of a column or its "label" (the combined tablename + "_" + column name), and does not already correspond to a bind parameter in the select object, it will be added as a comparison against that column. This is a shortcut to creating a full WHERE clause:
{python}
# specify a match for the "user_name" column
{sql}c = users.select().execute(user_name='ed')
SELECT users.user_id, users.user_name, users.password
FROM users WHERE users.user_name = :users_user_name
{'users_user_name': 'ed'}
# specify a full where clause for the "user_name" column, as well as a
# comparison for the "user_id" column
{sql}c = users.select(users.c.user_name=='ed').execute(user_id=10)
SELECT users.user_id, users.user_name, users.password
FROM users WHERE users.user_name = :users_user_name AND users.user_id = :users_user_id
{'users_user_name': 'ed', 'users_user_id': 10}
#### Operators {@name=operators}
Supported column operators so far are all the numerical comparison operators, i.e. '==', '>', '>=', etc., as well as `like()`, `startswith()`, `endswith()`, `between()`, and `in()`. Boolean operators include `not_()`, `and_()` and `or_()`, which also can be used inline via '~', '&amp;', and '|'. Math operators are '+', '-', '*', '/'. Any custom operator can be specified via the `op()` function shown below.
{python}
# "like" operator
users.select(users.c.user_name.like('%ter'))
# equality operator
users.select(users.c.user_name == 'jane')
# in opertator
users.select(users.c.user_id.in_(1,2,3))
# and_, endswith, equality operators
users.select(and_(addresses.c.street.endswith('green street'),
addresses.c.zip=='11234'))
# & operator subsituting for 'and_'
users.select(addresses.c.street.endswith('green street') & (addresses.c.zip=='11234'))
# + concatenation operator
select([users.c.user_name + '_name'])
# NOT operator
users.select(~(addresses.c.street == 'Green Street'))
# any custom operator
select([users.c.user_name.op('||')('_category')])
# "null" comparison via == (converts to IS)
{sql}users.select(users.c.user_name==None).execute()
SELECT users.user_id, users.user_name, users.password
FROM users
WHERE users.user_name IS NULL
# or via explicit null() construct
{sql}users.select(users.c.user_name==null()).execute()
SELECT users.user_id, users.user_name, users.password
FROM users
WHERE users.user_name IS NULL
#### Functions {@name=functions}
Functions can be specified using the `func` keyword:
{python}
{sql}select([func.count(users.c.user_id)]).execute()
SELECT count(users.user_id) FROM users
{sql}users.select(func.substr(users.c.user_name, 1) == 'J').execute()
SELECT users.user_id, users.user_name, users.password FROM users
WHERE substr(users.user_name, :substr) = :substr_1
{'substr_1': 'J', 'substr': 1}
Functions also are callable as standalone values:
{python}
# call the "now()" function
time = func.now(bind=myengine).scalar()
# call myfunc(1,2,3)
myvalue = func.myfunc(1, 2, 3, bind=db).execute()
# or call them off the engine
db.func.now().scalar()
#### Literals {@name=literals}
You can drop in a literal value anywhere there isnt a column to attach to via the `literal` keyword:
{python}
{sql}select([literal('foo') + literal('bar'), users.c.user_name]).execute()
SELECT :literal + :literal_1, users.user_name
FROM users
{'literal_1': 'bar', 'literal': 'foo'}
# literals have all the same comparison functions as columns
{sql}select([literal('foo') == literal('bar')], bind=myengine).scalar()
SELECT :literal = :literal_1
{'literal_1': 'bar', 'literal': 'foo'}
Literals also take an optional `type` parameter to give literals a type. This can sometimes be significant, for example when using the "+" operator with SQLite, the String type is detected and the operator is converted to "||":
{python}
{sql}select([literal('foo', type=String) + 'bar'], bind=e).execute()
SELECT ? || ?
['foo', 'bar']
#### Order By {@name=orderby}
The ORDER BY clause of a select statement can be specified as individual columns to order by within an array specified via the `order_by` parameter, and optional usage of the asc() and desc() functions:
{python}
# straight order by
{sql}c = users.select(order_by=[users.c.user_name]).execute()
SELECT users.user_id, users.user_name, users.password
FROM users ORDER BY users.user_name
# descending/ascending order by on multiple columns
{sql}c = users.select(
users.c.user_name>'J',
order_by=[desc(users.c.user_id), asc(users.c.user_name)]).execute()
SELECT users.user_id, users.user_name, users.password
FROM users WHERE users.user_name > :users_user_name
ORDER BY users.user_id DESC, users.user_name ASC
{'users_user_name':'J'}
#### DISTINCT, LIMIT and OFFSET {@name=options}
These are specified as keyword arguments:
{python}
{sql}c = select([users.c.user_name], distinct=True).execute()
SELECT DISTINCT users.user_name FROM users
{sql}c = users.select(limit=10, offset=20).execute()
SELECT users.user_id, users.user_name, users.password FROM users LIMIT 10 OFFSET 20
The Oracle driver does not support LIMIT and OFFSET directly, but instead wraps the generated query into a subquery and uses the "rownum" variable to control the rows selected (this is somewhat experimental). Similarly, the Firebird and MSSQL drivers convert LIMIT into queries using FIRST and TOP, respectively.
### Inner and Outer Joins {@name=join}
As some of the examples indicated above, a regular inner join can be implicitly stated, just like in a SQL expression, by just specifying the tables to be joined as well as their join conditions:
{python}
{sql}addresses.select(addresses.c.user_id==users.c.user_id).execute()
SELECT addresses.address_id, addresses.user_id, addresses.street,
addresses.city, addresses.state, addresses.zip FROM addresses, users
WHERE addresses.user_id = users.user_id
{}
There is also an explicit join constructor, which can be embedded into a select query via the `from_obj` parameter of the select statement:
{python}
{sql}addresses.select(from_obj=[
addresses.join(users, addresses.c.user_id==users.c.user_id)
]).execute()
SELECT addresses.address_id, addresses.user_id, addresses.street, addresses.city,
addresses.state, addresses.zip
FROM addresses JOIN users ON addresses.user_id = users.user_id
{}
The join constructor can also be used by itself:
{python}
{sql}join(users, addresses, users.c.user_id==addresses.c.user_id).select().execute()
SELECT users.user_id, users.user_name, users.password,
addresses.address_id, addresses.user_id, addresses.street, addresses.city,
addresses.state, addresses.zip
FROM addresses JOIN users ON addresses.user_id = users.user_id
{}
The join criterion in a join() call is optional. If not specified, the condition will be derived from the foreign key relationships of the two tables. If no criterion can be constructed, an exception will be raised.
{python}
{sql}join(users, addresses).select().execute()
SELECT users.user_id, users.user_name, users.password,
addresses.address_id, addresses.user_id, addresses.street, addresses.city,
addresses.state, addresses.zip
FROM addresses JOIN users ON addresses.user_id = users.user_id
{}
Notice that this is the first example where the FROM criterion of the select statement is explicitly specified. In most cases, the FROM criterion is automatically determined from the columns requested as well as the WHERE clause. The `from_obj` keyword argument indicates a list of explicit FROM clauses to be used in the statement.
A join can be created on its own using the `join` or `outerjoin` functions, or can be created off of an existing Table or other selectable unit via the `join` or `outerjoin` methods:
{python}
{sql}outerjoin(users, addresses,
users.c.user_id==addresses.c.address_id).select().execute()
SELECT users.user_id, users.user_name, users.password, addresses.address_id,
addresses.user_id, addresses.street, addresses.city, addresses.state, addresses.zip
FROM users LEFT OUTER JOIN addresses ON users.user_id = addresses.address_id
{}
{sql}users.select(keywords.c.name=='running', from_obj=[
users.join(
userkeywords, userkeywords.c.user_id==users.c.user_id).join(
keywords, keywords.c.keyword_id==userkeywords.c.keyword_id)
]).execute()
SELECT users.user_id, users.user_name, users.password FROM users
JOIN userkeywords ON userkeywords.user_id = users.user_id
JOIN keywords ON keywords.keyword_id = userkeywords.keyword_id
WHERE keywords.name = :keywords_name
{'keywords_name': 'running'}
Joins also provide a keyword argument `fold_equivalents` on the `select()` function which allows the column list of the resulting select to be "folded" to the minimal list of columns, based on those columns that are known to be equivalent from the "onclause" of the join. This saves the effort of constructing column lists manually in conjunction with databases like Postgres which can be picky about "ambiguous columns". In this example, only the "users.user_id" column, but not the "addresses.user_id" column, shows up in the column clause of the resulting select:
{python}
{sql}users.join(addresses).select(fold_equivalents=True).execute()
SELECT users.user_id, users.user_name, users.password, addresses.address_id,
addresses.street, addresses.city, addresses.state, addresses.zip
FROM users JOIN addresses ON users.user_id = addresses.address_id
{}
The `fold_equivalents` argument will recursively apply to "chained" joins as well, i.e. `a.join(b).join(c)...`.
### Table Aliases {@name=alias}
Aliases are used primarily when you want to use the same table more than once as a FROM expression in a statement:
{python}
address_b = addresses.alias('addressb')
{sql}# select users who have an address on Green street as well as Orange street
users.select(and_(
users.c.user_id==addresses.c.user_id,
addresses.c.street.like('%Green%'),
users.c.user_id==address_b.c.user_id,
address_b.c.street.like('%Orange%')
)).execute()
SELECT users.user_id, users.user_name, users.password
FROM users, addresses, addresses AS addressb
WHERE users.user_id = addresses.user_id
AND addresses.street LIKE :addresses_street
AND users.user_id = addressb.user_id
AND addressb.street LIKE :addressb_street
{'addressb_street': '%Orange%', 'addresses_street': '%Green%'}
### Subqueries {@name=subqueries}
SQLAlchemy allows the creation of select statements from not just Table objects, but from a whole class of objects that implement the `Selectable` interface. This includes Tables, Aliases, Joins and Selects. Therefore, if you have a Select, you can select from the Select:
{python}
>>> s = users.select()
>>> str(s)
SELECT users.user_id, users.user_name, users.password FROM users
{python}
>>> s = s.select()
>>> str(s)
SELECT user_id, user_name, password
FROM (SELECT users.user_id, users.user_name, users.password FROM users)
Any Select, Join, or Alias object supports the same column accessors as a Table:
{python}
>>> s = users.select()
>>> [c.key for c in s.columns]
['user_id', 'user_name', 'password']
When you use `use_labels=True` in a Select object, the label version of the column names become the keys of the accessible columns. In effect you can create your own "view objects":
{python}
s = select([users, addresses], users.c.user_id==addresses.c.user_id, use_labels=True)
{sql}select([
s.c.users_user_name, s.c.addresses_street, s.c.addresses_zip
], s.c.addresses_city=='San Francisco').execute()
SELECT users_user_name, addresses_street, addresses_zip
FROM (SELECT users.user_id AS users_user_id, users.user_name AS users_user_name,
users.password AS users_password, addresses.address_id AS addresses_address_id,
addresses.user_id AS addresses_user_id, addresses.street AS addresses_street,
addresses.city AS addresses_city, addresses.state AS addresses_state,
addresses.zip AS addresses_zip
FROM users, addresses
WHERE users.user_id = addresses.user_id)
WHERE addresses_city = :addresses_city
{'addresses_city': 'San Francisco'}
To specify a SELECT statement as one of the selectable units in a FROM clause, it usually should be given an alias.
{python}
{sql}s = users.select().alias('u')
select([addresses, s]).execute()
SELECT addresses.address_id, addresses.user_id, addresses.street, addresses.city,
addresses.state, addresses.zip, u.user_id, u.user_name, u.password
FROM addresses,
(SELECT users.user_id, users.user_name, users.password FROM users) AS u
{}
Select objects can be used in a WHERE condition, in operators such as IN:
{python}
# select user ids for all users whos name starts with a "p"
s = select([users.c.user_id], users.c.user_name.like('p%'))
# now select all addresses for those users
{sql}addresses.select(addresses.c.user_id.in_(s)).execute()
SELECT addresses.address_id, addresses.user_id, addresses.street,
addresses.city, addresses.state, addresses.zip
FROM addresses WHERE addresses.address_id IN
(SELECT users.user_id FROM users WHERE users.user_name LIKE :users_user_name)
{'users_user_name': 'p%'}
The sql package supports embedding select statements into other select statements as the criterion in a WHERE condition, or as one of the "selectable" objects in the FROM list of the query. It does not at the moment directly support embedding a SELECT statement as one of the column criterion for a statement, although this can be achieved via direct text insertion, described later.
#### Scalar Column Subqueries {@name=scalar}
Subqueries can be used in the column clause of a select statement by specifying the `scalar=True` flag:
{python}
{sql}select([table2.c.col1, table2.c.col2,
select([table1.c.col1], table1.c.col2==7, scalar=True)])
SELECT table2.col1, table2.col2,
(SELECT table1.col1 AS col1 FROM table1 WHERE col2=:table1_col2)
FROM table2
{'table1_col2': 7}
### Correlated Subqueries {@name=correlated}
When a select object is embedded inside of another select object, and both objects reference the same table, SQLAlchemy makes the assumption that the table should be correlated from the child query to the parent query. To disable this behavior, specify the flag `correlate=False` to the Select statement.
{python}
# make an alias of a regular select.
s = select([addresses.c.street], addresses.c.user_id==users.c.user_id).alias('s')
>>> str(s)
SELECT addresses.street FROM addresses, users
WHERE addresses.user_id = users.user_id
# now embed that select into another one. the "users" table is removed from
# the embedded query's FROM list and is instead correlated to the parent query
s2 = select([users, s.c.street])
>>> str(s2)
SELECT users.user_id, users.user_name, users.password, s.street
FROM users, (SELECT addresses.street FROM addresses
WHERE addresses.user_id = users.user_id) s
#### EXISTS Clauses {@name=exists}
An EXISTS clause can function as a higher-scaling version of an IN clause, and is usually used in a correlated fashion:
{python}
# find all users who have an address on Green street:
{sql}users.select(
exists(
[addresses.c.address_id],
and_(
addresses.c.user_id==users.c.user_id,
addresses.c.street.like('%Green%')
)
)
)
SELECT users.user_id, users.user_name, users.password
FROM users WHERE EXISTS (SELECT addresses.address_id
FROM addresses WHERE addresses.user_id = users.user_id
AND addresses.street LIKE :addresses_street)
{'addresses_street': '%Green%'}
### Unions {@name=unions}
Unions come in two flavors, UNION and UNION ALL, which are available via module level functions or methods off a Selectable:
{python}
{sql}union(
addresses.select(addresses.c.street=='123 Green Street'),
addresses.select(addresses.c.street=='44 Park Ave.'),
addresses.select(addresses.c.street=='3 Mill Road'),
order_by=[addresses.c.street]
).execute()
SELECT addresses.address_id, addresses.user_id, addresses.street,
addresses.city, addresses.state, addresses.zip
FROM addresses WHERE addresses.street = :addresses_street
UNION
SELECT addresses.address_id, addresses.user_id, addresses.street,
addresses.city, addresses.state, addresses.zip
FROM addresses WHERE addresses.street = :addresses_street_1
UNION
SELECT addresses.address_id, addresses.user_id, addresses.street,
addresses.city, addresses.state, addresses.zip
FROM addresses WHERE addresses.street = :addresses_street_2
ORDER BY addresses.street
{'addresses_street_1': '44 Park Ave.',
'addresses_street': '123 Green Street',
'addresses_street_2': '3 Mill Road'}
{sql}users.select(
users.c.user_id==7
).union_all(
users.select(
users.c.user_id==9
),
order_by=[users.c.user_id] # order_by is an argument to union_all()
).execute()
SELECT users.user_id, users.user_name, users.password
FROM users WHERE users.user_id = :users_user_id
UNION ALL
SELECT users.user_id, users.user_name, users.password
FROM users WHERE users.user_id = :users_user_id_1
ORDER BY users.user_id
{'users_user_id_1': 9, 'users_user_id': 7}
### Custom Bind Parameters {@name=bindparams}
Throughout all these examples, SQLAlchemy is busy creating bind parameters wherever literal expressions occur. You can also specify your own bind parameters with your own names, and use the same statement repeatedly. The bind parameters, shown here in the "named" format, will be converted to the appropriate named or positional style according to the database implementation being used.
{python title="Custom Bind Params"}
s = users.select(users.c.user_name==bindparam('username'))
# execute implicitly
{sql}s.execute(username='fred')
SELECT users.user_id, users.user_name, users.password
FROM users WHERE users.user_name = :username
{'username': 'fred'}
# execute explicitly
conn = engine.connect()
{sql}conn.execute(s, username='fred')
SELECT users.user_id, users.user_name, users.password
FROM users WHERE users.user_name = :username
{'username': 'fred'}
`executemany()` is also available by supplying multiple dictionary arguments instead of keyword arguments to the `execute()` method of `ClauseElement` or `Connection`. Examples can be found later in the sections on INSERT/UPDATE/DELETE.
#### Precompiling a Query {@name=precompiling}
By throwing the `compile()` method onto the end of any query object, the query can be "compiled" by the Engine into a `sqlalchemy.sql.Compiled` object just once, and the resulting compiled object reused, which eliminates repeated internal compilation of the SQL string:
{python}s = users.select(users.c.user_name==bindparam('username')).compile()
s.execute(username='fred')
s.execute(username='jane')
s.execute(username='mary')
### Literal Text Blocks {@name=textual}
The sql package tries to allow free textual placement in as many ways as possible. In the examples below, note that the from_obj parameter is used only when no other information exists within the select object with which to determine table metadata. Also note that in a query where there isnt even table metadata used, the Engine to be used for the query has to be explicitly specified:
{python}
# strings as column clauses
{sql}select(["user_id", "user_name"], from_obj=[users]).execute()
SELECT user_id, user_name FROM users
{}
# strings for full column lists
{sql}select(
["user_id, user_name, password, addresses.*"],
from_obj=[users.alias('u'), addresses]).execute()
SELECT u.user_id, u.user_name, u.password, addresses.*
FROM users AS u, addresses
{}
# functions, etc.
{sql}select([users.c.user_id, "process_string(user_name)"]).execute()
SELECT users.user_id, process_string(user_name) FROM users
{}
# where clauses
{sql}users.select(and_(users.c.user_id==7, "process_string(user_name)=27")).execute()
SELECT users.user_id, users.user_name, users.password FROM users
WHERE users.user_id = :users_user_id AND process_string(user_name)=27
{'users_user_id': 7}
# subqueries
{sql}users.select(
"exists (select 1 from addresses where addresses.user_id=users.user_id)").execute()
SELECT users.user_id, users.user_name, users.password FROM users
WHERE exists (select 1 from addresses where addresses.user_id=users.user_id)
{}
# custom FROM objects
{sql}select(
["*"],
from_obj=["(select user_id, user_name from users)"],
bind=db).execute()
SELECT * FROM (select user_id, user_name from users)
{}
# a full query
{sql}text("select user_name from users", bind=db).execute()
SELECT user_name FROM users
{}
#### Using Bind Parameters in Text Blocks {@name=textual_binds}
Use the format `':paramname'` to define bind parameters inside of a text block. They will be converted to the appropriate format upon compilation:
{python}t = text("select foo from mytable where lala=:hoho", bind=engine)
r = t.execute(hoho=7)
Bind parameters can also be explicit, which allows typing information to be added. Just specify them as a list with keys that match those inside the textual statement:
{python}t = text("select foo from mytable where lala=:hoho",
bindparams=[bindparam('hoho', type=types.String)], bind=engine)
r = t.execute(hoho="im hoho")
Result-row type processing can be added via the `typemap` argument, which is a dictionary of return columns mapped to types:
{python}# specify DateTime type for the 'foo' column in the result set
# sqlite, for example, uses result-row post-processing to construct dates
t = text("select foo from mytable where lala=:hoho",
bindparams=[bindparam('hoho', type=types.String)],
typemap={'foo':types.DateTime}, bind=engine
)
r = t.execute(hoho="im hoho")
# 'foo' is a datetime
year = r.fetchone()['foo'].year
### Building Select Objects {@name=building}
One of the primary motivations for a programmatic SQL library is to allow the piecemeal construction of a SQL statement based on program variables. All the above examples typically show Select objects being created all at once. The Select object also includes "builder" methods to allow building up an object. The below example is a "user search" function, where users can be selected based on primary key, user name, street address, keywords, or any combination:
{python}
def find_users(id=None, name=None, street=None, keywords=None):
statement = users.select()
if id is not None:
statement.append_whereclause(users.c.user_id==id)
if name is not None:
statement.append_whereclause(users.c.user_name==name)
if street is not None:
# append_whereclause joins "WHERE" conditions together with AND
statement.append_whereclause(users.c.user_id==addresses.c.user_id)
statement.append_whereclause(addresses.c.street==street)
if keywords is not None:
statement.append_from(
users.join(userkeywords, users.c.user_id==userkeywords.c.user_id).join(
keywords, userkeywords.c.keyword_id==keywords.c.keyword_id))
statement.append_whereclause(keywords.c.name.in_(keywords))
# to avoid multiple repeats, set query to be DISTINCT:
statement.distinct=True
return statement.execute()
{sql}find_users(id=7)
SELECT users.user_id, users.user_name, users.password
FROM users
WHERE users.user_id = :users_user_id
{'users_user_id': 7}
{sql}find_users(street='123 Green Street')
SELECT users.user_id, users.user_name, users.password
FROM users, addresses
WHERE users.user_id = addresses.user_id AND addresses.street = :addresses_street
{'addresses_street': '123 Green Street'}
{sql}find_users(name='Jack', keywords=['jack','foo'])
SELECT DISTINCT users.user_id, users.user_name, users.password
FROM users JOIN userkeywords ON users.user_id = userkeywords.user_id
JOIN keywords ON userkeywords.keyword_id = keywords.keyword_id
WHERE users.user_name = :users_user_name AND keywords.name IN ('jack', 'foo')
{'users_user_name': 'Jack'}
### Inserts {@name=insert}
An INSERT involves just one table. The Insert object is used via the insert() function, and the specified columns determine what columns show up in the generated SQL. If primary key columns are left out of the criterion, the SQL generator will try to populate them as specified by the particular database engine and sequences, i.e. relying upon an auto-incremented column or explicitly calling a sequence beforehand. Insert statements, as well as updates and deletes, can also execute multiple parameters in one pass via specifying an array of dictionaries as parameters.
The values to be populated for an INSERT or an UPDATE can be specified to the insert()/update() functions as the `values` named argument, or the query will be compiled based on the values of the parameters sent to the execute() method.
{python title="Using insert()"}
# basic insert
{sql}users.insert().execute(user_id=1, user_name='jack', password='asdfdaf')
INSERT INTO users (user_id, user_name, password)
VALUES (:user_id, :user_name, :password)
{'user_name': 'jack', 'password': 'asdfdaf', 'user_id': 1}
# insert just user_name, NULL for others
# will auto-populate primary key columns if they are configured
# to do so
{sql}users.insert().execute(user_name='ed')
INSERT INTO users (user_name) VALUES (:user_name)
{'user_name': 'ed'}
# INSERT with a list:
{sql}users.insert(values=(3, 'jane', 'sdfadfas')).execute()
INSERT INTO users (user_id, user_name, password)
VALUES (:user_id, :user_name, :password)
{'user_id': 3, 'password': 'sdfadfas', 'user_name': 'jane'}
# INSERT with user-defined bind parameters
i = users.insert(
values={'user_name':bindparam('name'), 'password':bindparam('pw')}
)
{sql}i.execute(name='mary', pw='adas5fs')
INSERT INTO users (user_name, password) VALUES (:name, :pw)
{'name': 'mary', 'pw': 'adas5fs'}
# INSERT many - if no explicit 'values' parameter is sent,
# the first parameter list in the list determines
# the generated SQL of the insert (i.e. what columns are present)
# executemany() is used at the DBAPI level
{sql}users.insert().execute(
{'user_id':7, 'user_name':'jack', 'password':'asdfasdf'},
{'user_id':8, 'user_name':'ed', 'password':'asdffcadf'},
{'user_id':9, 'user_name':'fred', 'password':'asttf'},
)
INSERT INTO users (user_id, user_name, password)
VALUES (:user_id, :user_name, :password)
[{'user_name': 'jack', 'password': 'asdfasdf', 'user_id': 7},
{'user_name': 'ed', 'password': 'asdffcadf', 'user_id': 8},
{'user_name': 'fred', 'password': 'asttf', 'user_id': 9}]
### Updates {@name=update}
Updates work a lot like INSERTS, except there is an additional WHERE clause that can be specified.
{python title="Using update()"}
# change 'jack' to 'ed'
{sql}users.update(users.c.user_name=='jack').execute(user_name='ed')
UPDATE users SET user_name=:user_name WHERE users.user_name = :users_user_name
{'users_user_name': 'jack', 'user_name': 'ed'}
# use bind parameters
u = users.update(users.c.user_name==bindparam('name'),
values={'user_name':bindparam('newname')})
{sql}u.execute(name='jack', newname='ed')
UPDATE users SET user_name=:newname WHERE users.user_name = :name
{'newname': 'ed', 'name': 'jack'}
# update a column to another column
{sql}users.update(values={users.c.password:users.c.user_name}).execute()
UPDATE users SET password=users.user_name
{}
# expressions OK too
{sql}users.update(values={users.c.user_id:users.c.user_id + 17}).execute()
UPDATE users SET user_id=users.user_id + :users_user_id
{'users_user_id':17}
# multi-update
{sql}users.update(users.c.user_id==bindparam('id')).execute(
{'id':7, 'user_name':'jack', 'password':'fh5jks'},
{'id':8, 'user_name':'ed', 'password':'fsr234ks'},
{'id':9, 'user_name':'mary', 'password':'7h5jse'},
)
UPDATE users SET user_name=:user_name, password=:password WHERE users.user_id = :id
[{'password': 'fh5jks', 'user_name': 'jack', 'id': 7},
{'password': 'fsr234ks', 'user_name': 'ed', 'id': 8},
{'password': '7h5jse', 'user_name': 'mary', 'id': 9}]
#### Correlated Updates {@name=correlated}
A correlated update lets you update a table using selection from another table, or the same table:
{python}s = select([addresses.c.city], addresses.c.user_id==users.c.user_id)
{sql}users.update(
and_(users.c.user_id>10, users.c.user_id<20),
values={users.c.user_name:s}
).execute()
UPDATE users SET user_name=(SELECT addresses.city
FROM addresses
WHERE addresses.user_id = users.user_id)
WHERE users.user_id > :users_user_id AND users.user_id < :users_user_id_1
{'users_user_id_1': 20, 'users_user_id': 10}
### Deletes {@name=delete}
A delete is formulated like an update, except theres no values:
{python}users.delete(users.c.user_id==7).execute()
users.delete(users.c.user_name.like(bindparam('name'))).execute(
{'name':'%Jack%'},
{'name':'%Ed%'},
{'name':'%Jane%'},
)
users.delete(exists())
-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.
-144
View File
@@ -1,144 +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.
The standard set of generic types are:
{python title="package sqlalchemy.types"}
class String(TypeEngine):
def __init__(self, length=None)
class Integer(TypeEngine)
class SmallInteger(Integer)
class Numeric(TypeEngine):
def __init__(self, precision=10, length=2)
class Float(Numeric):
def __init__(self, precision=10)
# DateTime, Date and Time types deal with datetime objects from the Python datetime module
class DateTime(TypeEngine)
class Date(TypeEngine)
class Time(TypeEngine)
class Binary(TypeEngine):
def __init__(self, length=None)
class Boolean(TypeEngine)
# converts unicode strings to raw bytes
# as bind params, raw bytes to unicode as
# rowset values, using the unicode encoding
# setting on the engine (defaults to 'utf-8')
class Unicode(TypeDecorator):
impl = String
# uses the pickle protocol to serialize data
# in/out of Binary columns
class PickleType(TypeDecorator):
impl = Binary
More specific subclasses of these types are available, which various database engines may choose to implement specifically, allowing finer grained control over types:
{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)
When using a specific database engine, these types are adapted even further via a set of database-specific subclasses defined by the database engine.
There may eventually be more type objects that are defined for specific databases. An example of this would be Postgres' Array type.
Type objects are specified to table meta data using either the class itself, or an instance of the class. Creating an instance of the class allows you to specify parameters for the type, such as string length, numerical precision, etc.:
{python}
mytable = Table('mytable', engine,
# define type using a class
Column('my_id', Integer, primary_key=True),
# define type using an object instance
Column('value', Number(7,4))
)
### 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, to support either database-specific types, or customized pre-processing of query parameters as well as post-processing of result set data. You can make your own classes to perform these operations. To augment the behavior of a `TypeEngine` type, such as `String`, the `TypeDecorator` class is used:
{python}
import sqlalchemy.types as types
class MyType(types.TypeDecorator):
"""basic type that decorates String, prefixes values with "PREFIX:" on
the way in and strips it off on the way out."""
impl = types.String
def convert_bind_param(self, value, engine):
return "PREFIX:" + value
def convert_result_value(self, value, engine):
return value[7:]
The `PickleType` class is an instance of `TypeDecorator` already and can be subclassed directly.
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 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
-108
View File
@@ -1,108 +0,0 @@
from toc import TOCElement
import docstring
import re
from sqlalchemy import schema, types, ansisql, engine, sql, pool, orm, exceptions, databases, interfaces
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=sql,include_all_classes=True),
make_doc(obj=schema),
make_doc(obj=pool),
make_doc(obj=types),
make_doc(obj=engine),
make_doc(obj=engine.url),
make_doc(obj=engine.strategies),
make_doc(obj=engine.default),
make_doc(obj=engine.threadlocal),
make_doc(obj=ansisql),
make_doc(obj=interfaces),
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]),
make_doc(obj=orm.shard),
make_doc(obj=exceptions),
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
-100
View File
@@ -1,100 +0,0 @@
#!/usr/bin/env python
import sys,re,os,shutil
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'
]
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=sqlalchemy.__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}
-126
View File
@@ -1,126 +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 desc.endswith('()'):
path = "docstrings_" + module + "_modfunc_" + desc[:-2]
else:
path = "docstrings_" + module + "_" + desc
return capture(nav.toclink, toc=toc, path=path, description=desc, extension=extension, paged=paged)
return lambda content: re.sub(r'\[(.+?)#(.+?)?\]', 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>
+71 -71
View File
@@ -1,71 +1,71 @@
import sys
sys.path = ['../../lib', './lib/'] + sys.path
import os
import re
import doctest
import sqlalchemy.util as util
import sqlalchemy.logging as salog
import logging
salog.default_enabled=True
rootlogger = logging.getLogger('sqlalchemy')
rootlogger.setLevel(logging.NOTSET)
class MyStream(object):
def write(self, string):
sys.stdout.write(string)
sys.stdout.flush()
def flush(self):
pass
handler = logging.StreamHandler(MyStream())
handler.setFormatter(logging.Formatter('%(message)s'))
rootlogger.addHandler(handler)
def teststring(s, name, globs=None, verbose=None, report=True,
optionflags=0, extraglobs=None, raise_on_error=False,
parser=doctest.DocTestParser()):
from doctest import DebugRunner, DocTestRunner, master
# Assemble the globals.
if globs is None:
globs = {}
else:
globs = globs.copy()
if extraglobs is not None:
globs.update(extraglobs)
if raise_on_error:
runner = DebugRunner(verbose=verbose, optionflags=optionflags)
else:
runner = DocTestRunner(verbose=verbose, optionflags=optionflags)
test = parser.get_doctest(s, globs, name, name, 0)
runner.run(test)
if report:
runner.summarize()
if master is None:
master = runner
else:
master.merge(runner)
return runner.failures, runner.tries
def replace_file(s, newfile):
engine = r"'(sqlite|postgres|mysql):///.*'"
engine = re.compile(engine, re.MULTILINE)
s, n = re.subn(engine, "'sqlite:///" + newfile + "'", s)
if not n:
raise ValueError("Couldn't find suitable create_engine call to replace '%s' in it" % oldfile)
return s
for filename in ('ormtutorial', 'sqlexpression'):
filename = 'content/%s.txt' % filename
s = open(filename).read()
#s = replace_file(s, ':memory:')
s = re.sub(r'{(?:stop|sql|opensql)}', '', s)
teststring(s, filename)
import sys
sys.path = ['../../lib', './lib/'] + sys.path
import os
import re
import doctest
import sqlalchemy.util as util
import sqlalchemy.log as salog
import logging
salog.default_enabled=True
rootlogger = logging.getLogger('sqlalchemy')
rootlogger.setLevel(logging.NOTSET)
class MyStream(object):
def write(self, string):
sys.stdout.write(string)
sys.stdout.flush()
def flush(self):
pass
handler = logging.StreamHandler(MyStream())
handler.setFormatter(logging.Formatter('%(message)s'))
rootlogger.addHandler(handler)
def teststring(s, name, globs=None, verbose=None, report=True,
optionflags=0, extraglobs=None, raise_on_error=False,
parser=doctest.DocTestParser()):
from doctest import DebugRunner, DocTestRunner, master
# Assemble the globals.
if globs is None:
globs = {}
else:
globs = globs.copy()
if extraglobs is not None:
globs.update(extraglobs)
if raise_on_error:
runner = DebugRunner(verbose=verbose, optionflags=optionflags)
else:
runner = DocTestRunner(verbose=verbose, optionflags=optionflags)
test = parser.get_doctest(s, globs, name, name, 0)
runner.run(test)
if report:
runner.summarize()
if master is None:
master = runner
else:
master.merge(runner)
return runner.failures, runner.tries
def replace_file(s, newfile):
engine = r"'(sqlite|postgresql|mysql):///.*'"
engine = re.compile(engine, re.MULTILINE)
s, n = re.subn(engine, "'sqlite:///" + newfile + "'", s)
if not n:
raise ValueError("Couldn't find suitable create_engine call to replace '%s' in it" % oldfile)
return s
for filename in ('ormtutorial', 'sqlexpression'):
filename = '%s.rst' % filename
s = open(filename).read()
#s = replace_file(s, ':memory:')
s = re.sub(r'{(?:stop|sql|opensql)}', '', s)
teststring(s, filename)
+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;
}

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