Compare commits

...
416 Commits
Author SHA1 Message Date
Ville Skyttä a5ba003858 Fix some broken links in docs
(cherry picked from commit b1c9b3bd38)
2016-04-06 10:00:15 -04:00
Mike Bayer d636f6c167 - update oracle JDBC driver URL, fixes #3554
(cherry picked from commit 24dba714cb)
2016-01-20 15:59:27 -05:00
Peter Demin 1ac5521029 Update base.py
Docstring typo keysowrds => keywords
(cherry picked from commit f8eb7c2858)
2015-10-08 00:33:59 -04:00
Mike Bayer e66297ea37 - remove ambiguous use of the phrase "joined together by AND" as this
may be construed as the Python "and" keyword
- add notes to ORM tutorial for beginners that Python "and" keyword
is not to be used
fixes #3545

(cherry picked from commit ac08920284)
2015-10-01 09:39:21 -04:00
Jason Myers 31c0f3a474 Correcting the Hybrid Property Example
Fixes #3528

Signed-off-by: Jason Myers <jason@jasonamyers.com>

Conflicts:
	lib/sqlalchemy/ext/hybrid.py
2015-09-10 09:37:39 -04:00
halfcrazy 4ae3de7214 fix typo int->into
(cherry picked from commit 51870ddaef)
2015-08-09 10:23:50 -04:00
Mike Bayer ce0aff9eb5 fdb drivers seem to be no longer available
(cherry picked from commit 422fca43f8)
2015-03-24 19:30:34 -04:00
Mike Bayer 8496aee4a3 - fix typo, fixes #3325
(cherry picked from commit 138293c246)
2015-03-17 17:32:38 -04:00
Mike Bayer 3c3dc2bce9 - remove now-misleading comment that SQLite doesn't support MATCH,
since they've apparently added something for it
2015-03-14 10:17:43 -04:00
Mike Bayer 2c893b520a - call this 1.0.0b1
(cherry picked from commit 64b7a50cbd)
(cherry picked from commit 601e16daf3)
2015-03-13 16:25:44 -04:00
Mike Bayer d5971b4430 - fix quantize recipe, fixes #3322 2015-03-12 11:49:35 -04:00
Mike Bayer 69618c0d56 - backport the latest version of scrolling 2014-12-30 10:35:40 -05:00
Mike Bayer a2fc048775 - merge the safari-related changes to init.js from zzzeeksphinx 2014-12-28 20:16:09 -05:00
Priit Laes e83d2f6fa6 Maul the evaulate & friends typo
this is only a small part of the cherry-pick as the docs
have been reorganized since 0.8

(cherry picked from commit 8ae47dc6e0)

Conflicts:
	doc/build/changelog/changelog_10.rst
	doc/build/changelog/migration_10.rst
	lib/sqlalchemy/ext/declarative/base.py

(cherry picked from commit 6e7701d534)

Conflicts:
	doc/build/changelog/changelog_09.rst
	lib/sqlalchemy/dialects/postgresql/json.py
	lib/sqlalchemy/orm/query.py
	lib/sqlalchemy/sql/dml.py
	lib/sqlalchemy/sql/elements.py
	lib/sqlalchemy/sql/operators.py
2014-12-19 13:57:55 -05:00
mozillazg 8aafe64b30 Fixed typo
Conflicts:
	lib/sqlalchemy/sql/schema.py
2014-10-15 09:57:38 -04:00
Jim Hokanson 8db30d7a2b Updated documenation for engines.rst
Clarified connecting to absolute path for sqlite using Windows.
2014-09-14 09:04:23 -04:00
Mike Bayer 6434b6b9ac - for whatever reason, Insert.values() with multi values wasn't
in the 0.8 migration, so let's just add that
2014-09-09 19:00:34 -04:00
Mike Bayer 1f93a505b8 - mako_layout isn't used, remove it
- to check for epub look at "builder"
2014-09-07 18:11:03 -04:00
hiaselhans 0264ab6988 Docs: use the base layout when mako_layout=='epub'
- https://readthedocs.org/projects/sqlalchemy/downloads/epub/latest/ renders with the full template which is unreadable on e-readers
  - in the makefile the template-variable mako_layout is set for target: epub which makes mako use only the base layout.
  - this is the more elegant solution as proposed to #133
2014-09-07 17:39:42 -04:00
Mike Bayer 57dc058146 - clarify docs that contains_eager() is included in the of_type() system,
fix #2438
2014-08-07 10:01:23 -04:00
Michael White 89d21f64c6 fix typo in cascade documentation 2014-08-06 22:14:19 -04:00
Mike Bayer 807680a9ec 0.8.7 2014-07-22 16:56:14 -04:00
Mike Bayer 367341c95f - backport 0.9-related updates 2014-07-22 16:53:31 -04:00
Mike Bayer 40fe737f4c - ticket inline 2014-07-22 16:26:16 -04:00
Mike Bayer a7ed17e714 - backport #3093 to 0.8, fixes #3093 2014-07-16 13:42:23 -04:00
Mike Bayer f024ce3b29 - need Integer here as #1765 is in 0.9 2014-07-16 10:52:35 -04:00
Mike Bayer 74d38623ff - Added statement encoding to the "SET IDENTITY_INSERT"
statements which operate when an explicit INSERT is being
interjected into an IDENTITY column, to support non-ascii table
identifiers on drivers such as pyodbc + unix + py2k that don't
support unicode statements.
ref #3091 as this fix is also in that issue's patch, but is
a different issue.
2014-07-14 19:03:59 -04:00
Mike Bayer a81d3a23c0 - In the SQL Server pyodbc dialect, repaired the implementation
for the ``description_encoding`` dialect parameter, which when
not explicitly set was preventing  cursor.description from
being parsed correctly in the case of result sets that
contained names in alternate encodings.  This parameter
shouldn't be needed going forward.
fixes #3091

Conflicts:
	test/sql/test_unicode.py
2014-07-14 18:57:25 -04:00
Mike Bayer 8be54fe1de - add some order bys 2014-07-14 16:31:39 -04:00
Mike Bayer 703cda62d7 - adjust this to work on PG also 2014-07-13 23:59:32 -04:00
Mike Bayer 53b72c35f4 - Fixed bug in :class:.Enum and other :class:.SchemaType
subclasses where direct association of the type with a
:class:`.MetaData` would lead to a hang when events
(like create events) were emitted on the :class:`.MetaData`.
fixes #3124

Conflicts:
	lib/sqlalchemy/sql/sqltypes.py
	test/sql/test_types.py
2014-07-13 19:01:15 -04:00
Mike Bayer a96267639a - Fixed a bug within the custom operator plus :meth:.TypeEngine.with_variant
system, whereby using a :class:`.TypeDecorator` in conjunction with
variant would fail with an MRO error when a comparison operator was used.
fixes #3102

Conflicts:
	lib/sqlalchemy/sql/type_api.py
2014-06-27 16:11:56 -04:00
Mike Bayer 50cb349294 wrong version number 2014-06-27 16:07:44 -04:00
Mike Bayer 034912c1d8 - MySQL error 2014 "commands out of sync" appears to be raised as a
ProgrammingError, not OperationalError, in modern MySQL-Python versions;
all MySQL error codes that are tested for "is disconnect" are now
checked within OperationalError and ProgrammingError regardless.
fixes #3101
2014-06-27 15:53:05 -04:00
Mike Bayer 7b5d45988a additional version notes 2014-06-20 18:06:35 -04:00
Mike Bayer 6f9a8034ca - Fixed bug where column names added to `mysql_length` parameter
on an index needed to have the same quoting for quoted names in
order to be recognized.  The fix makes the quotes optional but
also provides the old behavior for backwards compatibility with those
using the workaround.
fixes #3085
2014-06-18 10:56:57 -04:00
Mike Bayer 01eb52b516 - The `__mapper_args__` dictionary is copied from a declarative
mixin or abstract class when accessed, so that modifications made
to this dictionary by declarative itself won't conflict with that
of other mappings.  The dictionary is modified regarding the
``version_id_col`` and ``polymorphic_on`` arguments, replacing the
column within with the one that is officially mapped to the local
class/table. fixes #3062
2014-05-30 15:36:47 -04:00
Mike Bayer 7475cfd443 relative here 2014-05-29 18:52:33 -04:00
Mike Bayer 8f29b1fa49 - all new approach which allows the main text to be on the top 2014-05-29 16:51:41 -04:00
Mike Bayer 24c8a242dd - disable the scrolling layout, anchors for code/params are not working,
and the current approach used for sections is not able to work for fine-grained
anchors like these, another approach needs to be taken.
2014-05-29 12:29:08 -04:00
Mike Bayer b832bf7e67 - a rework of doc layout and nav:
- reduce the real estate for the top nav; move the search box
    into the top yellow box, close in space
  - use a new CSS/js approach such that the top toolbar freezes from
    scrolling at the top of the page.  The left nav scrolls independently
    of the content pane so that the local contents remain visible regardless
    of scrolling.   this approach is disabled on mobile where the dual scrollbars
    may be cumbersome (overall the site is not well designed for mobile).
2014-05-28 11:01:56 -04:00
Mike Bayer 83b2e9e8d2 - Fixed bug in INSERT..FROM SELECT construct where selecting from a
UNION would wrap the union in an anonymous (e.g. unlabled) subquery.
fixes #3044
2014-05-25 14:10:34 -04:00
Mike Bayer 21cb06d1c0 - Added the `hashable=False flag to the PG :class:.HSTORE` type, which
is needed to allow the ORM to skip over trying to "hash" an ORM-mapped
HSTORE column when requesting it in a mixed column/entity list.
Patch courtesy Gunnlaugur Þór Briem.  Fixes #3053
2014-05-25 13:58:36 -04:00
Mike Bayer 5cf8dbeb5e - Fixed bug in subquery eager loading where a long chain of
eager loads across a polymorphic-subclass boundary in conjunction
with polymorphic loading would fail to locate the subclass-link in the
chain, erroring out with a missing property name on an
:class:`.AliasedClass`. fixes #3055
- adjust the test from 1.0/0.9 to not use chained eager load style
2014-05-25 13:47:56 -04:00
Mike Bayer 96b24ec545 - hyperlink all the column operators listed in the ORM tutorial common filter operators section
- add language to MATCH explicitly stating this operator varies by backend and is not available
on SQLite, as the tutorial defaults to SQLite to start with, fix #3059
- on the actual match() documentation fix this up to be more accurate, list some example renderings
for different backends.  again mention SQLite not supported
2014-05-24 09:56:14 -04:00
Mike Bayer 480d395c44 typo 2014-05-16 13:00:01 -04:00
Mike Bayer 47e90e266e - add lots more to the profiling section
- add the query profiling recipe, fix it with a stack as we now nest those
calls occasionally
- tabs to spaces

Conflicts:
	doc/build/faq.rst
2014-05-16 11:54:51 -04:00
Gunnlaugur Þór Briem c0c4cf78a9 Add note on PostgreSQL config for test runs
Several tests on PostgreSQL depend on English-language text search
config being the default in the test DB. This adds a note about that.

Conflicts:
	README.unittests.rst
2014-05-15 17:11:52 -04:00
Mike Bayer cf1ba8e101 - Fixed bug in mutable extension where :class:.MutableDict did not
report change events for the ``setdefault()`` dictionary operation.
fixes #3051
2014-05-14 10:03:51 -04:00
Mike Bayer 64fbbde940 - add a new FAQ section "performance", put the profiling article there,
move the insert rows article there as well
2014-05-12 17:18:36 -04:00
Wieland Hoffmann 39b2bd0680 Posgtresql -> PostgreSQL 2014-05-12 11:53:13 -04:00
Mike Bayer 7d15b4fb38 changelog for pullreq bitbucket:15 2014-05-12 11:40:29 -04:00
W. Sean McGivern 41a334b3d8 Added optional '=' to MySQL KEY_BLOCK_SIZE regex 2014-05-12 11:40:23 -04:00
Matthias Urlichs 3b936983de Documentation fix-up: "its" vs. "it's"
Removed ungrammatical apostrophes from documentation, replacing
"it's" with "its" where appropriate (but in a few cases with "it is"
when that read better).

While doing that, I also fixed a couple of minor typos etc.
as I noticed them.
2014-05-11 13:08:51 -04:00
Mike Bayer ac68e85e54 - Fixed ORM bug where the :func:.class_mapper function would mask
AttributeErrors or KeyErrors that should raise during mapper
configuration due to user errors.  The catch for attribute/keyerror
has been made more specific to not include the configuration step.
fixes #3047
2014-05-10 23:28:58 -04:00
Mike Bayer 93e11a9d7d - Fixed bug where :meth:.Table.update and :meth:.Table.delete
would produce an empty WHERE clause when an empty :func:`.and_()`
or :func:`.or_()` or other blank expression were applied.  This is
now consistent with that of :func:`.select`.
fixes #3045
2014-05-08 22:45:01 -04:00
Alex Gaynor 43d99ac61b Fix many typos throughout the codebase
cherry pick of those parts of 1caa7fa which went in without issue
Found using: https://github.com/intgr/topy
2014-04-26 16:26:56 -04:00
Mike Bayer 517e22e476 - add a note about versioning only applying to flushes 2014-04-25 12:31:23 -04:00
Mike Bayer 6b23eb0f54 - document that joinedload/eagerload work with of_type() + with_polymoprhic() 2014-04-23 19:13:24 -04:00
benselme 8093eb546b Added some links in session docs 2014-04-23 12:39:11 -04:00
Mike Bayer 76682cff7d 0.8.7 2014-04-11 15:44:52 -04:00
Mike Bayer 9c59faf74e - Added a new "disconnect" message "connection has been closed unexpectedly".
This appears to be related to newer versions of SSL.
Pull request courtesy Antti Haapala.
2014-04-11 15:43:32 -04:00
antti_haapala 6577df9ef9 added an SSL related connection error 2014-04-11 15:43:23 -04:00
Daniel Smith 280758acc5 Fixed bug in example code. 2014-04-11 14:53:49 -04:00
Mike Bayer 0fd2fce8f8 repair erroneous whitespace in autodoc directives, preventing members from
being documented
2014-04-06 18:15:14 -04:00
Mike Bayer 9f72da7658 - add some docs for the instancestate linkage to the inspection system 2014-04-04 17:34:07 -04:00
Mike Bayer df7fe021a9 - reverse order of columns in sample CTEs as this is a UNION and the cols need to line up
- alter this in the unit tests as well as these queries were just copied from the tests
- remove the included_parts.join(parts) from the core CTE doc (also just copied from the
test, where we want to make sure joins don't get screwed up with the CTE) as it doesn't
contribute to the query itself
fixes #3014
2014-04-02 18:13:52 -04:00
Mike Bayer 6bb74b6ede 0.8.6 2014-03-28 19:40:21 -04:00
Mike Bayer 3bb9de09aa this doesnt need sane multi row now... 2014-03-28 19:17:04 -04:00
Mike Bayer 7473b54574 - revert part of c01558ae7f for now as we also test that a DELETE of two rows
where one is to be deleted from ON DELETE CASCADE succeeds; the check here makes that fail.
We will need to add an option to enable/disable this check per mapping, will likely
do this in next version
2014-03-28 17:44:23 -04:00
Mike Bayer 91de02cc66 revert inadvertent pdb 2014-03-28 16:48:05 -04:00
Mike Bayer b4eeebbabe - the rewording of orderinglist docs got committed by accident, finish it up 2014-03-28 16:47:38 -04:00
Mike Bayer e789a75f9a - Fixed ORM bug where changing the primary key of an object, then marking
it for DELETE would fail to target the correct row for DELETE.
Then to compound matters, basic "number of rows matched" checks were
not being performed.  Both issues are fixed, however note that the
"rows matched" check requires so-called "sane multi-row count"
functionality; the DBAPI's executemany() method must count up the
rows matched by individual statements and SQLAlchemy's dialect must
mark this feature as supported, currently applies to some mysql dialects,
psycopg2, sqlite only. fixes #3006
- Enabled "sane multi-row count" checking for the psycopg2 DBAPI, as
this seems to be supported as of psycopg2 2.0.9.

Conflicts:
	test/orm/test_naturalpks.py
	test/requirements.py
2014-03-28 16:34:54 -04:00
Stefan Reich eb94838c72 Added missing text_type requirement to TextTest 2014-03-26 16:04:23 +01:00
Mike Bayer f6614aed66 - Fixed regression caused by release 0.8.5 / 0.9.3's compatibility
enhancements where index reflection on Postgresql versions specific
to only the 8.1, 8.2 series again
broke, surrounding the ever problematic int2vector type.  While
int2vector supports array operations as of 8.1, apparently it only
supports CAST to a varchar as of 8.3.
fix #3000
2014-03-25 12:27:03 -04:00
Mike Bayer d8aa3d91d7 - Fixed regression from 0.8.3 as a result of 🎫2818
where :meth:`.Query.exists` wouldn't work on a query that only
had a :meth:`.Query.select_from` entry but no other entities.
re: #2818 fixes #2995
2014-03-22 19:55:27 -04:00
Mike Bayer 0dddcf924e - Adjusted `setup.py` file to support the possible future
removal of the ``setuptools.Feature`` extension from setuptools.
If this keyword isn't present, the setup will still succeed
with setuptools rather than falling back to distutils.  C extension
building can be disabled now also by setting the
DISABLE_SQLALCHEMY_CEXT environment variable.  This variable works
whether or not setuptools is even available. fixes #2986
- using platform.python_implementation() in setup.py to detect CPython.
I've tested this function on OSX and linux on Python 2.6 through 3.4,
including 3.1, 3.2, 3.3.
Unfortunately, on OSX + 3.2 only, it seems to segfault.  I've tried
installing 3.2.5 from the python.org .dmg, building it from source,
and also blew away the whole 3.2 directory, something seems to be wrong
with the "platform" module on that platform only, and there's also no
issue on bugs.python.org; however, I'm going with
it anyway. If someone is using 3.2 on OSX they really should be upgrading.
- adjusted the logic for platform_implementation(), apparently "platform"
is there in python 2.5, so we are doing a version check.
Conflicts:
	doc/build/intro.rst
	setup.py
2014-03-22 17:47:07 -04:00
Mike Bayer cb51fe3ef8 - reword the paragraph which talks about web framework integration for sessions:
1. fix the typo in the paragraph, fixes #2998
2. as zope-sqlalchemy only provides transaction integration and not session scoping,
dial back the language here as people are probably using scoped_session with pyramid anyway
3. as I'm going to again start recommending people don't cling to flask-sqlalchemy so hard,
take out the word "strongly" from the recommendation.
4. as flask is the only framework I can think of that actually has an explicit SQLAlchemy
layer that handles setting up scoped_session, take out the word "most", now it's "some web frameworks"
(by which it means "only flask...and flask-sqlalchemy is probably not worth using anyway")
2014-03-20 22:51:57 -04:00
Mike Bayer 5f69d83d6c fix assertion 2014-03-19 11:48:25 -04:00
Mike Bayer 66d090be9c - Fixed bug in mutable extension as well as
:func:`.attributes.flag_modified` where the change event would not be
propagated if the attribute had been reassigned to itself.
fixes #2997

Conflicts:
	lib/sqlalchemy/orm/state.py
	test/orm/test_attributes.py
2014-03-19 11:12:24 -04:00
Charles-Axel Dein c5005f7e4b Add is_ and isnot filter to the tutorial
Most linter complain when comparing with None.
2014-03-17 17:28:05 -04:00
Mike Bayer 987759aec5 - Improved an error message which would occur if a query() were made
against a non-selectable, such as a :func:`.literal_column`, and then
an attempt was made to use :meth:`.Query.join` such that the "left"
side would be determined as ``None`` and then fail.  This condition
is now detected explicitly.
2014-03-17 16:26:09 -04:00
Mike Bayer ce3505ea8b typo 2014-03-16 14:51:47 -04:00
Mike Bayer 149584d322 - fairly epic rework of the cascade documentation
Conflicts:
	lib/sqlalchemy/orm/relationships.py
2014-03-15 15:48:52 -04:00
mike bayer 14b39c8dd9 Merge pull request #79 from nibrahim/master
Fixes argument number in docs
2014-03-11 11:38:50 -04:00
Mike Bayer cd3c5dd45e merge new links to rel_0_8 2014-03-09 19:14:21 -04:00
Mike Bayer 14c74a8fb5 cut out the BS as far as MySQLdb urls, put the one url everyone should be using 2014-03-04 12:51:51 -05:00
Mike Bayer 3ba1385520 - Fixed bug in :func:.tuple_ construct where the "type" of essentially
the first SQL expression would be applied as the "comparison type"
to a compared tuple value; this has the effect in some cases of an
inappropriate "type coersion" occurring, such as when a tuple that
has a mix of String and Binary values improperly coerces target
values to Binary even though that's not what they are on the left
side.  :func:`.tuple_` now expects heterogeneous types within its
list of values.
fixes #2977

Conflicts:
	lib/sqlalchemy/sql/elements.py
	test/sql/test_operators.py
2014-02-27 19:57:20 -05:00
Mike Bayer 3dca38608b 0.8.6 2014-02-26 19:27:57 -05:00
Mike Bayer a3ad92de4b - Removed stale names from `sqlalchemy.orm.interfaces.__all__` and
refreshed with current names, so that an ``import *`` from this
module again works.
fixes #2975
2014-02-26 19:27:47 -05:00
Mike Bayer 1366ef9f1c 0.8.5 2014-02-19 22:59:11 -05:00
Mike Bayer 39c0485a21 2014 2014-02-19 22:56:06 -05:00
Mike Bayer 0c3fdb808a - isolate this failure as only on 0.8, only in py3.3, just comment it out,
not really worth tracking down
2014-02-19 20:33:31 -05:00
Mike Bayer cd0d29cef4 - Added an additional message to psycopg2 disconnect detection,
"could not send data to server", which complements the existing
"could not receive data from server" and has been observed by users,
fixes #2936
2014-02-19 19:22:48 -05:00
Mike Bayer bfdb51ac6a - add a topological rule here to place PARTITIONS after PARTITION_BY,
for output consistency within the tests as well as in practice
2014-02-19 17:01:26 -05:00
Mike Bayer f9059f6cd8 restore check ahead of the lock to avoid locking when not needed 2014-02-19 16:12:36 -05:00
Mike Bayer 0d81c6c7a3 - add improved support here for string parsing of predicates, backport
from 0.9
2014-02-19 16:07:01 -05:00
Mike Bayer 4f29ebc728 - Support has been improved for Postgresql reflection behavior on very old
(pre 8.1) versions of Postgresql, and potentially other PG engines
such as Redshift (assuming Redshift reports the version as < 8.1).
The query for "indexes" as well as "primary keys" relies upon inspecting
a so-called "int2vector" datatype, which refuses to coerce to an array
prior to 8.1 causing failures regarding the "ANY()" operator used
in the query.  Extensive googling has located the very hacky, but
recommended-by-PG-core-developer query to use when PG version < 8.1
is in use, so index and primary key constraint reflection now work
on these versions.

Conflicts:
	doc/build/changelog/changelog_09.rst
	test/dialect/postgresql/test_types.py
2014-02-19 16:02:29 -05:00
Mike Bayer dd6884eafa - Added new MySQL-specific :class:.mysql.DATETIME which includes
fractional seconds support; also added fractional seconds support
to :class:`.mysql.TIMESTAMP`.  DBAPI support is limited, though
fractional seconds are known to be supported by MySQL Connector/Python.
Patch courtesy Geert JM Vanderkelen. #2941
2014-02-19 15:49:59 -05:00
Mike Bayer 56364c636e changelog for pullreq 12 2014-02-19 15:27:31 -05:00
Marcus McCurdy 17d7e98b5e Fixes MySQL dialect partitioning 2014-02-19 15:27:24 -05:00
Mike Bayer 506817a84e - Fixed bug where calling :meth:.Insert.values with an empty list
or tuple would raise an IndexError.   It now produces an empty
insert construct as would be the case with an empty dictionary.

Conflicts:
	lib/sqlalchemy/sql/dml.py
2014-02-19 15:23:04 -05:00
Mike Bayer 4ac0558e11 - Fixed a critical regression caused by 🎫2880 where the newly
concurrent ability to return connections from the pool means that the
"first_connect" event is now no longer synchronized either, thus leading
to dialect mis-configurations under even minimal concurrency situations.

Conflicts:
	lib/sqlalchemy/event/attr.py
2014-02-19 10:51:57 -05:00
Mike Bayer df288504d9 - update mysql connector python link, #2938 2014-02-18 20:50:06 -05:00
Mike Bayer beb567ba6f - changelog for pullreq github:72. 2014-02-16 18:28:35 -05:00
Mike Bayer 28ec4fdfc8 Merge branch 'fix_sqlite_uc_reflection' of https://github.com/malor/sqlalchemy into t 2014-02-16 18:19:55 -05:00
Mike Bayer dd6362aed7 - extensive cross-linking of relationship options with their documentation sections
- convert all paramter references in relationship documentation to :paramref:

Conflicts:
	doc/build/orm/relationships.rst
	lib/sqlalchemy/ext/declarative/__init__.py
	lib/sqlalchemy/orm/relationships.py
2014-02-16 14:41:53 -05:00
Mike Bayer 0dc4beaa08 - add cross-linking for passive_deletes / passive_updates 2014-02-16 12:47:19 -05:00
Mike Bayer 929ff92598 - add documentation regarding native hstore flag, psycopg2
hstore extension. #2959
2014-02-16 12:03:53 -05:00
Mike Bayer 1a7b5d55c9 - expand docs on MySQL table arguments beyond just storage engines
- clarify section on "foreign key reflection" and group this in a
section that refers to foreign keys.
2014-02-15 20:52:33 -05:00
Mike Bayer 30febd7c19 - Revised this very old issue where the Postgresql "get primary key"
reflection query were updated to take into account primary key constraints
that were renamed; the newer query fails on very old versions of
Postgresql such as version 7, so the old query is restored in those cases
when server_version_info < (8, 0) is detected. #2291
2014-02-14 14:32:30 -05:00
Roman Podoliaka a7e0d3e1e3 Fix unique constraints reflection in SQLite
Reflection of unique constraints didn't work properly, if reserved
identifiers had been used as column names. In this case column names
would be put in double quotes (e.g. the name of column asc would be
returned as "asc").

This issue is only present in 0.8.4 and not in 0.9.x.
2014-02-14 14:09:21 +02:00
Mike Bayer b87935ed44 - Fixed bug where :meth:.in_() would go into an endless loop if
erroneously passed a column expression whose comparator included
the ``__getitem__()`` method, such as a column that uses the
:class:`.postgresql.ARRAY` type. [ticket:2957]
2014-02-13 15:21:28 -05:00
Mike Bayer 50d60cfb99 this example doesn't work, we don't really have a solution for this as far as automating this pattern 2014-02-10 17:44:09 -05:00
Mike Bayer 0470c5c5a6 - dont need these extra conditions from the previous test 2014-02-10 16:37:59 -05:00
Mike Bayer 99717570ff - Fixed bug where :meth:.Query.get would fail to consistently
raise the :class:`.InvalidRequestError` that invokes when called
on a query with existing criterion, when the given identity is
already present in the identity map. [ticket:2951]
2014-02-10 16:34:19 -05:00
Mike Bayer b77f958b8b - this seems to be the best string for pymysql 2014-02-03 12:11:44 -05:00
Mike Bayer cd1baf6572 - Fixed bug which prevented MySQLdb-based dialects (e.g.
pymysql) from working in Py3K, where a check for "connection
charset" would fail due to Py3K's more strict value comparison
rules.  The call in question  wasn't taking the database
version into account in any case as the server version was
still None at that point, so the method overall has been
simplified to rely upon connection.character_set_name().
[ticket:2933]
2014-02-03 12:06:57 -05:00
Mike Bayer 2ac2bbd350 - add a few missing methods to the cymysql dialect 2014-02-02 18:21:09 -05:00
Mike Bayer 5f144b2e43 - don't duplicate docs for Pool within QueuePool
- add huge warning regarding how use_threadlocal probably not what you want
2014-01-31 23:16:34 -05:00
Mike Bayer 1cf18e5cbb changelog 2014-01-31 17:31:20 -05:00
Mike Bayer e7f906f5e6 no Binary here if we are running a test with no DBAPI 2014-01-31 17:29:19 -05:00
Mike Bayer 95c370cc65 Merge branch 'fix-convenience-import' of github.com:witsch/sqlalchemy into t 2014-01-31 17:27:55 -05:00
Mike Bayer 5283b0cb31 - add a test for pullreq github:63 2014-01-31 17:27:35 -05:00
Andreas Zeidler a1f8479215 expose binary_type in util module
it is used in `sql/compiler.py(849)render_literal_value()`
2014-01-31 17:33:44 +01:00
Mike Bayer 66fe0880ba - add a test for [ticket:2927], which is an 0.9 issue but only because 0.8
isn't annotating correctly
2014-01-29 17:09:50 -05:00
Mike Bayer b05adc9507 - repair the fixture/test here to make sure state isn't left over causing other tests to fail 2014-01-28 19:49:15 -05:00
Mike Bayer da4256d3dc docs 2014-01-26 20:01:57 -05:00
Mike Bayer 4a7acd40ce updates 2014-01-26 01:04:46 -05:00
Mike Bayer 7680ba30eb tweak text 2014-01-25 16:55:27 -05:00
Mike Bayer a52b2da3d0 seealsos in the tutorial
Conflicts:
	lib/sqlalchemy/sql/selectable.py
2014-01-25 16:38:48 -05:00
Mike Bayer 81a6e04f8a imports 2014-01-25 16:37:49 -05:00
Mike Bayer 271d7cd9b3 case 2014-01-25 16:20:54 -05:00
Mike Bayer 267fe8bc5d - start building out very comprehensive docstrings for core functions 2014-01-25 15:49:53 -05:00
Mike Bayer 91eec74a6c docuemnt join 2014-01-24 22:51:31 -05:00
Mike Bayer 802b9adc41 - doc updates, include links to create_engine from tutorials, cleanup
and modernize the engines chapter a bit

Conflicts:
	doc/build/changelog/changelog_09.rst
	doc/build/orm/tutorial.rst
2014-01-23 19:40:17 -05:00
Mike Bayer 2866577231 - add tests for [ticket:2918], confirm this is an 0.9 regression 2014-01-23 14:30:34 -05:00
Wichert Akkerman 28fd486bd4 Remove uneeded import from code example
This had me reread the code twice to see if I missed why the import was
present.
2014-01-23 12:40:18 -05:00
Mike Bayer 21e012121a fix typo 2014-01-19 17:51:51 -05:00
Wichert Akkerman df06bd1803 Fix a typo in the relationship docs
The text refers to ``boston_addresses``, while the code sample uses
``addresses``.
2014-01-19 17:51:42 -05:00
Mike Bayer 3509ea9685 attic 2014-01-18 20:58:46 -05:00
Mike Bayer 070c745a86 akiban-> foundationdb
Conflicts:
	doc/build/dialects/index.rst
2014-01-18 20:58:10 -05:00
Mike Bayer aace3b0e96 - changelog + test for pullreq github 58 2014-01-14 20:24:28 -05:00
Kyle Stark 26b899e9ac Fix TypeError for class_mapper called w/ iterable
When the class_ passed is not a mapped class but is actually an iterable, the string formatting operation fails with a TypeError, and the expected ArgumentError is not raised. Calling code which is using reflection and expects this error will fail (e.g. the sadisplay module).
Conflicts:
	lib/sqlalchemy/orm/base.py
2014-01-14 20:24:04 -05:00
Mike Bayer a78110a21c pyo 2014-01-11 13:15:23 -05:00
Mike Bayer 6e4e1a8369 - add paramlinks and external changelog ext to 0.8 2014-01-11 13:14:49 -05:00
Mike Bayer cc82ea33a0 - support addition of fails_if()/only_on(), just wraps the decorators
- update a few exclusions to support current pymssql.  passes all of test_suite and dialect/mssql
2014-01-02 14:24:44 -05:00
Mike Bayer a1d2e4d491 - apply a similar fix for floats to mssql+pyodbc as we did to firebird
- wrangle through osx+pyodbc+freetds to get at least test_suite to pass again
with mssql+pyodbc. invovled adding some silly requirements
2013-12-28 17:51:51 -05:00
Mike Bayer a6905c8252 - fix the insert from select test to use a non-autoinc table 2013-12-28 17:51:42 -05:00
Ben Alpert a48a0f6a44 session docs: Change object to someobject
This makes the code block more consistent with the preceding one and also prevents the variable from being colored as a builtin (which `object` is) during syntax highlighting.
2013-12-28 01:56:34 -05:00
Mike Bayer db0bfa0676 - actually check the list of views! 2013-12-27 16:38:27 -05:00
Mike Bayer 1ed633c229 fix 2.5-ism 2013-12-27 16:19:54 -05:00
Mike Bayer 4a4a4beffb test hanging on 0.8, suspecting the test framework. this feature is tested on 0.9 successfully 2013-12-27 13:47:31 -05:00
Mike Bayer 819ad89276 - add an exclusion for "floats to four decimals", backported from 0.9 and applies to
firebird
2013-12-27 13:45:07 -05:00
Mike Bayer c24e825c37 - The firebird dialect will quote identifiers which begin with an
underscore.  Courtesy Treeve Jelbert. [ticket:2897]
2013-12-27 13:42:43 -05:00
Mike Bayer ab1ab82f16 - add a test which creates tables and views at the same time, then tests that the lists
of each can be reflected independently.  Testing [ticket:2898] at the moment.
2013-12-27 13:42:36 -05:00
Mike Bayer 320bc4861c - Fixed bug in Firebird index reflection where the columns within the
index were not sorted correctly; they are now sorted
in order of RDB$FIELD_POSITION.
2013-12-27 13:42:28 -05:00
Mike Bayer b5b84ae20d - The "asdecimal" flag used with the :class:.Float type will now
work with Firebird dialects; previously the decimal conversion was
not occurring.
- scale back some firebird FP numeric tests

Conflicts:
	test/requirements.py
2013-12-27 13:41:46 -05:00
Mike Bayer 2900b6e4ec - repair some suite tests for firebird 2013-12-27 13:41:21 -05:00
Mike Bayer 351d0b6f21 changelog, will merge to 0.8 2013-12-27 11:14:31 -05:00
John Anderson 4211207abf Remove terminated connections from the pool.
In pymssql, if you terminate a long running query manually
it will give you a connection reset by peer message, but this
connection remains in the pool and will be re-used.
2013-12-27 11:14:21 -05:00
Mike Bayer ae3c57f492 - Fixed issue where a primary key column that has a Sequence on it,
yet the column is not the "auto increment" column, either because
it has a foreign key constraint or ``autoincrement=False`` set,
would attempt to fire the Sequence on INSERT for backends that don't
support sequences, when presented with an INSERT missing the primary
key value.  This would take place on non-sequence backends like
SQLite, MySQL. [ticket:2896]
2013-12-20 10:26:35 -05:00
Mike Bayer 2042493228 - Fixed bug with :meth:.Insert.from_select method where the order
of the given names would not be taken into account when generating
the INSERT statement, thus producing a mismatch versus the column
names in the given SELECT statement.  Also noted that
:meth:`.Insert.from_select` implies that Python-side insert defaults
cannot be used, since the statement has no VALUES clause. [ticket:2895]
2013-12-19 16:05:15 -05:00
Mike Bayer 3c4a565f2d autoincrement here 2013-12-17 16:29:06 -05:00
Mike Bayer 0fefc6e226 - for [ticket:2651], leaving CheckConstraint alone, preferring to keep
backwards compatibility.  A note about backslashing escapes is added.
Because the Text() construct now supports bind params better, the example
given in the code raises an exception now, so that should cover us.
The exception itself has been enhanced to include the key name of the
bound param.  We're backporting this to 0.8 but 0.8 doesn't have the
text->bind behavior that raises.

Conflicts:
	lib/sqlalchemy/sql/schema.py
2013-12-16 19:34:48 -05:00
Mike Bayer dcb7e7759a - An adjustment to the :func:.subqueryload strategy which ensures that
the query runs after the loading process has begun; this is so that
the subqueryload takes precedence over other loaders that may be
hitting the same attribute due to other eager/noload situations
at the wrong time. [ticket:2887]
2013-12-16 19:22:26 -05:00
Mike Bayer 37ebd3f2f3 - fix up the FAQ regarding the "foo_id" issue
- add session object states to the glossary

Conflicts:
	doc/build/glossary.rst
2013-12-16 18:54:30 -05:00
Mike Bayer e294767148 - Fixed bug when using joined table inheritance from a table to a
select/alias on the base, where the PK columns were also not same
named; the persistence system would fail to copy primary key values
from the base table to the inherited table upon INSERT.
[ticket:2885]
2013-12-16 15:26:19 -05:00
Mike Bayer 329aefb11e wrong method name... 2013-12-16 14:36:30 -05:00
Mike Bayer 296f648e39 load_on_pending is different from enable_relationship_loading and should
not be superseded.  both have a potential use.

Conflicts:
	lib/sqlalchemy/orm/relationships.py
2013-12-15 20:07:59 -05:00
Mike Bayer 9c41c2cdc6 make the error message for [ticket:2889] more accurate, as we support
composites to many-to-ones now also
2013-12-12 13:36:25 -05:00
Mike Bayer 730f585dc6 - :func:.composite will raise an informative error message when the
columns/attribute (names) passed don't resolve to a Column or mapped
attribute (such as an erroneous tuple); previously raised an unbound
local. [ticket:2889]

Conflicts:
	test/orm/test_composites.py
2013-12-12 13:32:57 -05:00
Mike Bayer 69154698d2 - Error message when a string arg sent to :func:.relationship which
doesn't resolve to a class or mapper has been corrected to work
the same way as when a non-string arg is received, which indicates
the name of the relationship which had the configurational error.
[ticket:2888]

Conflicts:
	lib/sqlalchemy/orm/relationships.py
2013-12-12 12:53:34 -05:00
Mike Bayer a6eaf1cceb sqlany dialect moves to github
Conflicts:
	doc/build/dialects/index.rst
2013-12-09 20:57:18 -05:00
Mike Bayer 6fb499599a Merge branch 'rel_0_8' of /Users/classic/dev/sqlalchemy into rel_0_8 2013-12-08 15:01:39 -05:00
Mike Bayer 746a4e8f61 forgot authors!! 2013-12-08 15:00:37 -05:00
Mike Bayer b576359dba 0.8.4 prep 2013-12-08 14:56:30 -05:00
Mike Bayer 157dc421f9 python2.5 fix 2013-12-08 12:54:44 -05:00
Mike Bayer 6817592180 - documentation cleanup in ORM including [ticket:2816]
Conflicts:
	lib/sqlalchemy/orm/mapper.py
2013-12-07 18:03:05 -05:00
Mike Bayer 2d0573970c specify run as module here 2013-12-07 17:37:58 -05:00
Mike Bayer 7e78fb05c7 - remove verbiage about "namespaces" from the dogpile example, [ticket:2862]
- fix broken py2k/py3k isms
2013-12-07 17:36:26 -05:00
Mike Bayer b113202eec - A DBAPI that raises an error on `connect()` which is not a subclass
of dbapi.Error (such as ``TypeError``, ``NotImplementedError``, etc.)
will propagate the exception unchanged.  Previously,
the error handling specific to the ``connect()`` routine would both
inappropriately run the exception through the dialect's
:meth:`.Dialect.is_disconnect` routine as well as wrap it in
a :class:`sqlalchemy.exc.DBAPIError`.  It is now propagated unchanged
in the same way as occurs within the execute process. [ticket:2881]
- add tests for this in test_parseconnect, but also add tests in test_execute
to ensure the execute() behavior as well
2013-12-07 17:24:11 -05:00
Mike Bayer 71a9947410 - restore having the connection closed before decrementing the overflow counter 2013-12-06 23:01:59 -05:00
Mike Bayer 507a33319f - The :class:.QueuePool has been enhanced to not block new connection
attempts when an existing connection attempt is blocking.  Previously,
the production of new connections was serialized within the block
that monitored overflow; the overflow counter is now altered within
it's own critical section outside of the connection process itself.
[ticket:2880]
2013-12-06 19:59:06 -05:00
Mike Bayer 661b1158b4 - Made a slight adjustment to the logic which waits for a pooled
connection to be available, such that for a connection pool
with no timeout specified, it will every half a second break out of
the wait to check for the so-called "abort" flag, which allows the
waiter to break out in case the whole connection pool was dumped;
normally the waiter should break out due to a notify_all() but it's
possible this notify_all() is missed in very slim cases.
This is an extension of logic first introduced in 0.8.0, and the
issue has only been observed occasionally in stress tests.
2013-12-06 16:33:09 -05:00
Mike Bayer 187db36b74 - changelog + test for pullreq #7, MSSQL dialect for DROP INDEX 2013-12-05 18:37:58 -05:00
donkopotamus 2f18f9b2f5 Fix MSSQL dialects visit_drop_index to use the correct DDL 2013-12-05 18:37:50 -05:00
Mike Bayer 2b3073c76b fix for 2.5 2013-12-04 10:51:54 -05:00
Mike Bayer 37a534f764 - move tests for unique constraints to dialect suite, including
requirements etc., just like 0.9
2013-12-03 15:57:08 -05:00
Roman Podolyaka e8458ab96b - backport unique constraints reflection to 0.8.4, thereby
assisting with alembic installations that have upgraded and are dealing with
PG index/unique constraint reflection.

Inspection API already supports reflection of table
indexes information and those also include unique
constraints (at least for PostgreSQL and MySQL).
But it could be actually useful to distinguish between
indexes and plain unique constraints (though both are
implemented in the same way internally in RDBMS).

This change adds a new method to Inspection API - get_unique_constraints()
and implements it for SQLite, PostgreSQL and MySQL dialects.
2013-12-03 14:59:50 -05:00
Mike Bayer f2f54e04a5 - Added ORA-02396 "maximum idle time" error code to list of
"is disconnect" codes with cx_oracle. [ticket:2864]
2013-12-03 13:19:05 -05:00
Mike Bayer f7e3a3a36c - the pronoun removal commit. there was only one instance of a
standalone gendered pronoun with a gender-neutral subject, but also
have replaced all occurences of "his/her", "his or her", etc.  The docs have always
strived to account for both genders in any non-specific singular pronoun,
however recent controversy in the community suggests
that a zero-gendered-pronoun policy is probably best going forward.
2013-11-30 17:34:06 -05:00
Mike Bayer c23b111f30 - reduce verbiage in mutation.py regarding legacy style, place under versionadded
Conflicts:
	lib/sqlalchemy/ext/mutable.py
2013-11-29 16:33:33 -05:00
mike bayer c002b87f22 Merge pull request #47 from yoloseem/patch-2
Fixed a syntax error in example code.
2013-11-29 13:13:29 -08:00
Mike Bayer 8ebcbc0be9 -be more agnostic of quotes here
Conflicts:
	test/engine/test_execute.py
2013-11-23 17:24:20 -05:00
Mike Bayer ad85ab12d6 - Fixed bug where SQL statement would be improperly ASCII-encoded
when a pre-DBAPI :class:`.StatementError` were raised within
:meth:`.Connection.execute`, causing encoding errors for
non-ASCII statements.  The stringification now remains within
Python unicode thus avoiding encoding errors. [ticket:2871]

Conflicts:
	test/engine/test_execute.py
2013-11-22 18:37:35 -05:00
Mike Bayer 9d848680f4 Fixed bug where Oracle `VARCHAR` types given with no length
(e.g. for a ``CAST`` or similar) would incorrectly render ``None CHAR``
or similar. [ticket:2870]
2013-11-22 17:56:57 -05:00
Mike Bayer bad8be3bde - cleanup 2013-11-22 17:52:08 -05:00
Mike Bayer 038f93a49b disable this test for py3k, this is better fixed in 0.9 2013-11-21 17:22:55 -05:00
Mike Bayer 7d6f6972ab py3k fix for non-ascii SQL strings 2013-11-21 13:49:30 -05:00
Mike Bayer 3a3aeb431e - Fixed bug which prevented the `serializer` extension from working
correctly with table or column names that contain non-ASCII
characters. [ticket:2869]

Conflicts:
	lib/sqlalchemy/ext/serializer.py
	lib/sqlalchemy/sql/selectable.py
	lib/sqlalchemy/testing/assertions.py
2013-11-21 13:37:05 -05:00
Mike Bayer 34478fbb0b - add a sanity-check test for repr(), comparing against what we get
in 0.9 for [ticket:2868]
2013-11-21 13:13:40 -05:00
Hyunjun Kim 33a2ebc514 Fixed a syntax error in example code. 2013-11-21 16:36:48 +09:00
Mike Bayer 0f9e364b96 - apply a timeout to all join() calls for test_pool
- use thread.join() for waiters_handled test

Conflicts:
	test/engine/test_pool.py
2013-11-17 14:38:11 -05:00
Mike Bayer e816958f92 sync up some changes for #2604 and #2607 from 0.7.10, 0.8.0b2 2013-11-14 22:12:09 -05:00
Mike Bayer f8fe32d5b4 sync up the 07/08 changelogs from master 2013-11-14 22:02:34 -05:00
Mike Bayer a9fb549d4a add start-line here to avoid RST parse errors 2013-11-14 22:00:04 -05:00
Vraj Mohan db5ea00887 Fix cross references 2013-11-14 21:52:54 -05:00
Vraj Mohan bf4c99664a Add undocumented members to resolve cross references 2013-11-14 21:52:45 -05:00
Vraj Mohan 98251ebe11 Ensure API generation 2013-11-14 21:52:06 -05:00
Vraj Mohan 45db19e9ea Ensure API generation and fix cross references 2013-11-14 21:47:02 -05:00
Vraj Mohan 8ddb4e2959 Ensure API doc for make_url and resolve references 2013-11-14 21:45:03 -05:00
Vraj Mohan 92490e0c5d Suppress warnings about files not being included in any toctree 2013-11-14 21:45:03 -05:00
Vraj Mohan 97e8ac88ff Fix indentation and escape *args and **kwargs 2013-11-14 21:45:02 -05:00
Vraj Mohan 93cb0436e6 Fix indentation and escape *args and **kwargs 2013-11-12 23:50:55 -05:00
Mike Bayer db71e192bd add sap sqlanywhere 2013-11-12 13:01:16 -05:00
Mike Bayer de35f51b72 css updates to better work with RTD badge 2013-11-11 20:37:05 -05:00
Mike Bayer 1f5236cae1 - add a section re: using column_reflect for mapping, link to it from the "naming columns distinctly" and "column_prefix" sections since this is often what ppl are looking for. [ticket:2856] is related. 2013-10-31 15:30:47 -04:00
Mike Bayer 2576b5cdfb - Fixed a regression introduced by 🎫2818 where the EXISTS
query being generated would produce a "columns being replaced"
warning for a statement with two same-named columns,
as the internal SELECT wouldn't have use_labels set.
2013-10-30 10:34:57 -04:00
Mike Bayer ac73cb304a merge the same fix from master 2013-10-28 20:55:41 -04:00
Mike Bayer 264b650d62 no such thing as default_strategy()... 2013-10-28 13:54:24 -04:00
Mike Bayer 06985a7346 fix to the changelog system for when there's no change recs in the body 2013-10-27 18:26:30 -04:00
Mike Bayer eb58107417 - Fixed bug where index reflection would mis-interpret indkey values
when using the pypostgresql adapter, which returns these values
as lists vs. psycopg2's return type of string.
[ticket:2855]

Conflicts:
	doc/build/changelog/changelog_09.rst
	lib/sqlalchemy/__init__.py
2013-10-27 18:16:42 -04:00
Mike Bayer 64e32bda52 - farm out everthing to do with rtd out to the sqlalchemy.org site 2013-10-27 16:13:52 -04:00
Mike Bayer abb4e5314a 0.8.3 release 2013-10-26 16:38:26 -04:00
Mike Bayer f1905b619d - Fixed bug where Oracle table reflection using synonyms would fail
if the synonym and the table were in different remote schemas.
Patch to fix courtesy Kyle Derr. [ticket:2853]
2013-10-25 19:12:17 -04:00
Mike Bayer 785e44c48b - The regexp used by the :func:.url.make_url function now parses
ipv6 addresses, e.g. surrounded by brackets. [ticket:2851]
2013-10-23 15:02:56 -04:00
Mike Bayer 622772b469 - Fixed bug where :func:.type_coerce would not interpret ORM
elements with a ``__clause_element__()`` method properly.
[ticket:2849]

Conflicts:
	lib/sqlalchemy/sql/elements.py
2013-10-21 15:08:04 -04:00
Mike Bayer d0c2b9593a fix python 2.5 ism 2013-10-20 20:15:22 -04:00
Mike Bayer 45ad8af3f9 - add a type_coerce() step within Enum, Boolean to the CHECK constraint,
so that the custom type isn't exposed to an operation that is against the
"impl" type's constraint, [ticket:2842]
- this change showed up as some recursion overflow in pickling with labels,
add a __reduce__() there....pickling of expressions is less and less something
that's very viable...

Conflicts:
	lib/sqlalchemy/sql/elements.py
	lib/sqlalchemy/sql/sqltypes.py
2013-10-20 16:31:45 -04:00
Mike Bayer bb6df3f45f - attempt to clarify what mutable extension does for HSTORE, [ticket:2803] 2013-10-18 19:35:22 -04:00
Mike Bayer 4187fb2811 - add a "limitations of reflection" section, [ticket:2233]
Conflicts:
	doc/build/core/reflection.rst
2013-10-18 19:24:52 -04:00
Mike Bayer f498fc3d48 use sets here to eliminate ordering as a factor 2013-10-18 19:04:10 -04:00
Mike Bayer 5ae388b077 - Removed a 128-character truncation from the reflection of the
server default for a column; this code was original from
PG system views which truncated the string for readability.
[ticket:2844]
2013-10-18 19:00:29 -04:00
Mike Bayer cf1ac72bca - The change in 🎫2721, which is that the `deferrable` keyword
of :class:`.ForeignKeyConstraint` is silently ignored on the MySQL
backend, will be reverted as of 0.9; this keyword will now render again, raising
errors on MySQL as it is not understood - the same behavior will also
apply to the ``initially`` keyword.  In 0.8, the keywords will remain
ignored but a warning is emitted.   Additionally, the ``match`` keyword
now raises a :class:`.CompileError` on 0.9 and emits a warning on 0.8;
this keyword is not only silently ignored by MySQL but also breaks
the ON UPDATE/ON DELETE options.

To use a :class:`.ForeignKeyConstraint`
that does not render or renders differently on MySQL, use a custom
compilation option.  An example of this usage has been added to the
documentation, see :ref:`mysql_foreign_keys`.
[ticket:2721] [ticket:2839]
2013-10-18 15:00:42 -04:00
Mike Bayer 621c22ca31 The `.unique flag on :class:.Index could be produced as None`
if it was generated from a :class:`.Column` that didn't specify ``unique``
(where it defaults to ``None``).  The flag will now always be ``True`` or
``False``. [ticket:2825]

Conflicts:
	lib/sqlalchemy/sql/schema.py
2013-10-14 14:56:45 -04:00
Mike Bayer 5389bc5d8d workaround for #2838 here. still need to figure out why an ENUM test is suddenly hitting this. 2013-10-14 10:58:21 -04:00
Mike Bayer b26679e06a alphabetical ordering 2013-10-13 17:17:26 -04:00
Mike Bayer 28a3491357 - Added new option to :func:.relationship `distinct_target_key`.
This enables the subquery eager loader strategy to apply a DISTINCT
to the innermost SELECT subquery, to assist in the case where
duplicate rows are generated by the innermost query which corresponds
to this relationship (there's not yet a general solution to the issue
of dupe rows within subquery eager loading, however, when joins outside
of the innermost subquery produce dupes).  When the flag
is set to ``True``, the DISTINCT is rendered unconditionally, and when
it is set to ``None``, DISTINCT is rendered if the innermost relationship
targets columns that do not comprise a full primary key.
The option defaults to False in 0.8 (e.g. off by default in all cases),
None in 0.9 (e.g. automatic by default).   Thanks to Alexander Koval
for help with this. [ticket:2836]

Conflicts:
	lib/sqlalchemy/orm/relationships.py
2013-10-13 16:54:21 -04:00
Mike Bayer 0cea9fd61c MySQL-connector dialect now allows options in the create_engine
query string to override those defaults set up in the connect,
including "buffered" and "raise_on_warnings".
[ticket:2515]
2013-10-13 14:09:53 -04:00
Mike Bayer 70fdd3efa7 Parenthesis will be applied to a compound SQL expression as
rendered in the column list of a CREATE INDEX statement.
[ticket:2742]
2013-10-12 20:21:38 -04:00
Mike Bayer d8c17e2a6f - Fixed bug in default compiler plus those of postgresql, mysql, and
mssql to ensure that any literal SQL expression values are
rendered directly as literals, instead of as bound parameters,
within a CREATE INDEX statement. [ticket:2742]
- don't need expression_as_ddl(); literal_binds and include_table
take care of this functionality.

Conflicts:
	lib/sqlalchemy/sql/util.py
2013-10-12 20:13:27 -04:00
Mike Bayer e488bb47e4 A :func:.select that is made to refer to itself in its FROM clause,
typically via in-place mutation, will raise an informative error
message rather than causing a recursion overflow.
[ticket:2815]

Conflicts:
	lib/sqlalchemy/sql/selectable.py
2013-10-08 20:08:35 -04:00
Mike Bayer 79f55eb20b Fixed bug where using an annotation such as :func:.remote or
:func:`.foreign` on a :class:`.Column` before association with a parent
:class:`.Table` could produce issues related to the parent table not
rendering within joins, due to the inherent copy operation performed
by an annotation. [ticket:2813]

Conflicts:
	lib/sqlalchemy/sql/elements.py
2013-10-08 19:49:09 -04:00
Mike Bayer c1a69ab841 Non-working "schema" argument on :class:.ForeignKey is deprecated;
raises a warning.  Removed in 0.9. [ticket:2831]
2013-10-08 19:25:28 -04:00
mike bayer 92d477e453 Merge pull request #32 from yoloseem/patch-1
Fix a typo: evaluted -> evaluated.
2013-10-08 07:05:39 -07:00
Hyunjun Kim b4165f9e0d Fix a typo: evaluted -> evaluated. 2013-10-08 16:19:08 +09:00
Mike Bayer 396186ef39 - add monetdb
- break out into "production" and "experimental"
2013-10-01 13:45:59 -04:00
Mike Bayer 51c92e1132 - put exact version string in the test
- use match with a .* preceding instead of search
2013-09-29 17:25:34 -04:00
Mike Bayer a0b8ce4ecf changelog 2013-09-29 17:25:29 -04:00
mike bayer 1e75bb2fab Merge pull request #26 from hackop/ticket_2819
re.match to re.search
2013-09-29 17:25:19 -04:00
Mike Bayer bace4c949e add test for upcoming pullreq 2013-09-29 17:25:11 -04:00
Mike Bayer bb12d79f7b its a feature 2013-09-29 17:17:08 -04:00
Mike Bayer 4ef40dd85b changelog 2013-09-29 17:11:11 -04:00
mike bayer 38e71f52ec Merge pull request #25 from gthb/ticket_2821
Hide password in URL and Engine __repr__
Conflicts:
	lib/sqlalchemy/engine/url.py
2013-09-29 17:10:54 -04:00
Mike Bayer 3e947c2c06 fix typo [ticket:2827] 2013-09-17 17:49:07 -05:00
Mike Bayer 6b30416686 render "backports"/ "forward ports" automatically, only write out
changelog once

Conflicts:
	doc/build/changelog/changelog_09.rst
2013-09-17 15:03:46 -05:00
Vladimir Magamedov 5d1bd984e6 Fixed Query.exists() method for the case, when query doesn't have any filters applied.
Conflicts:
	doc/build/changelog/changelog_09.rst
2013-09-04 11:46:28 -04:00
Mike Bayer de60e3feb0 use a different col here to keep oracle happy 2013-09-02 16:36:14 -04:00
Mike Bayer 2008344aab plus some more adjustments for mysql, or in general if an Index refers to
in-python only cols
2013-08-28 17:32:05 -04:00
Mike Bayer 573b542747 Fixed bug where using the `column_reflect event to change the .key`
of the incoming :class:`.Column` would prevent primary key constraints,
indexes, and foreign key constraints from being correctly reflected.
Also in 0.8.3. [ticket:2811]

Conflicts:
	doc/build/changelog/changelog_09.rst
2013-08-28 17:26:11 -04:00
Mike Bayer bedfeee764 - fix a crapload of seealsos
- fix the label on metadata.rst
2013-08-27 20:45:28 -04:00
Mike Bayer d64ab47d76 correct for missing fail() methods which were lost when we removed unittest.TestCase 2013-08-26 12:36:43 -04:00
Mike Bayer 004710b1e0 clean up formatting and other linting issues
Conflicts:
	test/ext/test_associationproxy.py
2013-08-26 12:36:27 -04:00
Mike Bayer be594493e6 - add version_id documentation [ticket:867]
- add the RETURNING glossary entry
- other "seealso's" fixed
- add doc for eager_defaults option
2013-08-25 14:22:27 -04:00
Mike Bayer cd6cfe36d2 forgot to add system to the copy() method 2013-08-25 12:33:37 -04:00
Mike Bayer f0ac7e20d8 added "system=True" to Column, so that we generally don't have to bother
with CreateColumn rules
2013-08-25 12:31:58 -04:00
mike bayer 9fa0088d43 Merge pull request #23 from yoloseem/master
Fixed two typos.
2013-08-25 12:09:10 -04:00
Mike Bayer 58617ff5ff - The :class:.CreateColumn construct can be appled to a custom
compilation rule which allows skipping of columns, by producing
a rule that returns ``None``.  Also in 0.8.3.

Conflicts:
	doc/build/changelog/changelog_09.rst
	lib/sqlalchemy/sql/ddl.py
2013-08-24 13:58:32 -04:00
Mike Bayer f8061236b8 - this comment is wrong, the method here returns all mapper entities
- use a simple isinstance() check instead of looking for presence of
"primary_entity" flag, that's a little unclear
2013-08-24 11:30:57 -04:00
Mike Bayer 85ed24f79e formatting 2013-08-23 18:58:21 -04:00
Mike Bayer f27992de27 - update the test times, include pypy, clean up the script 2013-08-21 19:18:37 -04:00
Mike Bayer a71e5c68ca add FAQ entry on large inserts 2013-08-21 19:09:16 -04:00
Mike Bayer 702be8a7b8 fix link 2013-08-21 18:57:08 -04:00
Mike Bayer 09b93477d2 move FAQ to the docs, [ticket:2133]
Conflicts:
	doc/build/index.rst
2013-08-21 18:49:34 -04:00
Mike Bayer 78c339a113 yikes return the modname if no lookup found... 2013-08-21 17:14:04 -04:00
Mike Bayer 8665cf0f62 dont need to translate all these names 2013-08-21 17:12:32 -04:00
Mike Bayer b9c14221e7 too small! 2013-08-21 17:08:37 -04:00
Mike Bayer 0e47ee3627 add some height 2013-08-21 17:08:30 -04:00
Mike Bayer 822b85d114 additoinal
cherry pick of 9302be39a5 from 0.9
Conflicts:
	lib/sqlalchemy/sql/selectable.py
2013-08-21 17:08:08 -04:00
Mike Bayer 944f99a3b1 - reorganize docs so expression, schema are broken out into subfiles, they're too big
- fix the targeting of module names moved around by using custom handlers for "Bases", etc.

cherry pick of 0c19c1c66f from 0.9.   there's likely issues to be fixed.
2013-08-21 17:05:02 -04:00
Mike Bayer 241379a446 pypy pickle isn't "bulletproof" 2013-08-20 23:38:42 -04:00
Mike Bayer 06fd903af8 apply test skips for pypy issue #1573 in 0.8 [ticket:2805] 2013-08-20 23:32:23 -04:00
Mike Bayer 36de3538c1 Fixed bug where list instrumentation would fail to represent a
setslice of ``[0:0]`` correctly, which in particular could occur
when using ``insert(0, item)`` with the association proxy.  Due
to some quirk in Python collections, the issue was much more likely
with Python 3 rather than 2.  Also in 0.8.3, 0.7.11.
[ticket:2807]

Conflicts:
	doc/build/changelog/changelog_09.rst
2013-08-20 13:28:21 -04:00
Mike Bayer 22cd5c5cd5 changelog adjust... 2013-08-18 16:10:46 -04:00
Mike Bayer 01aeeb96f9 - use newly fixed WeakSequence (#2794) to not have to rely on class name for sorting in #2779 2013-08-18 16:10:23 -04:00
Mike Bayer ef73845ba7 - add WeakSequence.append()
- fix and test weakref cleanout for WeakSequence, [ticket:2794]
2013-08-18 16:06:08 -04:00
Mike Bayer 6028f82345 Backported a change from 0.9 whereby the iteration of a hierarchy
of mappers used in polymorphic inheritance loads is sorted on class name,
which allows the SELECT statements generated for polymorphic queries
to have deterministic rendering, which in turn helps with caching
schemes that cache on the SQL string itself.
[ticket:2779]
2013-08-18 15:57:06 -04:00
Mike Bayer 17274222f1 fix the merge 2013-08-18 15:36:26 -04:00
Mike Bayer 4b1ca23027 Fixed a potential issue in an ordered sequence implementation used
by the ORM to iterate mapper hierarchies; under the Jython interpreter
this implementation wasn't ordered, even though cPython and Pypy
maintained ordering.  Also in 0.8.3.
[ticket:2794]

Conflicts:
	doc/build/changelog/changelog_09.rst
	lib/sqlalchemy/util/_collections.py
2013-08-18 15:35:23 -04:00
Mike Bayer e71c84729d - also do delete, add seealsos, formatting, etc. [ticket:2798] 2013-08-18 15:15:58 -04:00
Mike Bayer ea787dceca - add better notes to query.update(), most notably how to deal with a joined table
update, [ticket:2798]
2013-08-18 15:02:40 -04:00
Mike Bayer 4f0bda7c45 Fixed regression dating back to 0.7.9 whereby the name of a CTE might
not be properly quoted if it was referred to in multiple FROM clauses.
Also in 0.8.3, 0.7.11. [ticket:2801]

Conflicts:
	doc/build/changelog/changelog_09.rst
2013-08-18 14:46:34 -04:00
Mike Bayer 5cbf8be95d 0.8 changelog 2013-08-17 00:23:50 -04:00
Mike Bayer 22b2fe56eb - changelog
- clean up these tests to not use globals so much, close out the session

Conflicts:
	doc/build/changelog/changelog_09.rst
	examples/versioning/test_versioning.py
2013-08-17 00:22:49 -04:00
Mike Bayer 4ead87dd3b Merged in prschmid/sqlalchemy (pull request #4)
Made primary_key autoincrement=False in versioning example.
2013-08-17 00:21:30 -04:00
Mike Bayer 4997485643 Merged in bacher09/sqlalchemy (pull request #5)
Fix typo in comparator_factory example
2013-08-17 00:05:53 -04:00
Mike Bayer f085167865 add some more detail about concurrency 2013-08-12 14:20:21 -04:00
Mike Bayer 7779c22e40 - more "when do i create the session" docs, get specific
- more glossary terms
- turn the session FAQ into rst sections at last
2013-08-12 11:33:58 -04:00
Mike Bayer f3fc584055 add more docs to index, even though this seems to be a little redundant 2013-08-11 02:22:54 -04:00
mike bayer ca078368ca Merge pull request #21 from svrin/patch-1
Typo in engines.rst
2013-08-08 15:40:47 -05:00
Mike Bayer 3302187fea - The :meth:.Operators.notin_ operator added in 0.8 now properly
produces the negation of the expression "IN" returns
when used against an empty collection.  Also in 0.8.3.
2013-08-07 13:29:19 -05:00
Mike Bayer 8aa5631107 Updates to MySQL reserved words for versions 5.5, 5.6, courtesy
Hanno Schlichting.  Also in 0.8.3, 0.7.11.
[ticket:2791]

Conflicts:
	doc/build/changelog/changelog_09.rst
2013-07-31 21:06:21 -04:00
Mike Bayer 312764983b - update ORM event docs to include that you can listen on an unmapped base,
[ticket:2777]

Conflicts:
	lib/sqlalchemy/orm/events.py
2013-07-31 19:08:05 -04:00
Mike Bayer d060052b52 fix cahngelog 2013-07-31 18:49:59 -04:00
Mike Bayer 834d18ca04 - Fixed bug in common table expression system where if the CTE were
used only as an ``alias()`` construct, it would not render using the
WITH keyword.  Also in 0.7.11.
[ticket:2783]
2013-07-31 18:44:12 -04:00
Mike Bayer 9fcb5348af fix py26 flag 2013-07-27 18:32:15 -04:00
Mike Bayer cbb6f6d4f3 try seeing if just clearing this ahead of time helps 2013-07-27 18:30:48 -04:00
Mike Bayer 8290d96eea exclude python2.5 on this also 2013-07-27 18:28:46 -04:00
Mike Bayer 0655e69c82 tpo 2013-07-20 00:41:21 -04:00
Mike Bayer 37fc3e3bf8 - Improved the examples in `examples/generic_associations`, including
that ``discriminator_on_association.py`` makes use of single table
inheritance do the work with the "discriminator".  Also
added a true "generic foreign key" example, which works similarly
to other popular frameworks in that it uses an open-ended integer
to point to any other table, foregoing traditional referential
integrity.  While we don't recommend this pattern, information wants
to be free.  Also in 0.8.3.

- Added a convenience class decorator :func:`.as_declarative`, is
a wrapper for :func:`.declarative_base` which allows an existing base
class to be applied using a nifty class-decorated approach.  Also
in 0.8.3.
2013-07-19 22:57:12 -04:00
mike bayer a6069c75c2 Merge pull request #18 from davidjb/patch-1
Minor hybrid extension documentation update
2013-07-19 11:58:22 -04:00
Mike Bayer 90d992f2da Fixed bug in ORM-level event registration where the "raw" or
"propagate" flags could potentially be mis-configured in some
"unmapped base class" configurations.  Also in 0.8.3.
[ticket:2786]
2013-07-18 23:19:04 -04:00
Mike Bayer 436ba1601d Fixed bug in :class:.CheckConstraint DDL where the "quote" flag from a
:class:`.Column` object would not be propagated.  Also in 0.8.3, 0.7.11.
[ticket:2784]
2013-07-17 11:19:50 -04:00
mike bayer 7da90f41b2 Merge pull request #17 from iElectric/docs/wording_yield_per
wording about supported dialects for Query.yield_per
2013-07-14 12:59:58 -04:00
Mike Bayer a5d9e13675 further tweaking 2013-07-13 16:41:30 -04:00
Mike Bayer 2bef496632 fix the scoping here 2013-07-13 16:36:54 -04:00
Mike Bayer 645a8bfda2 callcounts 2013-07-13 16:33:41 -04:00
Mike Bayer 5b65af0983 A performance fix related to the usage of the :func:.defer option
when loading mapped entities.   The function overhead of applying
a per-object deferred callable to an instance at load time was
significantly higher than that of just loading the data from the row
(note that ``defer()`` is meant to reduce DB/network overhead, not
necessarily function call count); the function call overhead is now
less than that of loading data from the column in all cases.  There
is also a reduction in the number of "lazy callable" objects created
per load from N (total deferred values in the result) to 1 (total
number of deferred cols).
[ticket:2778]
2013-07-13 16:28:42 -04:00
Mike Bayer 4018a03e7a doc fix 2013-07-12 22:02:13 -04:00
Mike Bayer 2edc13a6b8 The newly added SQLite DATETIME arguments storage_format and
regexp apparently were not fully implemented correctly; while the
arguments were accepted, in practice they would have no effect;
this has been fixed.
[ticket:2781]
2013-07-12 21:53:28 -04:00
Mike Bayer 3c363e125c - use inline=True for the insert..select here so it works on oracle 2013-07-12 12:15:39 -04:00
Mike Bayer 6c85d5bc0c CSS tweaks to support version styling in the latest versions of sphinx 2013-07-12 12:15:33 -04:00
Mike Bayer 5fb1a537e9 fix missing import here 2013-07-12 11:43:55 -04:00
Mike Bayer 574dba8e61 Fixed bug where the expression system relied upon the `str()`
form of a some expressions when referring to the ``.c`` collection
on a ``select()`` construct, but the ``str()`` form isn't available
since the element relies on dialect-specific compilation constructs,
notably the ``__getitem__()`` operator as used with a Postgresql
``ARRAY`` element.  The fix also adds a new exception class
:class:`.UnsupportedCompilationError` which is raised in those cases
where a compiler is asked to compile something it doesn't know
how to.
[ticket:2780]
2013-07-12 11:33:24 -04:00
Mike Bayer 1e332e03d3 don't split the regexps for chop_traceback() 2013-07-12 10:42:18 -04:00
Mike Bayer 9a95a39b49 Dialect.initialize() is not called a second time if an :class:.Engine
is recreated, due to a disconnect error.   This fixes a particular
issue in the Oracle 8 dialect, but in general the dialect.initialize()
phase should only be once per dialect.  [ticket:2776]
2013-07-11 15:16:34 -04:00
Mike Bayer 124db0eb91 remove undoc-members from query docstring [ticket:2774] 2013-07-07 12:02:51 -04:00
Mike Bayer 10c8867225 use "target tables" here 2013-07-06 16:42:44 -04:00
Mike Bayer 8373d7599e - Added new method to the :func:.insert construct
:meth:`.Insert.from_select`.  Given a list of columns and
a selectable, renders ``INSERT INTO (table) (columns) SELECT ..``.
While this feature is highlighted as part of 0.9 it is also
backported to 0.8.3. [ticket:722]
- The :func:`.update`, :func:`.insert`, and :func:`.delete` constructs
will now interpret ORM entities as FROM clauses to be operated upon,
in the same way that select() already does.
2013-07-05 15:52:34 -04:00
Mike Bayer 76290f87cc callcount 2013-07-05 14:40:47 -04:00
Mike Bayer 3f75c6aff6 Fixed bug whereby attribute history functions would fail
when an object we moved from "persistent" to "pending"
using the :func:`.make_transient` function, for operations
involving collection-based backrefs.
[ticket:2773]
2013-07-04 20:01:55 -04:00
Mike Bayer 4a9d88b1cf - take advantage of 0.9's pool redesign a bit, adding
_ConnectionRecord.checkin() so that this functionality is
encapsulated; is now called from two different locations
for [ticket:2772].
- Fixed bug where :class:`.QueuePool` would lose the correct
checked out count if an existing pooled connection failed to reconnect
after an invalidate or recycle event. [ticket:2772]
2013-07-04 13:25:40 -04:00
Mike Bayer 57da644320 0.8.2 2013-07-03 16:07:51 -04:00
Mike Bayer 55b664622d dont need this 2013-07-03 15:38:45 -04:00
Mike Bayer 52293e3d4c make this rule automatic based on coverage plugin 2013-07-03 13:47:44 -04:00
Mike Bayer 64e019f392 mark the tests that seem to be segfauling py3k+coverage so that we can exclude them
at the nose command line
2013-07-03 11:39:26 -04:00
Mike Bayer e68377b95b for this test, apparently we don't handle sets as unordered since neither does
MySQL.  for some reason set ordering was constant when testing mysqldb, but not
so with oursql.
2013-07-02 20:36:23 -04:00
Mike Bayer fabd199246 - clean up some of the requires for cross-schema reflection
- add oracle profile counts
2013-07-02 19:12:49 -04:00
Mike Bayer a4e59730d5 genericize tests here 2013-07-02 19:06:30 -04:00
Mike Bayer 9c5a13f049 Fixed bug when using multi-table UPDATE where a supplemental
table is a SELECT with its own bound parameters, where the positioning
of the bound parameters would be reversed versus the statement
itself when using MySQL's special syntax.
[ticket:2768]
2013-07-02 18:25:35 -04:00
Mike Bayer 2180cf44e5 Added :class:.BIGINT to the list of type names that can be
reflected by the SQLite dialect; courtesy Russell Stuart.
[ticket:2764]
2013-07-02 18:11:31 -04:00
Mike Bayer 761414a07b ORM descriptors such as hybrid properties can now be referenced
by name in a string argument used with ``order_by``,
``primaryjoin``, or similar in :func:`.relationship`,
in addition to column-bound attributes. [ticket:2761]
2013-07-02 18:02:56 -04:00
Mike Bayer 1bdd0bac3f fix python 2.5isms 2013-06-30 23:38:45 -04:00
Mike Bayer b8046baf74 fix a missing stringio import 2013-06-30 21:11:56 -04:00
Mike Bayer 63f157b18a fix syntax screwup
Conflicts:
	doc/build/changelog/changelog_09.rst
2013-06-30 20:15:47 -04:00
Mike Bayer cc299b2d1c Added new flag `retaining=False` to the kinterbasdb and fdb dialects.
This controls the value of the ``retaining`` flag sent to the
``commit()`` and ``rollback()`` methods of the DBAPI connection.
Defaults to False.  Also in 0.8.2, where it defaults to True.
[ticket:2763]
2013-06-30 19:54:59 -04:00
Mike Bayer 78cc4d9c34 - replace most explicitly-named test objects called "Mock..." with
actual mock objects from the mock library.  I'd like to use mock
for new tests so we might as well use it in obvious places.
- use unittest.mock in py3.3
- changelog
- add a note to README.unittests
- add tests_require in setup.py
- have tests import from sqlalchemy.testing.mock
- apply usage of mock to one of the event tests.  we can be using
this approach all over the place.
2013-06-30 18:41:35 -04:00
Mike Bayer d4b850fa6b - additional fix for [ticket:2750] where on an update, we make sure the
value is present
2013-06-30 15:59:26 -04:00
Mike Bayer 1d67b2a233 add better tests for [ticket:2750] 2013-06-30 11:21:19 -04:00
Mike Bayer ead7d9a680 A warning is emitted when trying to flush an object of an inherited
mapped class where the polymorphic discriminator has been assigned
to a value that is invalid for the class.   [ticket:2750]
2013-06-30 11:11:35 -04:00
Mike Bayer cf60290e7b tweak the timings here to maximize chance of test success 2013-06-29 11:17:57 -04:00
Mike Bayer acc3c69464 set UTC timezone on the connection here so we can get consistent results 2013-06-29 11:08:32 -04:00
Mike Bayer 627a8cecf6 fix 2.5 syntaxes 2013-06-29 10:56:34 -04:00
Mike Bayer 169c88c7a5 The behavior of :func:.extract has been simplified on the
Postgresql dialect to no longer inject a hardcoded ``::timestamp``
or similar cast into the given expression, as this interfered
with types such as timezone-aware datetimes, but also
does not appear to be at all necessary with modern versions
of psycopg2.  Also in 0.8.2.
[ticket:2740]

Conflicts:
	doc/build/changelog/changelog_09.rst
2013-06-28 23:54:11 -04:00
Mike Bayer 7e8f1341fc refactor test suites for postgresql, mssql, mysql into packages. 2013-06-28 22:40:10 -04:00
Mike Bayer fb8d23fb09 improve the close handling here so that we don't double-close a connection;
we log this now so it apparently happens a bunch
2013-06-28 22:32:43 -04:00
Mike Bayer f57fc6836f Type lookup when reflecting the Firebird types LONG and
INT64 has been fixed so that LONG is treated as INTEGER,
INT64 treated as BIGINT, unless the type has a "precision"
in which case it's treated as NUMERIC.  Patch courtesy
Russell Stuart.
[ticket:2757]

Conflicts:
	doc/build/changelog/changelog_09.rst
2013-06-28 11:50:27 -04:00
Mike Bayer 69179e009c Fixed bug in HSTORE type where keys/values that contained
backslashed quotes would not be escaped correctly when
using the "non native" (i.e. non-psycopg2) means
of translating HSTORE data.  Patch courtesy Ryan Kelly.
[ticket:2766]

Conflicts:
	doc/build/changelog/changelog_09.rst
	lib/sqlalchemy/dialects/postgresql/hstore.py
2013-06-28 11:38:28 -04:00
Mike Bayer d64c945eae add an ORDER BY here 2013-06-26 15:04:08 -04:00
Mike Bayer b4697e9e18 - rework of correlation, continuing on #2668, #2746
- add support for correlations to propagate all the way in; because
correlations require context now, need to make sure a select enclosure
of any level takes effect any number of levels deep.
- fix what we said correlate_except() was supposed to do when we first
released #2668 - "the FROM clause is left intact if the correlated SELECT
is not used in the context of an enclosing SELECT..." - it was not
considering the "existing_froms" collection at all, and prohibited
additional FROMs from being placed in an any() or has().
- add test for multilevel any()
- lots of docs, including glossary entries as we really need to define
"WHERE clause", "columns clause" etc. so that we can explain correlation better
- based on the insight that a SELECT can correlate anything that ultimately
came from an enclosing SELECT that links to this one via WHERE/columns/HAVING/ORDER BY,
have the compiler keep track of the FROM lists that correspond in this way,
link it to the asfrom flag, so that we send to _get_display_froms() the exact
list of candidate FROMs to correlate.  no longer need any asfrom logic in the
Select() itself
- preserve 0.8.1's behavior for correlation when no correlate options are given, not
to mention 0.7 and prior's behavior of not propagating implicit correlation more than one level..
this is to reduce surprises/hard-to-debug situations when a user isn't trying
to correlate anything.

Conflicts:
	doc/build/changelog/changelog_08.rst
	doc/build/changelog/changelog_09.rst
	lib/sqlalchemy/sql/compiler.py
2013-06-26 13:32:39 -04:00
Mike Bayer 699fdd4330 need the defaultdict import here 2013-06-26 11:42:42 -04:00
Mike Bayer fee954f253 changelog
Conflicts:
	doc/build/changelog/changelog_09.rst
2013-06-26 11:39:50 -04:00
mike bayer 59c01813ad Merge pull request #13 from malor/fix_indexes_reflection
Fix indexes reflection in PostgreSQL
2013-06-26 11:39:29 -04:00
mike bayer 23805f50ed Merge pull request #12 from cjw296/master
Range type docs improvements.
2013-06-26 11:17:01 -04:00
Mike Bayer e3e56ce70e 0.8 changelog
Conflicts:
	doc/build/changelog/changelog_09.rst
2013-06-23 17:57:20 -04:00
Mike Bayer decc72ee0e Merge branch 'master' of https://github.com/asldevi/sqlalchemy into some_branch 2013-06-23 17:57:00 -04:00
Mike Bayer 564c4eac71 2.5ism 2013-06-23 00:21:22 -04:00
Mike Bayer aa5cc5a2cc Provided a new attribute for :class:.TypeDecorator
called :attr:`.TypeDecorator.coerce_to_is_types`,
to make it easier to control how comparisons using
``==`` or ``!=`` to ``None`` and boolean types goes
about producing an ``IS`` expression, or a plain
equality expression with a bound parameter.
[ticket:2744]

Conflicts:
	doc/build/changelog/changelog_09.rst
2013-06-22 12:20:20 -04:00
Mike Bayer 6bdeab2e4c versionadds 2013-06-22 11:48:46 -04:00
Mike Bayer d42faf26b7 - 0.8 changelog
- some whitespace
2013-06-22 11:40:10 -04:00
mike bayer f52d45672b Merge pull request #5 from cjw296/pg-ranges
Support for Postgres range types.
2013-06-22 11:27:09 -04:00
Mike Bayer c05ef592bf Merged in goodscloud/sqlalchemy (pull request #3)
Forgot final check that a delete in a backref also doesn't create a new version
2013-06-18 14:03:34 -04:00
Mike Bayer 2bb1a3f8b4 fix up the isolation level docs which were a mess 2013-06-17 23:12:22 -04:00
Mike Bayer 9fda3588ac changelog for 0.8 2013-06-17 16:59:13 -04:00
Mike Bayer 6286f715db Merged in goodscloud/sqlalchemy (pull request #2)
don't create a history entry when an object in a backref has changed
2013-06-17 16:58:56 -04:00
Mike Bayer f7e6112d0f - changelog
- docs

Conflicts:
	doc/build/changelog/changelog_09.rst
2013-06-15 22:59:09 -04:00
mike bayer 7a3a446d02 Merge pull request #7 from malor/psycopg2_execution_options
Add AUTOCOMMIT isolation level support for psycopg2
2013-06-15 22:58:46 -04:00
Mike Bayer abd17f4daf fix tests here for 0.8 2013-06-15 15:29:21 -04:00
Mike Bayer aee826890c Fixed bug in polymorphic SQL generation where multiple joined-inheritance
entities against the same base class joined to each other as well
would not track columns on the base table independently of each other if
the string of joins were more than two entities long.  Also in 0.8.2.
[ticket:2759]

Conflicts:
	doc/build/changelog/changelog_09.rst
2013-06-15 15:29:00 -04:00
Mike Bayer 0b2c7765a0 - skip this test for py3k. serializer kind of a bust
Conflicts:
	test/ext/test_serializer.py
2013-06-14 18:36:32 -04:00
Mike Bayer 01b7b9ae29 changelog
Conflicts:
	doc/build/changelog/changelog_09.rst
2013-06-10 19:44:06 -04:00
mike bayer c0ee99da9e Merge pull request #6 from eevee/propagate-reset-on-return
Preserve reset_on_return when recreating a Pool.
2013-06-10 19:43:40 -04:00
Mike Bayer 9dba65b381 Fixed bug where sending a composite attribute into :meth:.Query.order_by
would produce a parenthesized expression not accepted by some databases.
[ticket:2754]

Conflicts:
	doc/build/changelog/changelog_09.rst
2013-06-10 13:51:18 -04:00
Mike Bayer 6d0b2f34d2 Fixed the interaction between composite attributes and
the :func:`.aliased` function.  Previously, composite attributes
wouldn't work correctly in comparison operations when aliasing
was applied.  Also in 0.8.2. [ticket:2755]

Conflicts:
	doc/build/changelog/changelog_09.rst
2013-06-10 13:50:56 -04:00
Mike Bayer 0deebb51c8 changelog
Conflicts:
	doc/build/changelog/changelog_09.rst
2013-06-08 15:21:25 -04:00
mike bayer 06a2ce55a1 Merge pull request #3 from bslatkin/master
Makes gaerdbms for App Engine use local MySQL server when running in dev_appserver2
2013-06-08 15:20:04 -04:00
Mike Bayer 614e8dc617 most of these dialect=mysql.dialect() calls are redundant 2013-06-08 14:56:20 -04:00
Mike Bayer 3f6006953e - changelog for [ticket:2704]
- use an isinstance() check, concerned a TypeError might be indiscriminate

Conflicts:
	doc/build/changelog/changelog_09.rst
2013-06-08 14:56:00 -04:00
Roman Podolyaka 1b231ff271 Fix using of 'mysql_length' for composite indexes
Currently, one can specify the prefix length for an index
column using 'mysql_length' keyword argument when creating
an Index instance. But in case of composite indexes the
prefix length value is applied only to the last column.

Extend the existing API in way so that 'mysql_length' argument
value can be either:
    - an integer specifying the same prefix length value
      for each column of an index
    - a (column_name --> integer value) mapping specifying
      the prefix length value for each column of an index
      separately

Fixes issue #2704.
2013-06-08 14:54:42 -04:00
Mike Bayer 45f8ff88c9 When querying the information schema on SQL Server 2000, removed
a CAST call that was added in 0.8.1 to help with driver issues,
which apparently is not compatible on 2000.
The CAST remains in place for SQL Server 2005 and greater.
[ticket:2747]

Conflicts:
	doc/build/changelog/changelog_09.rst
	lib/sqlalchemy/dialects/mssql/information_schema.py
2013-06-06 12:55:00 -04:00
Mike Bayer cc1ea5da10 The `deferrable keyword argument on :class:.ForeignKey` and
:class:`.ForeignKeyConstraint` will not render the ``DEFERRABLE`` keyword
on the MySQL dialect.  For a long time we left this in place because
a non-deferrable foreign key would act very differently than a deferrable
one, but some environments just disable FKs on MySQL, so we'll be less
opinionated here. [ticket:2721]

Conflicts:
	doc/build/changelog/changelog_09.rst
2013-06-03 18:33:57 -04:00
Mike Bayer c2bbdf7770 Fixed bug where :class:.MutableDict didn't report a change event
when ``clear()`` was called.
[ticket:2730]

Conflicts:
	doc/build/changelog/changelog_09.rst
2013-06-03 18:24:00 -04:00
Mike Bayer 1fc2611e20 Fixed bug whereby joining a select() of a table "A" with multiple
foreign key paths to a table "B", to that table "B", would fail
to produce the "ambiguous join condition" error that would be
reported if you join table "A" directly to "B"; it would instead
produce a join condition with multiple criteria.
[ticket:2738]

Conflicts:
	doc/build/changelog/changelog_09.rst
2013-06-03 17:04:03 -04:00
Mike Bayer f38e2aa460 Fixed bug whereby using :meth:.MetaData.reflect across a remote
schema as well as a local schema could produce wrong results
in the case where both schemas had a table of the same name.
[ticket:2728]

Conflicts:
	doc/build/changelog/changelog_09.rst
	lib/sqlalchemy/schema.py
2013-06-03 16:44:35 -04:00
Mike Bayer 33d3e11cbf - remove the `__iter__()` with notimplemented since it interferes
with legitimate iterable detection, [ticket:2726]

Conflicts:
	doc/build/changelog/changelog_09.rst
2013-06-03 16:42:46 -04:00
Mike Bayer c6f54ae434 Fixed a regression caused by [ticket:2682] whereby the
evaluation invoked by :meth:`.Query.update` and :meth:`.Query.delete`
would hit upon unsupported ``True`` and ``False`` symbols
which now appear due to the usage of ``IS``.
[ticket:2737]

Conflicts:
	doc/build/changelog/changelog_08.rst
2013-05-31 11:55:48 -04:00
Mike Bayer 77001c5a2d update 2013-05-30 20:31:49 -04:00
Mike Bayer 81e786f50f restore functionality here 2013-05-30 20:30:51 -04:00
Mike Bayer 489312998f Added a new method :meth:.Query.select_entity_from which
will in 0.9 replace part of the functionality of
:meth:`.Query.select_from`.  In 0.8, the two methods perform
the same function, so that code can be migrated to use the
:meth:`.Query.select_entity_from` method as appropriate.
See the 0.9 migration guide for details.
[ticket:2736]
2013-05-30 20:24:48 -04:00
Mike Bayer 6ec33758f6 Merge branch 'master' into rel_0_8 2013-05-29 18:36:36 -04:00
Mike Bayer a5bacf91f0 Merge branch 'master' into rel_0_8 2013-05-28 01:22:23 -04:00
Mike Bayer ff96d23a90 Merge branch 'master' into rel_0_8 2013-05-26 17:18:17 -04:00
diana 792facf539 - fixing AbstractConcreteBase import in docstring, 0.8
branch, [ticket:2717]
2013-05-26 10:21:13 -04:00
Mike Bayer e14632ce13 Merge branch 'master' into rel_0_8 2013-05-25 17:05:01 -04:00
Mike Bayer 20f567cba5 merge default 2013-05-23 13:02:44 -04:00
Mike Bayer 6717290d3d merge default 2013-05-15 15:46:57 -04:00
Mike Bayer ca7dc2a2ab merge default 2013-05-13 16:13:32 -04:00
Mike Bayer ff8e043669 merge default 2013-05-10 01:06:23 -04:00
Mike Bayer 361376f634 merge default 2013-05-05 01:39:52 -04:00
Mike Bayer 1f9c95333e merge default 2013-05-04 16:36:10 -04:00
Mike Bayer 7d16ab9103 merge default 2013-05-01 10:09:48 -04:00
Mike Bayer 07f7a5b0be merge default 2013-04-30 10:03:02 -04:00
Mike Bayer d1b089c67b merge default 2013-04-29 19:50:08 -04:00
Mike Bayer 5a843390ef merge default 2013-04-29 19:49:45 -04:00
Mike Bayer e0e44ddd67 merge default 2013-04-29 19:28:00 -04:00
328 changed files with 24323 additions and 13272 deletions
+10 -4
View File
@@ -1,12 +1,18 @@
*.pyc
build/
dist/
docs/build/output/
dogpile_data/
*.pyo
/build/
/dist/
/doc/build/output/
/dogpile_data/
*.orig
tox.ini
.venv
*.egg-info
.coverage
.*,cover
*.class
*.so
sqlnet.log
/mapping_setup.py
/test.py
+1 -1
View File
@@ -1,6 +1,6 @@
This is the MIT license: http://www.opensource.org/licenses/mit-license.php
Copyright (c) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>.
Copyright (c) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>.
SQLAlchemy is a trademark of Michael Bayer.
Permission is hereby granted, free of charge, to any person obtaining a copy of this
+1 -1
View File
@@ -9,5 +9,5 @@ recursive-include test *.py *.dat
# don't come in if --with-cextensions isn't specified.
recursive-include lib *.c *.txt
include README* LICENSE distribute_setup.py sa2to3.py ez_setup.py sqla_nose.py CHANGES*
include README* AUTHORS LICENSE distribute_setup.py sa2to3.py ez_setup.py sqla_nose.py CHANGES*
prune doc/build/output
+13 -1
View File
@@ -7,12 +7,18 @@ module. If running on Python 2.4, pysqlite must be installed.
Unit tests are run using nose. Nose is available at::
http://pypi.python.org/pypi/nose/
https://pypi.python.org/pypi/nose/
SQLAlchemy implements a nose plugin that must be present when tests are run.
This plugin is invoked when the test runner script provided with
SQLAlchemy is used.
The test suite as of version 0.8.2 also requires the mock library. While
mock is part of the Python standard library as of 3.3, previous versions
will need to have it installed, and is available at::
https://pypi.python.org/pypi/mock
**NOTE:** - the nose plugin is no longer installed by setuptools as of
version 0.7 ! Use "python setup.py test" or "./sqla_nose.py".
@@ -154,6 +160,12 @@ Additional steps specific to individual databases are as follows::
requires using a test.cfg configuration file as the cmd.exe shell won't
properly pass the URL arguments into the nose test runner.
POSTGRESQL: Full-text search configuration should be set to English, else
several tests of ``.match()`` will fail. This can be set (if it isn't so
already) with:
ALTER DATABASE test SET default_text_search_config = 'pg_catalog.english'
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::
+2 -2
View File
@@ -41,7 +41,7 @@ clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html -A mako_layout=html $(ALLSPHINXOPTS) $(BUILDDIR)/html
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
@@ -51,7 +51,7 @@ gettext:
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
dist-html:
$(SPHINXBUILD) -b html -A mako_layout=html $(ALLSPHINXOPTS) ..
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) ..
@echo
@echo "Build finished. The HTML pages are in ../."
+38 -4
View File
@@ -9,6 +9,22 @@ def autodoc_skip_member(app, what, name, obj, skip, options):
else:
return skip
_convert_modname = {
}
_convert_modname_w_class = {
("sqlalchemy.engine.interfaces", "Connectable"): "sqlalchemy.engine"
}
def _adjust_rendered_mod_name(modname, objname):
if modname in _convert_modname:
return _convert_modname[modname]
elif (modname, objname) in _convert_modname_w_class:
return _convert_modname_w_class[(modname, objname)]
else:
return modname
# im sure this is in the app somewhere, but I don't really
# know where, so we're doing it here.
_track_autodoced = {}
@@ -16,6 +32,24 @@ _inherited_names = set()
def autodoc_process_docstring(app, what, name, obj, options, lines):
if what == "class":
_track_autodoced[name] = obj
# need to translate module names for bases, others
# as we document lots of symbols in namespace modules
# outside of their source
bases = []
for base in obj.__bases__:
if base is not object:
bases.append(":class:`%s.%s`" % (
_adjust_rendered_mod_name(base.__module__, base.__name__),
base.__name__))
if bases:
lines[:0] = [
"Bases: %s" % (", ".join(bases)),
""
]
elif what in ("attribute", "method") and \
options.get("inherited-members"):
m = re.match(r'(.*?)\.([\w_]+)$', name)
@@ -35,15 +69,16 @@ def autodoc_process_docstring(app, what, name, obj, options, lines):
" *inherited from the* :%s:`~%s.%s.%s` *%s of* :class:`~%s.%s`" % (
"attr" if what == "attribute"
else "meth",
supercls.__module__, supercls.__name__,
_adjust_rendered_mod_name(supercls.__module__, supercls.__name__),
supercls.__name__,
attrname,
what,
supercls.__module__, supercls.__name__
_adjust_rendered_mod_name(supercls.__module__, supercls.__name__),
supercls.__name__
),
""
]
from docutils import nodes
def missing_reference(app, env, node, contnode):
if node.attributes['reftarget'] in _inherited_names:
return node.children[0]
@@ -51,7 +86,6 @@ def missing_reference(app, env, node, contnode):
return None
def setup(app):
app.connect('autodoc-skip-member', autodoc_skip_member)
app.connect('autodoc-process-docstring', autodoc_process_docstring)
-296
View File
@@ -1,296 +0,0 @@
import re
from sphinx.util.compat import Directive
from docutils.statemachine import StringList
from docutils import nodes, utils
import textwrap
import itertools
import collections
import md5
def _comma_list(text):
return re.split(r"\s*,\s*", text.strip())
def _parse_content(content):
d = {}
d['text'] = []
idx = 0
for line in content:
idx += 1
m = re.match(r' *\:(.+?)\:(?: +(.+))?', line)
if m:
attrname, value = m.group(1, 2)
d[attrname] = value or ''
else:
break
d["text"] = content[idx:]
return d
class EnvDirective(object):
@property
def env(self):
return self.state.document.settings.env
class ChangeLogDirective(EnvDirective, Directive):
has_content = True
type_ = "change"
default_section = 'misc'
def _organize_by_section(self, changes):
compound_sections = [(s, s.split(" ")) for s in
self.sections if " " in s]
bysection = collections.defaultdict(list)
all_sections = set()
for rec in changes:
inner_tag = rec['tags'].intersection(self.inner_tag_sort)
if inner_tag:
inner_tag = inner_tag.pop()
else:
inner_tag = ""
for compound, comp_words in compound_sections:
if rec['tags'].issuperset(comp_words):
bysection[(compound, inner_tag)].append(rec)
all_sections.add(compound)
break
else:
intersect = rec['tags'].intersection(self.sections)
if intersect:
for sec in rec['sorted_tags']:
if sec in intersect:
bysection[(sec, inner_tag)].append(rec)
all_sections.add(sec)
break
else:
bysection[(self.default_section, inner_tag)].append(rec)
return bysection, all_sections
@classmethod
def changes(cls, env):
return env.temp_data['ChangeLogDirective_%s_changes' % cls.type_]
def _setup_run(self):
self.sections = self.env.config.changelog_sections
self.inner_tag_sort = self.env.config.changelog_inner_tag_sort + [""]
self.env.temp_data['ChangeLogDirective_%s_changes' % self.type_] = []
self._parsed_content = _parse_content(self.content)
p = nodes.paragraph('', '',)
self.state.nested_parse(self.content[1:], 0, p)
def run(self):
self._setup_run()
changes = self.changes(self.env)
output = []
self.version = version = self._parsed_content.get('version', '')
id_prefix = "%s-%s" % (self.type_, version)
topsection = self._run_top(id_prefix)
output.append(topsection)
bysection, all_sections = self._organize_by_section(changes)
counter = itertools.count()
sections_to_render = [s for s in self.sections if s in all_sections]
if not sections_to_render:
for cat in self.inner_tag_sort:
append_sec = self._append_node()
for rec in bysection[(self.default_section, cat)]:
rec["id"] = "%s-%s" % (id_prefix, next(counter))
self._render_rec(rec, None, cat, append_sec)
if append_sec.children:
topsection.append(append_sec)
else:
for section in sections_to_render + [self.default_section]:
sec = nodes.section('',
nodes.title(section, section),
ids=["%s-%s" % (id_prefix, section.replace(" ", "-"))]
)
append_sec = self._append_node()
sec.append(append_sec)
for cat in self.inner_tag_sort:
for rec in bysection[(section, cat)]:
rec["id"] = "%s-%s" % (id_prefix, next(counter))
self._render_rec(rec, section, cat, append_sec)
if append_sec.children:
topsection.append(sec)
return output
def _append_node(self):
return nodes.bullet_list()
def _run_top(self, id_prefix):
version = self._parsed_content.get('version', '')
topsection = nodes.section('',
nodes.title(version, version),
ids=[id_prefix]
)
if self._parsed_content.get("released"):
topsection.append(nodes.Text("Released: %s" %
self._parsed_content['released']))
else:
topsection.append(nodes.Text("no release date"))
intro_para = nodes.paragraph('', '')
for len_, text in enumerate(self._parsed_content['text']):
if ".. change::" in text:
break
if len_:
self.state.nested_parse(self._parsed_content['text'][0:len_], 0,
intro_para)
topsection.append(intro_para)
return topsection
def _render_rec(self, rec, section, cat, append_sec):
para = rec['node'].deepcopy()
text = _text_rawsource_from_node(para)
to_hash = "%s %s" % (self.version, text[0:100])
targetid = "%s-%s" % (self.type_,
md5.md5(to_hash.encode('ascii', 'ignore')
).hexdigest())
targetnode = nodes.target('', '', ids=[targetid])
para.insert(0, targetnode)
permalink = nodes.reference('', '',
nodes.Text("(link)", "(link)"),
refid=targetid,
classes=['changeset-link']
)
para.append(permalink)
insert_ticket = nodes.paragraph('')
para.append(insert_ticket)
i = 0
for collection, render, prefix in (
(rec['tickets'], self.env.config.changelog_render_ticket, "#%s"),
(rec['pullreq'], self.env.config.changelog_render_pullreq,
"pull request %s"),
(rec['changeset'], self.env.config.changelog_render_changeset, "r%s"),
):
for refname in collection:
if i > 0:
insert_ticket.append(nodes.Text(", ", ", "))
else:
insert_ticket.append(nodes.Text(" ", " "))
i += 1
if render is not None:
refuri = render % refname
node = nodes.reference('', '',
nodes.Text(prefix % refname, prefix % refname),
refuri=refuri
)
else:
node = nodes.Text(prefix % refname, prefix % refname)
insert_ticket.append(node)
if rec['tags']:
tag_node = nodes.strong('',
" ".join("[%s]" % t for t
in
[t1 for t1 in [section, cat]
if t1 in rec['tags']] +
list(rec['tags'].difference([section, cat]))
) + " "
)
para.children[0].insert(0, tag_node)
append_sec.append(
nodes.list_item('',
nodes.target('', '', ids=[rec['id']]),
para
)
)
class ChangeDirective(EnvDirective, Directive):
has_content = True
type_ = "change"
parent_cls = ChangeLogDirective
def run(self):
content = _parse_content(self.content)
p = nodes.paragraph('', '',)
sorted_tags = _comma_list(content.get('tags', ''))
rec = {
'tags': set(sorted_tags).difference(['']),
'tickets': set(_comma_list(content.get('tickets', ''))).difference(['']),
'pullreq': set(_comma_list(content.get('pullreq', ''))).difference(['']),
'changeset': set(_comma_list(content.get('changeset', ''))).difference(['']),
'node': p,
'type': self.type_,
"title": content.get("title", None),
'sorted_tags': sorted_tags
}
if "declarative" in rec['tags']:
rec['tags'].add("orm")
self.state.nested_parse(content['text'], 0, p)
self.parent_cls.changes(self.env).append(rec)
return []
def _text_rawsource_from_node(node):
src = []
stack = [node]
while stack:
n = stack.pop(0)
if isinstance(n, nodes.Text):
src.append(n.rawsource)
stack.extend(n.children)
return "".join(src)
def _rst2sphinx(text):
return StringList(
[line.strip() for line in textwrap.dedent(text).split("\n")]
)
def make_ticket_link(name, rawtext, text, lineno, inliner,
options={}, content=[]):
env = inliner.document.settings.env
render_ticket = env.config.changelog_render_ticket or "%s"
prefix = "#%s"
if render_ticket:
ref = render_ticket % text
node = nodes.reference(rawtext, prefix % text, refuri=ref, **options)
else:
node = nodes.Text(prefix % text, prefix % text)
return [node], []
def setup(app):
app.add_directive('changelog', ChangeLogDirective)
app.add_directive('change', ChangeDirective)
app.add_config_value("changelog_sections", [], 'env')
app.add_config_value("changelog_inner_tag_sort", [], 'env')
app.add_config_value("changelog_render_ticket",
None,
'env'
)
app.add_config_value("changelog_render_pullreq",
None,
'env'
)
app.add_config_value("changelog_render_changeset",
None,
'env'
)
app.add_role('ticket', make_ticket_link)
+17 -35
View File
@@ -23,49 +23,31 @@ class MakoBridge(TemplateBridge):
)
if rtd:
# RTD layout, imported from sqlalchemy.org
import urllib2
template_url = builder.config['site_base'] + "/docs_base.mako"
template = urllib2.urlopen(template_url).read()
self.lookup.put_string("/rtd_base.mako", template)
template = urllib2.urlopen(builder.config['site_base'] + "/docs_adapter.mako").read()
self.lookup.put_string("docs_adapter.mako", template)
setup_ctx = urllib2.urlopen(builder.config['site_base'] + "/docs_adapter.py").read()
lcls = {}
exec(setup_ctx, lcls)
self.setup_ctx = lcls['setup_context']
def setup_ctx(self, context):
pass
def render(self, template, context):
template = template.replace(".html", ".mako")
context['prevtopic'] = context.pop('prev', None)
context['nexttopic'] = context.pop('next', None)
# RTD layout
if rtd:
# add variables if not present, such
# as if local test of READTHEDOCS variable
if 'MEDIA_URL' not in context:
context['MEDIA_URL'] = "http://media.readthedocs.org/"
if 'slug' not in context:
context['slug'] = context['project'].lower()
if 'url' not in context:
context['url'] = "/some/test/url"
if 'current_version' not in context:
context['current_version'] = "latest"
if 'name' not in context:
context['name'] = context['project'].lower()
context['rtd'] = True
context['toolbar'] = True
context['layout'] = "rtd_layout.mako"
context['base'] = "rtd_base.mako"
context['pdf_url'] = "%spdf/%s/%s/%s.pdf" % (
context['MEDIA_URL'],
context['slug'],
context['current_version'],
context['slug']
)
# local docs layout
else:
context['rtd'] = False
context['toolbar'] = False
context['layout'] = "layout.mako"
context['base'] = "static_base.mako"
context['rtd'] = False
context['toolbar'] = False
context['base'] = "static_base.mako"
# override context attributes
self.setup_ctx(context)
context.setdefault('_', lambda x: x)
return self.lookup.get_template(template).render_unicode(**context)
+17 -16
View File
@@ -48,7 +48,7 @@
:tags:
:tickets:
types types types! still werent working....have to use TypeDecorator again :(
types types types! still weren't working....have to use TypeDecorator again :(
.. change::
:tags:
@@ -182,7 +182,7 @@
:tickets:
added a "mods" system which allows pluggable modules that modify/augment
core functionality, using the function "install_mods(*modnames)".
core functionality, using the function "install_mods(\*modnames)".
.. change::
:tags:
@@ -421,7 +421,7 @@
:tags:
:tickets:
added *args, **kwargs pass-thru to engine.transaction(func) allowing easier
added \*args, \**kwargs pass-thru to engine.transaction(func) allowing easier
creation of transactionalizing decorator functions
.. change::
@@ -477,7 +477,7 @@
:tickets:
fixed attributes bug where if an object is committed, its lazy-loaded list got
blown away if it hadnt been loaded
blown away if it hadn't been loaded
.. change::
:tags:
@@ -520,7 +520,7 @@
create_engine() now uses genericized parameters; host/hostname,
db/dbname/database, password/passwd, etc. for all engine connections. makes
engine URIs much more "universal"
engine URIs much more "universal"
.. change::
:tags:
@@ -672,7 +672,7 @@
:tags:
:tickets:
added 'get_session().invalidate(*obj)' method to objectstore, instances will
added 'get_session().invalidate(\*obj)' method to objectstore, instances will
refresh() themselves upon the next attribute access.
.. change::
@@ -805,7 +805,7 @@
:tags:
:tickets:
added "refresh(*obj)" method to objectstore / Session to reload the attributes of
added "refresh(\*obj)" method to objectstore / Session to reload the attributes of
any set of objects from the database unconditionally
.. changelog::
@@ -854,16 +854,17 @@
:tags:
:tickets:
two issues related to postgres, which doesnt want to give you the "lastrowid"
two issues related to postgres, which doesn't want to give you the "lastrowid"
since oids are deprecated:
* postgres database-side defaults that are on primary key cols *do* execute
explicitly beforehand, even though thats not the idea of a PassiveDefault. this is
because sequences on columns get reflected as PassiveDefaults, but need to be explicitly
executed on a primary key col so we know what we just inserted.
* if you did add a row that has a bunch of database-side defaults on it,
and the PassiveDefault thing was working the old way, i.e. they just execute on
the DB side, the "cant get the row back without an OID" exception that occurred
also will not happen unless someone (usually the ORM) explicitly asks for it.
* postgres database-side defaults that are on primary key cols *do* execute
explicitly beforehand, even though that's not the idea of a PassiveDefault. this is
because sequences on columns get reflected as PassiveDefaults, but need to be explicitly
executed on a primary key col so we know what we just inserted.
* if you did add a row that has a bunch of database-side defaults on it,
and the PassiveDefault thing was working the old way, i.e. they just execute on
the DB side, the "cant get the row back without an OID" exception that occurred
also will not happen unless someone (usually the ORM) explicitly asks for it.
.. change::
:tags:
+18 -18
View File
@@ -81,7 +81,7 @@
unit-of-work does a better check for "orphaned" objects that are
part of a "delete-orphan" cascade, for certain conditions where the
parent isnt available to cascade from.
parent isn't available to cascade from.
.. change::
:tags:
@@ -164,13 +164,13 @@
so far will convert this to "TIME[STAMP] (WITH|WITHOUT) TIME ZONE",
so that control over timezone presence is more controllable (psycopg2
returns datetimes with tzinfo's if available, which can create confusion
against datetimes that dont).
against datetimes that don't).
.. change::
:tags:
:tickets: 287
fix to using query.count() with distinct, **kwargs with SelectResults
fix to using query.count() with distinct, \**kwargs with SelectResults
count()
.. change::
@@ -203,7 +203,7 @@
turned on for individual table, schema, and column identifiers when
used in all queries/creates/drops. Enabled via "quote=True" in
Table or Column, as well as "quote_schema=True" in Table. Thanks to
Aaron Spike for his excellent efforts.
Aaron Spike for the excellent efforts.
.. change::
:tags:
@@ -229,8 +229,8 @@
with the session, and the INSERT statements are then sorted within the
mapper save_obj. the INSERT ordering has basically been pushed all
the way to the end of the flush cycle. that way the various sorts and
organizations occuring within UOWTask (particularly the circular task
sort) dont have to worry about maintaining order (which they werent anyway)
organizations occurring within UOWTask (particularly the circular task
sort) don't have to worry about maintaining order (which they weren't anyway)
.. change::
:tags:
@@ -392,7 +392,7 @@
:tags:
:tickets:
overhaul to MapperExtension calling scheme, wasnt working very well
overhaul to MapperExtension calling scheme, wasn't working very well
previously
.. change::
@@ -432,7 +432,7 @@
:tags:
:tickets:
select_table mappers *still* werent always compiling
select_table mappers *still* weren't always compiling
.. change::
:tags:
@@ -647,7 +647,7 @@
:tickets: 206
utterly remarkable: added a single space between 'CREATE TABLE'
and '(<the rest of it>' since *thats how MySQL indicates a non-
and '(<the rest of it>' since *that's how MySQL indicates a non-
reserved word tablename.....*
.. change::
@@ -743,7 +743,7 @@
of an attribute is no longer micromanaged with each change and is
instead part of a "CommittedState" object created when the
instance is first loaded. HistoryArraySet is gone, the behavior of
list attributes is now more open ended (i.e. theyre not sets anymore).
list attributes is now more open ended (i.e. they're not sets anymore).
.. change::
:tags:
@@ -757,7 +757,7 @@
:tickets:
fix to transaction control, so that repeated rollback() calls
dont fail (was failing pretty badly when flush() would raise
don't fail (was failing pretty badly when flush() would raise
an exception in a larger try/except transaction block)
.. change::
@@ -771,14 +771,14 @@
:tags:
:tickets:
fixed bug where tables with schema names werent getting indexed in
fixed bug where tables with schema names weren't getting indexed in
the MetaData object properly
.. change::
:tags:
:tickets: 207
fixed bug where Column with redefined "key" property wasnt getting
fixed bug where Column with redefined "key" property wasn't getting
type conversion happening in the ResultProxy
.. change::
@@ -792,7 +792,7 @@
:tickets:
fixed old bug where if a many-to-many table mapped as "secondary"
had extra columns, delete operations didnt work
had extra columns, delete operations didn't work
.. change::
:tags:
@@ -811,7 +811,7 @@
:tickets: 138
added NonExistentTable exception thrown when reflecting a table
that doesnt exist
that doesn't exist
.. change::
:tags:
@@ -1010,7 +1010,7 @@
:tags:
:tickets:
placeholder dispose() method added to SingletonThreadPool, doesnt
placeholder dispose() method added to SingletonThreadPool, doesn't
do anything yet
.. change::
@@ -1018,7 +1018,7 @@
:tickets:
rollback() is automatically called when an exception is raised,
but only if theres no transaction in process (i.e. works more like
but only if there's no transaction in process (i.e. works more like
autocommit).
.. change::
@@ -1114,7 +1114,7 @@
"oid" system has been totally moved into compile-time behavior;
if they are used in an order_by where they are not available, the order_by
doesnt get compiled, fixes
doesn't get compiled, fixes
.. change::
:tags:
+40 -41
View File
@@ -25,7 +25,7 @@
:tags: sql
:tickets: 768
dont assume join criterion consists only of column objects
don't assume join criterion consists only of column objects
.. change::
:tags: sql
@@ -305,7 +305,7 @@
:tags: sql
:tickets:
ForeignKey to a table in a schema thats not the default schema
ForeignKey to a table in a schema that's not the default schema
requires the schema to be explicit; i.e. ForeignKey('alt_schema.users.id')
.. change::
@@ -558,7 +558,7 @@
:tags: sqlite
:tickets: 603
string PK column inserts dont get overwritten with OID
string PK column inserts don't get overwritten with OID
.. change::
:tags: mssql
@@ -634,7 +634,7 @@
parenthesis are applied to clauses via a new _Grouping
construct. uses operator precedence to more intelligently apply
parenthesis to clauses, provides cleaner nesting of clauses
(doesnt mutate clauses placed in other clauses, i.e. no 'parens'
(doesn't mutate clauses placed in other clauses, i.e. no 'parens'
flag)
.. change::
@@ -648,7 +648,7 @@
:tags: sql
:tickets: 578
removed "no group by's in a select thats part of a UNION"
removed "no group by's in a select that's part of a UNION"
restriction
.. change::
@@ -666,7 +666,7 @@
:tags: orm
:tickets:
fixed bug in query.instances() that wouldnt handle more than
fixed bug in query.instances() that wouldn't handle more than
on additional mapper or one additional column.
.. change::
@@ -695,7 +695,7 @@
:tags: orm
:tickets:
session.get() and session.load() propagate **kwargs through to
session.get() and session.load() propagate \**kwargs through to
query
.. change::
@@ -923,7 +923,7 @@
means their lengths are dialect-dependent. So on oracle a label
that gets truncated to 30 chars will go out to 63 characters
on postgres. Also, the true labelname is always attached as the
accessor on the parent Selectable so theres no need to be aware
accessor on the parent Selectable so there's no need to be aware
of the "truncated" label names.
.. change::
@@ -941,7 +941,7 @@
:tickets: 513
the "mini" column labels generated when using subqueries, which
are to work around glitchy SQLite behavior that doesnt understand
are to work around glitchy SQLite behavior that doesn't understand
"foo.id" as equivalent to "id", are now only generated in the case
that those named columns are selected from (part of)
@@ -979,7 +979,7 @@
:tickets:
query strings in unicode URLs get keys encoded to ascii
for **kwargs compat
for \**kwargs compat
.. change::
:tags: sql
@@ -1113,14 +1113,14 @@
:tickets:
support for SSL arguments given as inline within URL query string,
prefixed with "ssl_", courtesy terjeros@gmail.com.
prefixed with "ssl\_", courtesy terjeros@gmail.com.
.. change::
:tags: <schemaname>, mysql
:tickets:
mysql uses "DESCRIBE.<tablename>", catching exceptions
if table doesnt exist, in order to determine if a table exists.
if table doesn't exist, in order to determine if a table exists.
this supports unicode table names as well as schema names. tested
with MySQL5 but should work with 4.1 series as well. (#557)
@@ -1266,7 +1266,7 @@
:tags: sql
:tickets:
the "else_" parameter to the case statement now properly works when
the "else\_" parameter to the case statement now properly works when
set to zero.
.. change::
@@ -1279,16 +1279,15 @@
and a new one returned with additional criterion added.
The new methods include:
filter() - applies select criterion to the query
filter_by() - applies "by"-style criterion to the query
avg() - return the avg() function on the given column
join() - join to a property (or across a list of properties)
outerjoin() - like join() but uses LEFT OUTER JOIN
limit()/offset() - apply LIMIT/OFFSET
range-based access which applies limit/offset:
session.query(Foo)[3:5]
distinct() - apply DISTINCT
list() - evaluate the criterion and return results
* filter() - applies select criterion to the query
* filter_by() - applies "by"-style criterion to the query
* avg() - return the avg() function on the given column
* join() - join to a property (or across a list of properties)
* outerjoin() - like join() but uses LEFT OUTER JOIN
* limit()/offset() - apply LIMIT/OFFSET range-based access
which applies limit/offset: session.query(Foo)[3:5]
* distinct() - apply DISTINCT
* list() - evaluate the criterion and return results
no incompatible changes have been made to Query's API and no methods
have been deprecated. Existing methods like select(), select_by(),
@@ -1321,7 +1320,7 @@
:tags: orm
:tickets:
strings and columns can also be sent to the *args of instances()
strings and columns can also be sent to the \*args of instances()
where those exact result columns will be part of the result tuples.
.. change::
@@ -1373,7 +1372,7 @@
more fixes to polymorphic relations, involving proper lazy-clause
generation on many-to-one relationships to polymorphic mappers. also fixes to detection of "direction", more specific
targeting of columns that belong to the polymorphic union vs. those
that dont.
that don't.
.. change::
:tags: orm
@@ -1469,7 +1468,7 @@
got binary working for any size input ! cx_oracle works fine,
it was my fault as BINARY was being passed and not BLOB for
setinputsizes (also unit tests werent even setting input sizes).
setinputsizes (also unit tests weren't even setting input sizes).
.. change::
:tags: oracle
@@ -1488,7 +1487,7 @@
:tags: mysql
:tickets:
added a catchall **kwargs to MSString, to help reflection of
added a catchall \**kwargs to MSString, to help reflection of
obscure types (like "varchar() binary" in MS 4.0)
.. change::
@@ -1526,7 +1525,7 @@
:tickets:
fixed argument passing to straight textual execute() on engine,
connection. can handle *args or a list instance for positional, **kwargs
connection. can handle \*args or a list instance for positional, \**kwargs
or a dict instance for named args, or a list of list or dicts to invoke
executemany()
@@ -1799,7 +1798,7 @@
:tags: orm, bugs
:tickets:
fix to deferred so that load operation doesnt mistakenly occur when only
fix to deferred so that load operation doesn't mistakenly occur when only
PK col attributes are set
.. change::
@@ -1949,7 +1948,7 @@
:tickets:
type system slightly modified to support TypeDecorators that can be
overridden by the dialect (ok, thats not very clear, it allows the mssql
overridden by the dialect (ok, that's not very clear, it allows the mssql
tweak below to be possible)
.. change::
@@ -1999,7 +1998,7 @@
:tickets: 420
mysql is inconsistent with what kinds of quotes it uses in foreign keys
during a SHOW CREATE TABLE, reflection updated to accomodate for all three
during a SHOW CREATE TABLE, reflection updated to accommodate for all three
styles
.. change::
@@ -2134,7 +2133,7 @@
:tags: orm
:tickets: 407
fixed bug in mapper refresh/expire whereby eager loaders didnt properly
fixed bug in mapper refresh/expire whereby eager loaders didn't properly
re-populate item lists
.. change::
@@ -2266,7 +2265,7 @@
:tickets:
MySQL detects errors 2006 (server has gone away) and 2014
(commands out of sync) and invalidates the connection on which it occured.
(commands out of sync) and invalidates the connection on which it occurred.
.. change::
:tags:
@@ -2305,7 +2304,7 @@
:tickets:
added onupdate and ondelete keyword arguments to ForeignKey; propagate
to underlying ForeignKeyConstraint if present. (dont propagate in the
to underlying ForeignKeyConstraint if present. (don't propagate in the
other direction, however)
.. change::
@@ -2364,7 +2363,7 @@
:tags: engine/pool
:tickets:
create_engine() reworked to be strict about incoming **kwargs. all keyword
create_engine() reworked to be strict about incoming \**kwargs. all keyword
arguments must be consumed by one of the dialect, connection pool, and engine
constructors, else a TypeError is thrown which describes the full set of
invalid kwargs in relation to the selected dialect/pool/engine configuration.
@@ -2416,9 +2415,9 @@
fixed bug in circular dependency sorting at flush time; if object A
contained a cyclical many-to-one relationship to object B, and object B
was just attached to object A, *but* object B itself wasnt changed,
was just attached to object A, *but* object B itself wasn't changed,
the many-to-one synchronize of B's primary key attribute to A's foreign key
attribute wouldnt occur.
attribute wouldn't occur.
.. change::
:tags: orm
@@ -2573,7 +2572,7 @@
a fair amount of cleanup to the schema package, removal of ambiguous
methods, methods that are no longer needed. slightly more constrained
useage, greater emphasis on explicitness
usage, greater emphasis on explicitness
.. change::
:tags: schema
@@ -2672,7 +2671,7 @@
:tags: connections/pooling/execution
:tickets:
fixed bug where Connection wouldnt lose its Transaction
fixed bug where Connection wouldn't lose its Transaction
after commit/rollback
.. change::
@@ -2745,7 +2744,7 @@
including the addition of a MutableType mixin which is implemented by
PickleType. unit-of-work now tracks the "dirty" list as an expression
of all persistent objects where the attribute manager detects changes.
The basic issue thats fixed is detecting changes on PickleType
The basic issue that's fixed is detecting changes on PickleType
objects, but also generalizes type handling and "modified" object
checking to be more complete and extensible.
@@ -2851,7 +2850,7 @@
implemented "version check" logic in Query/Mapper, used
when version_id_col is in effect and query.with_lockmode()
is used to get() an instance thats already loaded
is used to get() an instance that's already loaded
.. change::
:tags: orm
+34 -36
View File
@@ -83,14 +83,11 @@
:tickets:
Added "add()" and "add_all()" to scoped_session
methods. Workaround for 0.4.7:
methods. Workaround for 0.4.7::
from sqlalchemy.orm.scoping import ScopedSession,\
instrument
setattr(
ScopedSession, "add", instrument("add"))
setattr(
ScopedSession, "add_all", instrument("add_all"))
from sqlalchemy.orm.scoping import ScopedSession, instrument
setattr(ScopedSession, "add", instrument("add"))
setattr(ScopedSession, "add_all", instrument("add_all"))
.. change::
:tags: orm
@@ -344,7 +341,7 @@
:tags: orm
:tickets:
set-based collections |=, -=, ^= and &= are stricter about
set-based collections \|=, -=, ^= and &= are stricter about
their operands and only operate on sets, frozensets or
subclasses of the collection type. Previously, they would
accept any duck-typed set.
@@ -424,7 +421,7 @@
:tags: ext
:tickets:
set-based association proxies |=, -=, ^= and &= are
set-based association proxies \|=, -=, ^= and &= are
stricter about their operands and only operate on sets,
frozensets or other association proxies. Previously, they
would accept any duck-typed set.
@@ -541,11 +538,12 @@
The new approach also automatically allows eager loads
to work for subclasses, if they are present, for
example
example::
sess.query(Company).options(
eagerload_all(
))
to load Company objects, their employees, and the
'machines' collection of employees who happen to be
Engineers. A "with_polymorphic" Query option should be
@@ -561,7 +559,7 @@
is not carved in stone just yet: _values() and
_from_self(). We'd like feedback on these.
- _values(*columns) is given a list of column
- _values(\*columns) is given a list of column
expressions, and returns a new Query that only
returns those columns. When evaluated, the return
value is a list of tuples just like when using
@@ -594,7 +592,7 @@
:tickets:
query.order_by() and query.group_by() will accept
multiple arguments using *args (like select()
multiple arguments using \*args (like select()
already does).
.. change::
@@ -1526,7 +1524,7 @@
transaction directly to the parent of the transaction
that could be rolled back to. Now it rolls back the next
transaction up that can handle it, but sets the current
transaction to it's parent and inactivates the
transaction to its parent and inactivates the
transactions in between. Inactive transactions can only
be rolled back or closed, any other call results in an
error.
@@ -1535,7 +1533,7 @@
subtransactions.
- unitofwork flush didn't close the failed transaction
when the session was not in a transaction and commiting
when the session was not in a transaction and committing
the transaction failed.
.. change::
@@ -1564,7 +1562,7 @@
Better support for schemas in SQLite (linked in by ATTACH
DATABASE ... AS name). In some cases in the past, schema
names were ommitted from generated SQL for SQLite. This is
names were omitted from generated SQL for SQLite. This is
no longer the case.
.. change::
@@ -1780,7 +1778,7 @@
:tags: ext
:tickets:
'+', '*', '+=' and '*=' support for association
'+', '*', '+=' and '\*=' support for association
proxied lists.
.. change::
@@ -1866,7 +1864,7 @@
:tickets:
added new flag to String and create_engine(),
assert_unicode=(True|False|'warn'|None). Defaults to `False` or `None` on
assert_unicode=(True|False|'warn'\|None). Defaults to `False` or `None` on
create_engine() and String, `'warn'` on the Unicode type. When `True`,
results in all unicode conversion operations raising an exception when a
non-unicode bytestring is passed as a bind parameter. 'warn' results
@@ -2010,8 +2008,8 @@
:tickets: 908
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
__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
@@ -2818,10 +2816,10 @@
:tickets:
Improvements and fixes on Firebird reflection:
. FBDialect now mimics OracleDialect, regarding case-sensitivity of TABLE and
COLUMN names (see 'case_sensitive remotion' topic on this current file).
. FBDialect.table_names() doesn't bring system tables (ticket:796).
. FB now reflects Column's nullable property correctly.
* FBDialect now mimics OracleDialect, regarding case-sensitivity of TABLE and
COLUMN names (see 'case_sensitive remotion' topic on this current file).
* FBDialect.table_names() doesn't bring system tables (ticket:796).
* FB now reflects Column's nullable property correctly.
.. change::
:tags:
@@ -2963,7 +2961,7 @@
:tags:
:tickets:
Changed the API for the in_ operator. in_() now accepts a single argument
Changed the API for the in\_ operator. in_() now accepts a single argument
that is a sequence of values or a selectable. The old API of passing in
values as varargs still works but is deprecated.
@@ -3246,7 +3244,7 @@
:tags:
:tickets:
Tidied up what ends up in your namespace when you 'from sqlalchemy import *':
Tidied up what ends up in your namespace when you 'from sqlalchemy import \*':
.. change::
:tags:
@@ -3269,7 +3267,7 @@
:tags:
:tickets:
The 'Smallinteger' compatiblity name (small i!) is no longer imported,
The 'Smallinteger' compatibility name (small i!) is no longer imported,
but remains in schema.py for now. SmallInteger (big I!) is still
imported.
@@ -3630,7 +3628,7 @@
:tickets: 643
Class-level properties are now usable as query elements... no more
'.c.'! "Class.c.propname" is now superceded by "Class.propname". All
'.c.'! "Class.c.propname" is now superseded by "Class.propname". All
clause operators are supported, as well as higher level operators such
as Class.prop==<some instance> for scalar attributes,
Class.prop.contains(<some instance>) and Class.prop.any(<some
@@ -3816,10 +3814,10 @@
is represented by more than one column, when using the ORM. Objects of
the new type are fully functional in query expressions, comparisons,
query.get() clauses, etc. and act as though they are regular single-column
scalars... except they're not! Use the function composite(cls, *columns)
scalars... except they're not! Use the function composite(cls, \*columns)
inside of the mapper's "properties" dict, and instances of cls will be
created/mapped to a single attribute, comprised of the values correponding
to *columns.
created/mapped to a single attribute, comprised of the values corresponding
to \*columns.
.. change::
:tags: orm
@@ -3846,7 +3844,7 @@
Joined-table inheritance will now generate the primary key columns of all
inherited classes against the root table of the join only. This implies
that each row in the root table is distinct to a single instance. If for
some rare reason this is not desireable, explicit primary_key settings on
some rare reason this is not desirable, explicit primary_key settings on
individual mappers will override it.
.. change::
@@ -3904,7 +3902,7 @@
:tickets:
Speed! Clause compilation as well as the mechanics of SQL constructs have
been streamlined and simplified to a signficant degree, for a 20-30%
been streamlined and simplified to a significant degree, for a 20-30%
improvement of the statement construction/compilation overhead of 0.3.
.. change::
@@ -3912,7 +3910,7 @@
:tickets:
All "type" keyword arguments, such as those to bindparam(), column(),
Column(), and func.<something>(), renamed to "type_". Those objects still
Column(), and func.<something>(), renamed to "type\_". Those objects still
name their "type" attribute as "type".
.. change::
@@ -3922,7 +3920,7 @@
case_sensitive=(True|False) setting removed from schema items, since
checking this state added a lot of method call overhead and there was no
decent reason to ever set it to False. Table and column names which are
all lower case will be treated as case-insenstive (yes we adjust for
all lower case will be treated as case-insensitive (yes we adjust for
Oracle's UPPERCASE style too).
.. change::
@@ -4195,4 +4193,4 @@
Very rudimental support for OUT parameters added; use sql.outparam(name,
type) to set up an OUT parameter, just like bindparam(); after execution,
values are avaiable via result.out_parameters dictionary.
values are available via result.out_parameters dictionary.
+11 -11
View File
@@ -51,7 +51,7 @@
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,
ORM behavior only available in 0.6 via,
but is more correct at the SQL expression level
as well.
@@ -437,7 +437,7 @@
:tags: orm
:tickets: 1501
Fixed recursion issue which occured if a mapped object's
Fixed recursion issue which occurred if a mapped object's
`__len__()` or `__nonzero__()` method resulted in state
changes.
@@ -567,7 +567,7 @@
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,
configured explicitly (if they do in fact require this setting,
see the next note...).
.. change::
@@ -794,7 +794,7 @@
graph of mappers.
- Cached a wasteful "table sort" operation that previously
occured multiple times per flush, also removing significant
occurred multiple times per flush, also removing significant
method call count from flush().
- Other redundant behaviors have been simplified in
@@ -1093,7 +1093,7 @@
Session.scalar() now converts raw SQL strings to text()
the same way Session.execute() does and accepts same
alternative **kw args.
alternative \**kw args.
.. change::
:tags: orm
@@ -1325,7 +1325,7 @@
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
one-to-one via a foreign key or association 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.
@@ -1506,7 +1506,7 @@
:tickets:
ColumnProperty (and front-end helpers such as ``deferred``) no
longer ignores unknown **keyword arguments.
longer ignores unknown \**keyword arguments.
.. change::
:tags: orm
@@ -1557,7 +1557,7 @@
:tickets:
Column with no name (as in declarative) won't raise a
NoneType error when it's string output is requsted
NoneType error when its string output is requested
(such as in a stack trace).
.. change::
@@ -2903,7 +2903,7 @@
:tags: orm
:tickets:
The RowTuple object returned by Query(*cols) now features
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
@@ -2984,7 +2984,7 @@
:tickets: 1140
class.someprop.in_() raises NotImplementedError pending the
implementation of "in_" for relation
implementation of "in\_" for relation
.. change::
:tags: orm
@@ -3499,7 +3499,7 @@
Unicode, UnicodeText types now set "assert_unicode" and
"convert_unicode" by default, but accept overriding
**kwargs for these values.
\**kwargs for these values.
.. change::
:tags: sql
+42 -36
View File
@@ -837,7 +837,7 @@
The "unicode warning" against non-unicode bind data
is now raised only when the
Unicode type is used explictly; not when
Unicode type is used explicitly; not when
convert_unicode=True is used on the engine
or String type.
@@ -1013,7 +1013,7 @@
New Query methods: query.label(name), query.as_scalar(),
return the query's statement as a scalar subquery
with /without label;
query.with_entities(*ent), replaces the SELECT list of
query.with_entities(\*ent), replaces the SELECT list of
the query with new entities.
Roughly equivalent to a generative form of query.values()
which accepts mapped entities as well as column
@@ -1246,7 +1246,7 @@
:tags: sql
:tickets:
Added type_coerce(expr, type_) expression element.
Added type_coerce(expr, type\_) expression element.
Treats the given expression as the given type when evaluating
expressions and processing result rows, but does not
affect the generation of SQL, other than an anonymous
@@ -1517,7 +1517,7 @@
A warning is now emitted if a mapper is created against a
join or other single selectable that includes multiple
columns with the same name in its .c. collection,
and those columns aren't explictly named as part of
and those columns aren't explicitly named as part of
the same or separate attributes (or excluded).
In 0.7 this warning will be an exception. Note that
this warning is not emitted when the combination occurs
@@ -1813,7 +1813,7 @@
the _Label construct, i.e. the one that is produced
whenever you say somecol.label(), now counts itself
in its "proxy_set" unioned with that of it's
in its "proxy_set" unioned with that of its
contained column's proxy set, instead of
directly returning that of the contained column.
This allows column correspondence
@@ -1966,7 +1966,7 @@
:tags: examples
:tickets:
The beaker_caching example has been reorgnized
The beaker_caching example has been reorganized
such that the Session, cache manager,
declarative_base are part of environment, and
custom cache code is portable and now within
@@ -2948,7 +2948,7 @@
:tags: orm
:tickets:
To accomodate the fact that there are now two kinds of eager
To accommodate the fact that there are now two kinds of eager
loading available, the new names for eagerload() and
eagerload_all() are joinedload() and joinedload_all(). The
old names will remain as synonyms for the foreseeable future.
@@ -3005,7 +3005,7 @@
:tags: orm
:tickets:
Query gains an add_columns(*columns) method which is a multi-
Query gains an add_columns(\*columns) method which is a multi-
version of add_column(col). add_column(col) is future
deprecated.
@@ -3052,7 +3052,7 @@
:tags: postgresql
:tickets: 997
the TIME and TIMESTAMP types are now availble from the
the TIME and TIMESTAMP types are now available from the
postgresql dialect directly, which add the PG-specific
argument 'precision' to both. 'precision' and
'timezone' are correctly reflected for both TIME and
@@ -3165,7 +3165,7 @@
Fixed bug in session.rollback() which involved not removing
formerly "pending" objects from the session before
re-integrating "deleted" objects, typically occured with
re-integrating "deleted" objects, typically occurred with
natural primary keys. If there was a primary key conflict
between them, the attach of the deleted would fail
internally. The formerly "pending" objects are now expunged
@@ -3465,7 +3465,7 @@
the date/time/interval system created for Postgresql
EXTRACT in has now been generalized into
the type system. The previous behavior which often
occured of an expression "column + literal" forcing
occurred of an expression "column + literal" forcing
the type of "literal" to be the same as that of "column"
will now usually not occur - the type of
"literal" is first derived from the Python type of the
@@ -3641,9 +3641,9 @@
:tags: declarative
:tickets:
DeclarativeMeta exclusively uses cls.__dict__ (not dict_)
DeclarativeMeta exclusively uses cls.__dict__ (not dict\_)
as the source of class information; _as_declarative exclusively
uses the dict_ passed to it as the source of class information
uses the dict\_ passed to it as the source of class information
(which when using DeclarativeMeta is cls.__dict__). This should
in theory make it easier for custom metaclasses to modify
the state passed into _as_declarative.
@@ -3845,7 +3845,7 @@
:tickets:
For the full set of feature descriptions, see
http://www.sqlalchemy.org/trac/wiki/06Migration .
http://docs.sqlalchemy.org/en/latest/changelog/migration_06.html .
This document is a work in progress.
.. change::
@@ -4190,10 +4190,10 @@
* Passing a single list of elements to eagerload(),
eagerload_all(), contains_eager(), lazyload(),
defer(), and undefer() instead of multiple positional
*args is deprecated.
\*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.
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
@@ -4363,7 +4363,7 @@
"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.
as appropriate for more complex situations.
.. change::
:tags: sql
@@ -4523,7 +4523,7 @@
* 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,
\*args and \**kwargs which are passed to the given callable,
as well as the operating connection.
.. change::
@@ -4570,11 +4570,13 @@
Removed public mutability from Index and Constraint
objects:
- ForeignKeyConstraint.append_element()
- Index.append_column()
- UniqueConstraint.append_column()
- PrimaryKeyConstraint.add()
- PrimaryKeyConstraint.remove()
* ForeignKeyConstraint.append_element()
* Index.append_column()
* UniqueConstraint.append_column()
* PrimaryKeyConstraint.add()
* PrimaryKeyConstraint.remove()
These should be constructed declaratively (i.e. in one
construction).
@@ -4682,18 +4684,22 @@
The signature of the "on" callable passed to DDL() and
DDLElement() is revised as follows:
"ddl" - the DDLElement object itself.
"event" - the string event name.
"target" - previously "schema_item", the Table or
MetaData object triggering the event.
"connection" - the Connection object in use for the operation.
**kw - keyword arguments. In the case of MetaData before/after
create/drop, the list of Table objects for which
CREATE/DROP DDL is to be issued is passed as the kw
argument "tables". This is necessary for metadata-level
DDL that is dependent on the presence of specific tables.
ddl
the DDLElement object itself
event
the string event name.
target
previously "schema_item", the Table or MetaData object triggering the event.
connection
the Connection object in use for the operation.
\**kw
keyword arguments. In the case of MetaData before/after
create/drop, the list of Table objects for which
CREATE/DROP DDL is to be issued is passed as the kw
argument "tables". This is necessary for metadata-level
DDL that is dependent on the presence of specific tables.
- the "schema_item" attribute of DDL has been renamed to
The "schema_item" attribute of DDL has been renamed to
"target".
.. change::
@@ -4822,7 +4828,7 @@
postgresql://scott:tiger@localhost/test
postgresql+pg8000://scott:tiger@localhost/test
The "postgres" name remains for backwards compatiblity
The "postgres" name remains for backwards compatibility
in the following ways:
- There is a "postgres.py" dummy dialect which
@@ -4994,7 +5000,7 @@
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"
and in the case of TIMESTAMP explicitly renders "NULL"
due to MySQL's "switching" of default nullability
for TIMESTAMP columns.
+125 -41
View File
@@ -7,8 +7,61 @@
:version: 0.7.11
.. change::
:tags: bug, orm
:tickets: 2699
:tags: bug, engine
:tickets: 2851
:versions: 0.8.3, 0.9.0b1
The regexp used by the :func:`~sqlalchemy.engine.url.make_url` function now parses
ipv6 addresses, e.g. surrounded by brackets.
.. change::
:tags: bug, orm
:tickets: 2807
:versions: 0.8.3, 0.9.0b1
Fixed bug where list instrumentation would fail to represent a
setslice of ``[0:0]`` correctly, which in particular could occur
when using ``insert(0, item)`` with the association proxy. Due
to some quirk in Python collections, the issue was much more likely
with Python 3 rather than 2.
.. change::
:tags: bug, sql
:tickets: 2801
:versions: 0.8.3, 0.9.0b1
Fixed regression dating back to 0.7.9 whereby the name of a CTE might
not be properly quoted if it was referred to in multiple FROM clauses.
.. change::
:tags: mysql, bug
:tickets: 2791
:versions: 0.8.3, 0.9.0b1
Updates to MySQL reserved words for versions 5.5, 5.6, courtesy
Hanno Schlichting.
.. change::
:tags: sql, bug, cte
:tickets: 2783
:versions: 0.8.3, 0.9.0b1
Fixed bug in common table expression system where if the CTE were
used only as an ``alias()`` construct, it would not render using the
WITH keyword.
.. change::
:tags: bug, sql
:tickets: 2784
:versions: 0.8.3, 0.9.0b1
Fixed bug in :class:`.CheckConstraint` DDL where the "quote" flag from a
:class:`.Column` object would not be propagated.
.. change::
:tags: bug, orm
:tickets: 2699
:versions: 0.8.1
Fixed bug when a query of the form:
``query(SubClass).options(subqueryload(Baseclass.attrname))``,
@@ -23,6 +76,7 @@
.. change::
:tags: bug, orm
:tickets: 2689
:versions: 0.8.1
Fixed bug in unit of work whereby a joined-inheritance
subclass could insert the row for the "sub" table
@@ -32,6 +86,7 @@
.. change::
:tags: feature, postgresql
:tickets: 2676
:versions: 0.8.0
Added support for Postgresql's traditional SUBSTRING
function syntax, renders as "SUBSTRING(x FROM y FOR z)"
@@ -73,6 +128,27 @@
:version: 0.7.10
:released: Thu Feb 7 2013
.. change::
:tags: engine, bug
:tickets: 2604
:versions: 0.8.0b2
Fixed :meth:`.MetaData.reflect` to correctly use
the given :class:`.Connection`, if given, without
opening a second connection from that connection's
:class:`.Engine`.
.. change::
:tags: mssql, bug
:tickets:2607
:versions: 0.8.0b2
Fixed bug whereby using "key" with Column
in conjunction with "schema" for the owning
Table would fail to locate result rows due
to the MSSQL dialect's "schema rendering"
logic's failure to take .key into account.
.. change::
:tags: sql, mysql, gae
:tickets: 2649
@@ -131,6 +207,7 @@
.. change::
:tags: sqlite, bug
:tickets: 2568
:versions: 0.8.0b2
More adjustment to this SQLite related issue which was released in
0.7.9, to intercept legacy SQLite quoting characters when reflecting
@@ -141,6 +218,7 @@
.. change::
:tags: sql, bug
:tickets: 2631
:versions: 0.8.0b2
Fixed bug where using server_onupdate=<FetchedValue|DefaultClause>
without passing the "for_update=True" flag would apply the default
@@ -421,7 +499,7 @@
:tickets: 2529
Added gaerdbms import to mysql/__init__.py,
the absense of which was preventing the new
the absence of which was preventing the new
GAE dialect from being loaded.
.. change::
@@ -1131,12 +1209,12 @@
:tickets:
Added some decent context managers
to Engine, Connection:
to Engine, Connection::
with engine.begin() as conn:
<work with conn in a transaction>
and:
and::
with engine.connect() as conn:
<work with conn>
@@ -1387,10 +1465,10 @@
:tickets: 2361
Dialect-specific compilers now raise
CompileException for all type/statement compilation
CompileError for all type/statement compilation
issues, instead of InvalidRequestError or ArgumentError.
The DDL for CREATE TABLE will re-raise
CompileExceptions to include table/column information
CompileError to include table/column information
for the problematic column.
.. change::
@@ -1721,10 +1799,10 @@
polymorphic_on now accepts many
new kinds of values:
- standalone expressions that aren't
* standalone expressions that aren't
otherwise mapped
- column_property() objects
- string names of any column_property()
* column_property() objects
* string names of any column_property()
or attribute name of a mapped Column
The docs include an example using
@@ -1890,7 +1968,7 @@
:tickets: 1679
a "has_schema" method has been implemented
on dialect, but only works on Postgresql so far.
on dialect, but only works on Postgresql so far.
Courtesy Manlio Perillo.
.. change::
@@ -2117,7 +2195,7 @@
Enhanced the instrumentation in the ORM to support
Py3K's new argument style of "required kw arguments",
i.e. fn(a, b, *, c, d), fn(a, b, *args, c, d).
i.e. fn(a, b, \*, c, d), fn(a, b, \*args, c, d).
Argument signatures of mapped object's __init__
method will be preserved, including required kw rules.
@@ -2139,8 +2217,9 @@
Fixed a variety of synonym()-related regressions
from 0.6:
- making a synonym against a synonym now works.
- synonyms made against a relationship() can
* making a synonym against a synonym now works.
* synonyms made against a relationship() can
be passed to query.join(), options sent
to query.options(), passed by name
to query.with_parent().
@@ -2249,19 +2328,20 @@
:tickets: 2239
New declarative features:
- __declare_last__() method, establishes an event
listener for the class method that will be called
when mappers are completed with the final "configure"
step.
- __abstract__ flag. The class will not be mapped
at all when this flag is present on the class.
- New helper classes ConcreteBase, AbstractConcreteBase.
Allow concrete mappings using declarative which automatically
set up the "polymorphic_union" when the "configure"
mapper step is invoked.
- The mapper itself has semi-private methods that allow
the "with_polymorphic" selectable to be assigned
to the mapper after it has already been configured.
* __declare_last__() method, establishes an event
listener for the class method that will be called
when mappers are completed with the final "configure"
step.
* __abstract__ flag. The class will not be mapped
at all when this flag is present on the class.
* New helper classes ConcreteBase, AbstractConcreteBase.
Allow concrete mappings using declarative which automatically
set up the "polymorphic_union" when the "configure"
mapper step is invoked.
* The mapper itself has semi-private methods that allow
the "with_polymorphic" selectable to be assigned
to the mapper after it has already been configured.
.. change::
:tags: orm
@@ -2799,7 +2879,7 @@
:tickets: 2206
Fixed bug whereby adaptation of old append_ddl_listener()
function was passing unexpected **kw through
function was passing unexpected \**kw through
to the Table event. Table gets no kws, the MetaData
event in 0.6 would get "tables=somecollection",
this behavior is preserved.
@@ -3046,7 +3126,7 @@
:tickets:
Streamlined the process by which a Select
determines what's in it's '.c' collection.
determines what's in its '.c' collection.
Behaves identically, except that a
raw ClauseList() passed to select([])
(which is not a documented case anyway) will
@@ -3140,7 +3220,7 @@
This section documents those changes from 0.7b4
to 0.7.0. For an overview of what's new in
SQLAlchemy 0.7, see
http://www.sqlalchemy.org/trac/wiki/07Migration
http://docs.sqlalchemy.org/en/latest/changelog/migration_07.html
.. change::
:tags: orm
@@ -3437,7 +3517,7 @@
:tags: orm
:tickets: 2122
Some fixes to "evaulate" and "fetch" evaluation
Some fixes to "evaluate" and "fetch" evaluation
when query.update(), query.delete() are called.
The retrieval of records is done after autoflush
in all cases, and before update/delete is
@@ -3601,7 +3681,7 @@
:tags: schema
:tickets: 2109
The 'useexisting' flag on Table has been superceded
The 'useexisting' flag on Table has been superseded
by a new pair of flags 'keep_existing' and
'extend_existing'. 'extend_existing' is equivalent
to 'useexisting' - the existing Table is returned,
@@ -4004,7 +4084,7 @@
The Column.references() method now returns True
if it has a foreign key referencing the
given column exactly, not just it's parent
given column exactly, not just its parent
table.
.. change::
@@ -4045,13 +4125,13 @@
Detailed descriptions of each change below are
described at:
http://www.sqlalchemy.org/trac/wiki/07Migration
http://docs.sqlalchemy.org/en/latest/changelog/migration_07.html
.. change::
:tags: general
:tickets: 1902
New event system, supercedes all extensions, listeners,
New event system, supersedes all extensions, listeners,
etc.
.. change::
@@ -4087,7 +4167,7 @@
:tags: orm
:tickets: 1903
Hybrid Attributes, implements/supercedes synonym()
Hybrid Attributes, implements/supersedes synonym()
.. change::
:tags: orm
@@ -4099,7 +4179,11 @@
:tags: orm
:tickets:
Mutation Event Extension, supercedes "mutable=True"
Mutation Event Extension, supersedes "mutable=True"
.. seealso::
:ref:`07_migration_mutation_extension`
.. change::
:tags: orm
@@ -4264,7 +4348,7 @@
:tickets: 1069
Query.distinct() now accepts column expressions
as *args, interpreted by the Postgresql dialect
as \*args, interpreted by the Postgresql dialect
as DISTINCT ON (<expr>).
.. change::
@@ -4364,7 +4448,7 @@
:tickets: 1069
select.distinct() now accepts column expressions
as *args, interpreted by the Postgresql dialect
as \*args, interpreted by the Postgresql dialect
as DISTINCT ON (<expr>). Note this was already
available via passing a list to the `distinct`
keyword argument to select().
@@ -4374,7 +4458,7 @@
:tickets:
select.prefix_with() accepts multiple expressions
(i.e. *expr), 'prefix' keyword argument to select()
(i.e. \*expr), 'prefix' keyword argument to select()
accepts a list or tuple.
.. change::
@@ -4446,7 +4530,7 @@
execution_options() on Connection accepts
"isolation_level" argument, sets transaction isolation
level for that connection only until returned to the
connection pool, for thsoe backends which support it
connection pool, for those backends which support it
(SQLite, Postgresql)
.. change::
+1336 -93
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -305,7 +305,7 @@ So what happens when we say:
Notice the nice clean alias names too. The joining doesn't
care if it's against the same immediate table or some other
object which then cycles back to the beginining. Any kind
object which then cycles back to the beginning. Any kind
of chain of eager loads can cycle back onto itself when
``join_depth`` is specified. When not present, eager
loading automatically stops when it hits a cycle.
@@ -749,10 +749,10 @@ Just like it says:
b = bindparam('foo', type_=String)
in_ Function Changed to Accept Sequence or Selectable
-----------------------------------------------------
in\_ Function Changed to Accept Sequence or Selectable
------------------------------------------------------
The in_ function now takes a sequence of values or a
The in\_ function now takes a sequence of values or a
selectable as its sole argument. The previous API of passing
in values as positional arguments still works, but is now
deprecated. This means that
+2 -2
View File
@@ -338,7 +338,7 @@ extend the ORM. Heres a summary:
* **SessionExtension.** - This is an easy to use extension
class for session events. In particular, it provides
``before_flush()``, ``after_flush()`` and
``after_flush_postexec()`` methods. It's usage is
``after_flush_postexec()`` methods. This usage is
recommended over ``MapperExtension.before_XXX`` in many
cases since within ``before_flush()`` you can modify the
flush plan of the session freely, something which cannot
@@ -545,7 +545,7 @@ Removed
* **get()/load() cleanup**
The ``load()`` method has been removed. It's
The ``load()`` method has been removed. Its
functionality was kind of arbitrary and basically copied
from Hibernate, where it's also not a particularly
meaningful method.
+1 -1
View File
@@ -935,7 +935,7 @@ parent rows.
To make room for the new subquery load feature, the existing
```eagerload()````/````eagerload_all()```` options are now
superceded by ````joinedload()```` and
superseded by ````joinedload()```` and
````joinedload_all()````. The old names will hang around
for the foreseeable future just like ````relation()```.
+5 -5
View File
@@ -278,18 +278,18 @@ unchanged:
:ticket:`1923`
.. _07_migration_mutation_extension:
Mutation event extension, supersedes "mutable=True"
---------------------------------------------------
A new extension, `Mutation Tracking <http://www.sqlalchemy.o
rg/docs/07/orm/extensions/mutable.html>`_, provides a
A new extension, :ref:`mutable_toplevel`, provides a
mechanism by which user-defined datatypes can provide change
events back to the owning parent or parents. The extension
includes an approach for scalar database values, such as
those managed by ``PickleType``, ``postgresql.ARRAY``, or
those managed by :class:`.PickleType`, ``postgresql.ARRAY``, or
other custom ``MutableType`` classes, as well as an approach
for ORM "composites", those configured using :ref:`composite()
<mapper_composite>`_.
for ORM "composites", those configured using :func:`~.sqlalchemy.orm.composite`.
.. seealso::
+27 -3
View File
@@ -632,7 +632,7 @@ not really a column, CAST operator, or SQL function that
really drives what kinds of operations are present, it's the
*type* of the expression. The implementation details are
minimal - only a few extra methods are added to the core
:class:`.ColumnElement` type so that it consults it's
:class:`.ColumnElement` type so that it consults its
:class:`.TypeEngine` object for an optional set of operators.
New or revised operations can be associated with any type,
either via subclassing of an existing type, by using
@@ -683,6 +683,30 @@ as more string, integer and date operators.
:ticket:`2547`
.. _feature_2623:
Multiple-VALUES support for Insert
-----------------------------------
The :meth:`.Insert.values` method now supports a list of dictionaries,
which will render a multi-VALUES statement such as
``VALUES (<row1>), (<row2>), ...``. This is only relevant to backends which
support this syntax, including Postgresql, SQLite, and MySQL. It is
not the same thing as the usual ``executemany()`` style of INSERT which
remains unchanged::
users.insert().values([
{"name": "some name"},
{"name": "some other name"},
{"name": "yet another name"},
])
.. seealso::
:meth:`.Insert.values`
:ticket:`2623`
Type Expressions
-----------------
@@ -1176,7 +1200,7 @@ Prior to this change, the above would return::
which is invalid SQL as "t1" is not referred to in any FROM clause.
Now, in the absense of an enclosing SELECT, it returns::
Now, in the absence of an enclosing SELECT, it returns::
SELECT t1.x, t2.y FROM t1, t2
@@ -1235,7 +1259,7 @@ No more magic coercion of "=" to IN when comparing to subquery in MS-SQL
------------------------------------------------------------------------
We found a very old behavior in the MSSQL dialect which
would attempt to rescue the user from his or herself when
would attempt to rescue users from themselves when
doing something like this:
::
+9 -6
View File
@@ -34,7 +34,8 @@ import sqlalchemy
extensions = [
'sphinx.ext.autodoc',
'builder.autodoc_mods',
'builder.changelog',
'changelog',
'sphinx_paramlinks',
'builder.dialect_info',
'builder.mako',
'builder.sqlformatter',
@@ -74,7 +75,7 @@ master_doc = 'contents'
# General information about the project.
project = u'SQLAlchemy'
copyright = u'2007-2013, the SQLAlchemy authors and contributors'
copyright = u'2007-2014, 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
@@ -83,11 +84,11 @@ copyright = u'2007-2013, the SQLAlchemy authors and contributors'
# The short X.Y version.
version = "0.8"
# The full version, including alpha/beta/rc tags.
release = "0.8.1"
release = "0.8.7"
release_date = "April 27, 2013"
release_date = "July 22, 2014"
site_base = "http://www.sqlalchemy.org"
site_base = os.environ.get("RTD_SITE_BASE", "http://www.sqlalchemy.org")
# arbitrary number recognized by builders.py, incrementing this
# will force a rebuild
@@ -197,9 +198,11 @@ html_domain_indices = False
# If true, the reST sources are included in the HTML build as _sources/<name>.
#html_copy_source = True
html_copy_source = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
html_show_sourcelink = False
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
@@ -280,7 +283,7 @@ man_pages = [
epub_title = u'SQLAlchemy'
epub_author = u'SQLAlchemy authors'
epub_publisher = u'SQLAlchemy authors'
epub_copyright = u'2013, SQLAlchemy authors'
epub_copyright = u'2014, SQLAlchemy authors'
# The language of the text. It defaults to the language option
# or en if the language is not set.
+3 -1
View File
@@ -1,10 +1,12 @@
:orphan:
====================
Appendix: Copyright
====================
This is the MIT license: `<http://www.opensource.org/licenses/mit-license.php>`_
Copyright (c) 2005-2013 Michael Bayer and contributors.
Copyright (c) 2005-2014 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
-6
View File
@@ -495,19 +495,15 @@ Connection / Engine API
=======================
.. autoclass:: Connection
:show-inheritance:
:members:
.. autoclass:: Connectable
:show-inheritance:
:members:
.. autoclass:: Engine
:show-inheritance:
:members:
.. autoclass:: NestedTransaction
:show-inheritance:
:members:
.. autoclass:: sqlalchemy.engine.ResultProxy
@@ -517,10 +513,8 @@ Connection / Engine API
:members:
.. autoclass:: Transaction
:show-inheritance:
:members:
.. autoclass:: TwoPhaseTransaction
:show-inheritance:
:members:
+385
View File
@@ -0,0 +1,385 @@
.. _metadata_constraints_toplevel:
.. _metadata_constraints:
.. module:: sqlalchemy.schema
=================================
Defining Constraints and Indexes
=================================
.. _metadata_foreignkeys:
This section will discuss SQL :term:`constraints` and indexes. In SQLAlchemy
the key classes include :class:`.ForeignKeyConstraint` and :class:`.Index`.
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 :class:`~sqlalchemy.schema.ForeignKey` object
as an argument to a :class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.schema.Column` object, which as we'll
see later is accessed from an existing :class:`~sqlalchemy.schema.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
:class:`~sqlalchemy.schema.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
:class:`~sqlalchemy.schema.ForeignKeyConstraint` is the only way to define a
composite foreign key. While we could also have placed individual
:class:`~sqlalchemy.schema.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.
.. _use_alter:
Creating/Dropping Foreign Key Constraints via ALTER
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In all the above examples, the :class:`~sqlalchemy.schema.ForeignKey` object
causes the "REFERENCES" keyword to be added inline to a column definition
within a "CREATE TABLE" statement when
:func:`~sqlalchemy.schema.MetaData.create_all` is issued, and
:class:`~sqlalchemy.schema.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, :class:`~sqlalchemy.schema.ForeignKey` and
:class:`~sqlalchemy.schema.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_on_delete:
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
:class:`~sqlalchemy.schema.ForeignKey` and
:class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.schema.Column`. Explicitly named
unique constraints and/or those with multiple columns are created via the
:class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.schema.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.
.. 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}
Setting up Constraints when using the Declarative ORM Extension
----------------------------------------------------------------
The :class:`.Table` is the SQLAlchemy Core construct that allows one to define
table metadata, which among other things can be used by the SQLAlchemy ORM
as a target to map a class. The :ref:`Declarative <declarative_toplevel>`
extension allows the :class:`.Table` object to be created automatically, given
the contents of the table primarily as a mapping of :class:`.Column` objects.
To apply table-level constraint objects such as :class:`.ForeignKeyConstraint`
to a table defined using Declarative, use the ``__table_args__`` attribute,
described at :ref:`declarative_table_args`.
Constraints API
---------------
.. autoclass:: Constraint
.. autoclass:: CheckConstraint
.. autoclass:: ColumnCollectionConstraint
.. autoclass:: ForeignKey
:members:
.. autoclass:: ForeignKeyConstraint
:members:
.. autoclass:: PrimaryKeyConstraint
.. autoclass:: UniqueConstraint
.. _schema_indexes:
Indexes
-------
Indexes can be created anonymously (using an auto-generated name ``ix_<column
label>``) for a single column using the inline ``index`` keyword on
:class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.schema.Index` construct,
which requires a name.
Below we illustrate a :class:`~sqlalchemy.schema.Table` with several
:class:`~sqlalchemy.schema.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}
Note in the example above, the :class:`.Index` construct is created
externally to the table which it corresponds, using :class:`.Column`
objects directly. :class:`.Index` also supports
"inline" definition inside the :class:`.Table`, using string names to
identify columns::
meta = MetaData()
mytable = Table('mytable', meta,
Column('col1', Integer),
Column('col2', Integer),
Column('col3', Integer),
Column('col4', Integer),
# place an index on col1, col2
Index('idx_col12', 'col1', 'col2'),
# place a unique index on col3, col4
Index('idx_col34', 'col3', 'col4', unique=True)
)
.. versionadded:: 0.7
Support of "inline" definition inside the :class:`.Table`
for :class:`.Index`\ .
The :class:`~sqlalchemy.schema.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}
.. _schema_indexes_functional:
Functional Indexes
~~~~~~~~~~~~~~~~~~~
:class:`.Index` supports SQL and function expressions, as supported by the
target backend. To create an index against a column using a descending
value, the :meth:`.ColumnElement.desc` modifier may be used::
from sqlalchemy import Index
Index('someindex', mytable.c.somecol.desc())
Or with a backend that supports functional indexes such as Postgresql,
a "case insensitive" index can be created using the ``lower()`` function::
from sqlalchemy import func, Index
Index('someindex', func.lower(mytable.c.somecol))
.. versionadded:: 0.8 :class:`.Index` supports SQL expressions and functions
as well as plain columns.
Index API
---------
.. autoclass:: Index
:members:
+287
View File
@@ -0,0 +1,287 @@
.. _metadata_ddl_toplevel:
.. _metadata_ddl:
.. module:: sqlalchemy.schema
Customizing DDL
===============
In the preceding sections we've discussed a variety of schema constructs
including :class:`~sqlalchemy.schema.Table`,
:class:`~sqlalchemy.schema.ForeignKeyConstraint`,
:class:`~sqlalchemy.schema.CheckConstraint`, and
:class:`~sqlalchemy.schema.Sequence`. Throughout, we've relied upon the
``create()`` and :func:`~sqlalchemy.schema.MetaData.create_all` methods of
:class:`~sqlalchemy.schema.Table` and :class:`~sqlalchemy.schema.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.
.. _schema_ddl_sequences:
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 :class:`~sqlalchemy.schema.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
:class:`~sqlalchemy.schema.Table` or :class:`~sqlalchemy.schema.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
:class:`~sqlalchemy.schema.CheckConstraint` will also be issued inline within
the table definition.
The :class:`~sqlalchemy.schema.CheckConstraint` construct can also be
constructed externally and associated with the
:class:`~sqlalchemy.schema.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 :class:`.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
from sqlalchemy import event
event.listen(
users,
"after_create",
AddConstraint(constraint)
)
event.listen(
users,
"before_drop",
DropConstraint(constraint)
)
{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
:meth:`.DDLElement.execute_if` method. This method returns a modified form of
the DDL callable which will filter on criteria before responding to a
received event. It accepts a parameter ``dialect``, which is the string
name of a dialect or a tuple of such, which will limit the execution of the
item to just those dialects. It also accepts a ``callable_`` parameter which
may reference a Python callable which will be invoked upon event reception,
returning ``True`` or ``False`` indicating if the event should proceed.
If our :class:`~sqlalchemy.schema.CheckConstraint` was only supported by
Postgresql and not other databases, we could limit its usage to just that dialect::
event.listen(
users,
'after_create',
AddConstraint(constraint).execute_if(dialect='postgresql')
)
event.listen(
users,
'before_drop',
DropConstraint(constraint).execute_if(dialect='postgresql')
)
Or to any set of dialects::
event.listen(
users,
"after_create",
AddConstraint(constraint).execute_if(dialect=('postgresql', 'mysql'))
)
event.listen(
users,
"before_drop",
DropConstraint(constraint).execute_if(dialect=('postgresql', 'mysql'))
)
When using a callable, the callable is passed the ddl element, the
:class:`.Table` or :class:`.MetaData`
object whose "create" or "drop" event is in progress, and the
:class:`.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, 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, target, connection, **kw):
return not should_create(ddl, target, connection, **kw)
event.listen(
users,
"after_create",
AddConstraint(constraint).execute_if(callable_=should_create)
)
event.listen(
users,
"before_drop",
DropConstraint(constraint).execute_if(callable_=should_drop)
)
{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
event.listen(
metadata,
"after_create",
DDL("ALTER TABLE users ADD CONSTRAINT "
"cst_user_name_length "
" CHECK (length(user_name) >= 8)")
)
A more comprehensive method of creating libraries of DDL constructs is to use
custom compilation - see :ref:`sqlalchemy.ext.compiler_toplevel` for
details.
.. _schema_api_ddl:
DDL Expression Constructs API
-----------------------------
.. autoclass:: DDLElement
:members:
:undoc-members:
.. autoclass:: DDL
:members:
:undoc-members:
.. autoclass:: CreateTable
:members:
:undoc-members:
.. autoclass:: DropTable
:members:
:undoc-members:
.. autoclass:: CreateColumn
:members:
:undoc-members:
.. autoclass:: CreateSequence
:members:
:undoc-members:
.. autoclass:: DropSequence
:members:
:undoc-members:
.. autoclass:: CreateIndex
:members:
:undoc-members:
.. autoclass:: DropIndex
:members:
:undoc-members:
.. autoclass:: AddConstraint
:members:
:undoc-members:
.. autoclass:: DropConstraint
:members:
:undoc-members:
.. autoclass:: CreateSchema
:members:
:undoc-members:
.. autoclass:: DropSchema
:members:
:undoc-members:
+345
View File
@@ -0,0 +1,345 @@
.. _metadata_defaults_toplevel:
.. _metadata_defaults:
.. module:: sqlalchemy.schema
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
:class:`~sqlalchemy.schema.Column` including the ``autoincrement`` flag, as
well as the section on :class:`~sqlalchemy.schema.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
:class:`~sqlalchemy.sql.expression.Insert()` or
:class:`~sqlalchemy.sql.expression.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
:class:`~sqlalchemy.engine.ResultProxy` will contain a collection
accessible via ``result.postfetch_cols()`` which contains a list of all
:class:`~sqlalchemy.schema.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
:class:`~sqlalchemy.engine.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:
Triggered Columns
------------------
Columns with values set by a database trigger or other external process may be
called out using :class:`.FetchedValue` as a marker::
t = Table('test', meta,
Column('abc', String(20), server_default=FetchedValue()),
Column('def', String(20), server_onupdate=FetchedValue())
)
.. versionchanged:: 0.8.0b2,0.7.10
The ``for_update`` argument on :class:`.FetchedValue` is set automatically
when specified as the ``server_onupdate`` argument. If using an older version,
specify the onupdate above as ``server_onupdate=FetchedValue(for_update=True)``.
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.
.. note::
It's generally not appropriate to use :class:`.FetchedValue` in
conjunction with a primary key column, particularly when using the
ORM or any other scenario where the :attr:`.ResultProxy.inserted_primary_key`
attribute is required. This is becaue the "post-fetch" operation requires
that the primary key value already be available, so that the
row can be selected on its primary key.
For a server-generated primary key value, all databases provide special
accessors or other techniques in order to acquire the "last inserted
primary key" column of a table. These mechanisms aren't affected by the presence
of :class:`.FetchedValue`. For special situations where triggers are
used to generate primary key values, and the database in use does not
support the ``RETURNING`` clause, it may be necessary to forego the usage
of the trigger and instead apply the SQL expression or function as a
"pre execute" expression::
t = Table('test', meta,
Column('abc', MyType, default=func.generate_new_value(), primary_key=True)
)
Where above, when :meth:`.Table.insert` is used,
the ``func.generate_new_value()`` expression will be pre-executed
in the context of a scalar ``SELECT`` statement, and the new value will
be applied to the subsequent ``INSERT``, while at the same time being
made available to the :attr:`.ResultProxy.inserted_primary_key`
attribute.
Defining Sequences
-------------------
SQLAlchemy represents database sequences using the
:class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.schema.Sequence` object is
otherwise ignored.
The :class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.schema.Table` object defines a
:class:`~sqlalchemy.schema.Sequence` on its primary key column so that it
works with Oracle and Firebird, the :class:`~sqlalchemy.schema.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
:class:`~sqlalchemy.schema.Sequence` object - this indicates that the
:class:`~sqlalchemy.schema.Sequence` should only be used if the database
provides no other option for generating primary key identifiers.
The :class:`~sqlalchemy.schema.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)
Default Objects API
-------------------
.. autoclass:: ColumnDefault
.. autoclass:: DefaultClause
.. autoclass:: DefaultGenerator
.. autoclass:: FetchedValue
.. autoclass:: PassiveDefault
.. autoclass:: Sequence
:members:
+35
View File
@@ -0,0 +1,35 @@
Insert, Updates, Deletes
========================
INSERT, UPDATE and DELETE statements build on a hierarchy starting
with :class:`.UpdateBase`. The :class:`.Insert` and :class:`.Update`
constructs build on the intermediary :class:`.ValuesBase`.
.. module:: sqlalchemy.sql.expression
.. autofunction:: delete
.. autofunction:: insert
.. autofunction:: update
.. autoclass:: Delete
:members:
:inherited-members:
.. autoclass:: Insert
:members:
:inherited-members:
.. autoclass:: Update
:members:
:inherited-members:
.. autoclass:: sqlalchemy.sql.expression.UpdateBase
:members:
:inherited-members:
.. autoclass:: sqlalchemy.sql.expression.ValuesBase
:members:
+55 -43
View File
@@ -39,7 +39,6 @@ covers the details of configuring an :class:`.Engine`. The next section, :ref:
will detail the usage API of the :class:`.Engine` and similar, typically for non-ORM
applications.
.. _supported_dbapis:
Supported Databases
@@ -51,46 +50,36 @@ of others require an additional install of a separate dialect.
See the section :ref:`dialect_toplevel` for information on the various backends available.
.. _create_engine_args:
Engine Creation API
===================
Keyword options can also be specified to :func:`~sqlalchemy.create_engine`,
following the string URL as follows:
.. sourcecode:: python+sql
db = create_engine('postgresql://...', encoding='latin1', echo=True)
.. autofunction:: sqlalchemy.create_engine
.. autofunction:: sqlalchemy.engine_from_config
.. _database_urls:
Database Urls
=============
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::
The :func:`.create_engine` function produces an :class:`.Engine` object based
on a URL. These URLs follow `RFC-1738
<http://rfc.net/rfc1738.html>`_, and usually can include username, password,
hostname, database name as well as optional keyword arguments for additional configuration.
In some cases a file path is accepted, and in others a "data source name" replaces
the "host" and "database" portions. The typical form of a database 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
Dialect names include the identifying name of the SQLAlchemy dialect,
a name such as ``sqlite``, ``mysql``, ``postgresql``, ``oracle``, or ``mssql``.
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.
known driver available for that backend.
Examples for common connection styles follow below. For a full index of
detailed information on all included dialects as well as links to third-party dialects, see
:ref:`dialect_toplevel`.
Postgresql
----------
The Postgresql dialect uses psycopg2 as the default DBAPI::
The Postgresql dialect uses psycopg2 as the default DBAPI. pg8000 is
also available as a pure-Python substitute::
# default
engine = create_engine('postgresql://scott:tiger@localhost/mydatabase')
@@ -101,15 +90,13 @@ The Postgresql dialect uses psycopg2 as the default DBAPI::
# pg8000
engine = create_engine('postgresql+pg8000://scott:tiger@localhost/mydatabase')
# Jython
engine = create_engine('postgresql+zxjdbc://scott:tiger@localhost/mydatabase')
More notes on connecting to Postgresql at :ref:`postgresql_toplevel`.
MySQL
-----
The MySQL dialect uses mysql-python as the default DBAPI::
The MySQL dialect uses mysql-python as the default DBAPI. There are many
MySQL DBAPIs available, including MySQL-connector-python and OurSQL::
# default
engine = create_engine('mysql://scott:tiger@localhost/foo')
@@ -117,6 +104,9 @@ The MySQL dialect uses mysql-python as the default DBAPI::
# mysql-python
engine = create_engine('mysql+mysqldb://scott:tiger@localhost/foo')
# MySQL-connector-python
engine = create_engine('mysql+mysqlconnector://scott:tiger@localhost/foo')
# OurSQL
engine = create_engine('mysql+oursql://scott:tiger@localhost/foo')
@@ -125,7 +115,7 @@ More notes on connecting to MySQL at :ref:`mysql_toplevel`.
Oracle
------
cx_oracle is usualjy used here::
The Oracle dialect uses cx_oracle as the default DBAPI::
engine = create_engine('oracle://scott:tiger@127.0.0.1:1521/sidname')
@@ -136,25 +126,39 @@ More notes on connecting to Oracle at :ref:`oracle_toplevel`.
Microsoft SQL Server
--------------------
There are a few drivers for SQL Server, currently PyODBC is the most solid::
The SQL Server dialect uses pyodbc as the default DBAPI. pymssql is
also available::
engine = create_engine('mssql+pyodbc://mydsn')
# pyodbc
engine = create_engine('mssql+pyodbc://scott:tiger@mydsn')
# pymssql
engine = create_engine('mssql+pymssql://scott:tiger@hostname:port/dbname')
More notes on connecting to SQL Server at :ref:`mssql_toplevel`.
SQLite
------
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 connects to file-based databases, using the Python built-in
module ``sqlite3`` by default.
As SQLite connects to local files, the URL format is slightly different.
The "file" portion of the URL is the filename of the database.
For a relative file path, this requires three slashes::
# sqlite://<nohostname>/<path>
# where <path> is relative:
engine = create_engine('sqlite:///foo.db')
# or absolute, starting with a slash:
And for an absolute file path, the three slashes are followed by the absolute path::
#Unix/Mac - 4 initial slashes in total
engine = create_engine('sqlite:////absolute/path/to/foo.db')
#Windows
engine = create_engine('sqlite:///C:\\path\\to\\foo.db')
#Windows alternative using raw string
engine = create_engine(r'sqlite:///C:\path\to\foo.db')
To use a SQLite ``:memory:`` database, specify an empty URL::
@@ -165,11 +169,20 @@ More notes on connecting to SQLite at :ref:`sqlite_toplevel`.
Others
------
See :ref:`dialect_toplevel`, the top-level page for all dialect
See :ref:`dialect_toplevel`, the top-level page for all additional dialect
documentation.
URL API
--------
.. _create_engine_args:
Engine Creation API
===================
.. autofunction:: sqlalchemy.create_engine
.. autofunction:: sqlalchemy.engine_from_config
.. autofunction:: sqlalchemy.engine.url.make_url
.. autoclass:: sqlalchemy.engine.url.URL
:members:
@@ -196,7 +209,6 @@ application, rather than creating a new one for each connection.
For more information on connection pooling, see :ref:`pooling_toplevel`.
.. _custom_dbapi_args:
Custom DBAPI connect() arguments
+1 -1
View File
@@ -7,7 +7,7 @@ SQLAlchemy includes an event API which publishes a wide variety of hooks into
the internals of both SQLAlchemy Core and ORM.
.. versionadded:: 0.7
The system supercedes the previous system of "extension", "proxy",
The system supersedes the previous system of "extension", "proxy",
and "listener" classes.
Event Registration
+4 -1
View File
@@ -8,8 +8,11 @@ SQLAlchemy Core.
For an introduction to the event listening API, see :ref:`event_toplevel`.
ORM events are described in :ref:`orm_event_toplevel`.
.. autoclass:: sqlalchemy.event.base.Events
:members:
.. versionadded:: 0.7
The event system supercedes the previous system of "extension", "listener",
The event system supersedes the previous system of "extension", "listener",
and "proxy" classes.
Connection Pool Events
-1
View File
@@ -2,5 +2,4 @@ Core Exceptions
===============
.. automodule:: sqlalchemy.exc
:show-inheritance:
:members:
+7 -243
View File
@@ -8,249 +8,13 @@ SQL Statements and Expressions API
This section presents the API reference for the SQL Expression Language. For a full introduction to its usage,
see :ref:`sqlexpression_toplevel`.
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:: sqlalchemy.sql.expression.column
.. autofunction:: collate
.. autofunction:: delete
.. autofunction:: desc
.. autofunction:: distinct
.. autofunction:: except_
.. autofunction:: except_all
.. autofunction:: exists
.. autofunction:: extract
.. autofunction:: false
.. autodata:: func
.. autofunction:: insert
.. autofunction:: intersect
.. autofunction:: intersect_all
.. autofunction:: join
.. autofunction:: label
.. autofunction:: literal
.. autofunction:: literal_column
.. autofunction:: not_
.. autofunction:: null
.. autofunction:: nullsfirst
.. autofunction:: nullslast
.. autofunction:: or_
.. autofunction:: outparam
.. autofunction:: outerjoin
.. autofunction:: over
.. autofunction:: select
.. autofunction:: subquery
.. autofunction:: sqlalchemy.sql.expression.table
.. autofunction:: text
.. autofunction:: true
.. autofunction:: tuple_
.. autofunction:: type_coerce
.. autofunction:: union
.. autofunction:: union_all
.. autofunction:: update
Classes
-------
.. autoclass:: Alias
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: BinaryExpression
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: BindParameter
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: ClauseElement
:members:
:show-inheritance:
.. autoclass:: ClauseList
:members:
:show-inheritance:
.. autoclass:: ColumnClause
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: ColumnCollection
:members:
:show-inheritance:
.. autoclass:: ColumnElement
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: sqlalchemy.sql.operators.ColumnOperators
:members:
:special-members:
:inherited-members:
:show-inheritance:
.. autoclass:: CompoundSelect
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: sqlalchemy.sql.operators.custom_op
:members:
.. autoclass:: CTE
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: Delete
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: Executable
:members:
:show-inheritance:
.. autoclass:: FunctionElement
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: Function
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: FromClause
:members:
:show-inheritance:
.. autoclass:: Insert
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: Join
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: sqlalchemy.sql.operators.Operators
:members:
:special-members:
.. autoclass:: Select
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: Selectable
:members:
:show-inheritance:
.. autoclass:: SelectBase
:members:
:show-inheritance:
.. autoclass:: TableClause
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: UnaryExpression
:members:
:show-inheritance:
.. autoclass:: Update
:members:
:inherited-members:
:show-inheritance:
.. autoclass:: UpdateBase
:members:
:show-inheritance:
.. autoclass:: ValuesBase
:members:
: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)
Note that any name not known to :attr:`func` generates the function name as is
- there is no restriction on what SQL functions can be called, known or
unknown to SQLAlchemy, built-in or user defined. The section here only
describes those functions where SQLAlchemy already knows what argument and
return types are in use.
.. automodule:: sqlalchemy.sql.functions
:members:
:undoc-members:
:show-inheritance:
.. toctree::
:maxdepth: 1
sqlelement
selectable
dml
functions
types
+27
View File
@@ -0,0 +1,27 @@
.. _functions_toplevel:
.. _generic_functions:
=========================
SQL and Generic Functions
=========================
.. module:: sqlalchemy.sql.expression
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)
Note that any name not known to :attr:`func` generates the function name as is
- there is no restriction on what SQL functions can be called, known or
unknown to SQLAlchemy, built-in or user defined. The section here only
describes those functions where SQLAlchemy already knows what argument and
return types are in use.
.. automodule:: sqlalchemy.sql.functions
:members:
:undoc-members:
+1 -2
View File
@@ -13,11 +13,10 @@ Language provides a schema-centric usage paradigm.
tutorial
expression_api
schema
engines
connections
pooling
schema
types
event
events
compiler
+4 -6
View File
@@ -12,12 +12,10 @@ Some key internal constructs are listed here.
.. autoclass:: sqlalchemy.sql.compiler.DDLCompiler
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: sqlalchemy.engine.default.DefaultDialect
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: sqlalchemy.engine.interfaces.Dialect
@@ -25,17 +23,17 @@ Some key internal constructs are listed here.
.. autoclass:: sqlalchemy.engine.default.DefaultExecutionContext
:members:
:show-inheritance:
.. autoclass:: sqlalchemy.engine.interfaces.ExecutionContext
:members:
:show-inheritance:
.. autoclass:: sqlalchemy.sql.compiler.IdentifierPreparer
:members:
:show-inheritance:
.. autoclass:: sqlalchemy.sql.compiler.SQLCompiler
:members:
:show-inheritance:
+328
View File
@@ -0,0 +1,328 @@
.. _metadata_toplevel:
.. _metadata_describing_toplevel:
.. _metadata_describing:
==================================
Describing Databases with MetaData
==================================
.. module:: sqlalchemy.schema
This section discusses the fundamental :class:`.Table`, :class:`.Column`
and :class:`.MetaData` objects.
A collection of metadata entities is stored in an object aptly named
:class:`~sqlalchemy.schema.MetaData`::
from sqlalchemy import *
metadata = MetaData()
:class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.schema.Table` class. Its two
primary arguments are the table name, then the
:class:`~sqlalchemy.schema.MetaData` object which it will be associated with.
The remaining positional arguments are mostly
:class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.types.Integer` and
:class:`~sqlalchemy.types.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 :class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.schema.Table` objects have been
explicitly declared, and these objects are typically accessed directly as
module-level variables in an application. Once a
:class:`~sqlalchemy.schema.Table` has been defined, it has a full set of
accessors which allow inspection of its properties. Given the following
:class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.schema.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_keys
# get the "key" of a column, which defaults to its name, but can
# be any user-defined string:
employees.c.employee_name.key
# access a column's table:
employees.c.employee_id.table is employees
# get the table related by a foreign key
list(employees.c.employee_dept.foreign_keys)[0].column.table
Creating and Dropping Database Tables
-------------------------------------
Once you've defined some :class:`~sqlalchemy.schema.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
:func:`~sqlalchemy.schema.MetaData.create_all` on the
:class:`~sqlalchemy.schema.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)
)
:func:`~sqlalchemy.schema.MetaData.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
:func:`~sqlalchemy.schema.MetaData.drop_all` method. This method does the
exact opposite of :func:`~sqlalchemy.schema.MetaData.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 :class:`~sqlalchemy.schema.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)
.. _schema_migrations:
Altering Schemas through Migrations
-----------------------------------
While SQLAlchemy directly supports emitting CREATE and DROP statements for schema
constructs, the ability to alter those constructs, usually via the ALTER statement
as well as other database-specific constructs, is outside of the scope of SQLAlchemy
itself. While it's easy enough to emit ALTER statements and similar by hand,
such as by passing a string to :meth:`.Connection.execute` or by using the
:class:`.DDL` construct, it's a common practice to automate the maintenance of
database schemas in relation to application code using schema migration tools.
There are two major migration tools available for SQLAlchemy:
* `Alembic <http://alembic.readthedocs.org>`_ - Written by the author of SQLAlchemy,
Alembic features a highly customizable environment and a minimalistic usage pattern,
supporting such features as transactional DDL, automatic generation of "candidate"
migrations, an "offline" mode which generates SQL scripts, and support for branch
resolution.
* `SQLAlchemy-Migrate <http://code.google.com/p/sqlalchemy-migrate/>`_ - The original
migration tool for SQLAlchemy, SQLAlchemy-Migrate is widely used and continues
under active development. SQLAlchemy-Migrate includes features such as
SQL script generation, ORM class generation, ORM model comparison, and extensive
support for SQLite migrations.
Specifying the Schema Name
---------------------------
Some databases support the concept of multiple schemas. A
:class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.schema.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
:class:`~sqlalchemy.schema.Table`. :class:`~sqlalchemy.schema.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
------------------------
:class:`~sqlalchemy.schema.Table` supports database-specific options. For
example, MySQL has different table backend types, including "MyISAM" and
"InnoDB". This can be expressed with :class:`~sqlalchemy.schema.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 - these would be
described in the individual documentation sections for each dialect.
Column, Table, MetaData API
---------------------------
.. autoclass:: Column
:members:
:inherited-members:
:undoc-members:
.. autoclass:: MetaData
:members:
:undoc-members:
.. autoclass:: SchemaItem
:members:
.. autoclass:: Table
:members:
:inherited-members:
:undoc-members:
.. autoclass:: ThreadLocalMetaData
:members:
:undoc-members:
+9 -7
View File
@@ -127,7 +127,7 @@ within a transparent proxy::
cursor.execute("select foo")
The purpose of the transparent proxy is to intercept the ``close()`` call,
such that instead of the DBAPI connection being closed, it's returned to the
such that instead of the DBAPI connection being closed, it is returned to the
pool::
# "close" the connection. Returns
@@ -229,7 +229,7 @@ Disconnect Handling - Pessimistic
At the expense of some extra SQL emitted for each connection checked out from the pool,
a "ping" operation established by a checkout event handler
can detect an invalid connection before it's used::
can detect an invalid connection before it is used::
from sqlalchemy import exc
from sqlalchemy import event
@@ -291,25 +291,27 @@ API Documentation - Available Pool Implementations
.. automethod:: connect
.. automethod:: dispose
.. automethod:: recreate
.. automethod:: unique_connection
.. autoclass:: sqlalchemy.pool.QueuePool
:show-inheritance:
.. automethod:: __init__
.. automethod:: connect
.. automethod:: unique_connection
.. autoclass:: SingletonThreadPool
:show-inheritance:
.. automethod:: __init__
.. autoclass:: AssertionPool
:show-inheritance:
.. autoclass:: NullPool
:show-inheritance:
.. autoclass:: StaticPool
:show-inheritance:
Pooling Plain DB-API Connections
+166
View File
@@ -0,0 +1,166 @@
.. module:: sqlalchemy.schema
.. _metadata_reflection_toplevel:
.. _metadata_reflection:
Reflecting Database Objects
===========================
A :class:`~sqlalchemy.schema.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*. In the
most simple case you need only specify the table name, a :class:`~sqlalchemy.schema.MetaData`
object, and the ``autoload=True`` flag. If the
:class:`~sqlalchemy.schema.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
:class:`~sqlalchemy.schema.Column`, :class:`~sqlalchemy.schema.ForeignKey`,
and other objects corresponding to this information as though the
:class:`~sqlalchemy.schema.Table` object were hand-constructed in Python.
When tables are reflected, if a given table references another one via foreign
key, a second :class:`~sqlalchemy.schema.Table` object is created within the
:class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.schema.MetaData` has an interesting "singleton-like"
behavior such that if you requested both tables individually,
:class:`~sqlalchemy.schema.MetaData` will ensure that exactly one
:class:`~sqlalchemy.schema.Table` object is created for each distinct table
name. The :class:`~sqlalchemy.schema.Table` constructor actually returns to
you the already-existing :class:`~sqlalchemy.schema.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
:class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.schema.Table` object with
:class:`~sqlalchemy.schema.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 :class:`~sqlalchemy.schema.MetaData` object can also get a listing of
tables and reflect the full set. This is achieved by using the
:func:`~sqlalchemy.schema.MetaData.reflect` method. After calling it, all
located tables are present within the :class:`~sqlalchemy.schema.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())
Fine Grained Reflection with Inspector
--------------------------------------
A low level interface which provides a backend-agnostic system of loading
lists of schema, table, column, and constraint descriptions from a given
database is also available. This is known as the "Inspector"::
from sqlalchemy import create_engine
from sqlalchemy.engine import reflection
engine = create_engine('...')
insp = reflection.Inspector.from_engine(engine)
print insp.get_table_names()
.. autoclass:: sqlalchemy.engine.reflection.Inspector
:members:
:undoc-members:
Limitations of Reflection
-------------------------
It's important to note that the reflection process recreates :class:`.Table`
metadata using only information which is represented in the relational database.
This process by definition cannot restore aspects of a schema that aren't
actually stored in the database. State which is not available from reflection
includes but is not limited to:
* Client side defaults, either Python functions or SQL expressions defined using
the ``default`` keyword of :class:`.Column` (note this is separate from ``server_default``,
which specifically is what's available via reflection).
* Column information, e.g. data that might have been placed into the
:attr:`.Column.info` dictionary
* The value of the ``.quote`` setting for :class:`.Column` or :class:`.Table`
* The association of a particular :class:`.Sequence` with a given :class:`.Column`
The relational database also in many cases reports on table metadata in a
different format than what was specified in SQLAlchemy. The :class:`.Table`
objects returned from reflection cannot be always relied upon to produce the identical
DDL as the original Python-defined :class:`.Table` objects. Areas where
this occurs includes server defaults, column-associated sequences and various
idosyncrasies regarding constraints and datatypes. Server side defaults may
be returned with cast directives (typically Postgresql will include a ``::<type>``
cast) or different quoting patterns than originally specified.
Another category of limitation includes schema structures for which reflection
is only partially or not yet defined. Recent improvements to reflection allow
things like views, indexes and foreign key options to be reflected. As of this
writing, structures like CHECK constraints, table comments, and triggers are
not reflected.
+10 -1452
View File
File diff suppressed because it is too large Load Diff
+79
View File
@@ -0,0 +1,79 @@
Selectables, Tables, FROM objects
=================================
The term "selectable" refers to any object that rows can be selected from;
in SQLAlchemy, these objects descend from :class:`.FromClause` and their
distinguishing feature is their :attr:`.FromClause.c` attribute, which is
a namespace of all the columns contained within the FROM clause (these
elements are themselves :class:`.ColumnElement` subclasses).
.. module:: sqlalchemy.sql.expression
.. autofunction:: alias
.. autofunction:: except_
.. autofunction:: except_all
.. autofunction:: exists
.. autofunction:: intersect
.. autofunction:: intersect_all
.. autofunction:: join
.. autofunction:: outerjoin
.. autofunction:: select
.. autofunction:: subquery
.. autofunction:: sqlalchemy.sql.expression.table
.. autofunction:: union
.. autofunction:: union_all
.. autoclass:: Alias
:members:
:inherited-members:
.. autoclass:: CompoundSelect
:members:
:inherited-members:
.. autoclass:: CTE
:members:
:inherited-members:
.. autoclass:: Executable
:members:
.. autoclass:: FromClause
:members:
.. autoclass:: Join
:members:
:inherited-members:
.. autoclass:: ScalarSelect
:members:
.. autoclass:: Select
:members:
:inherited-members:
.. autoclass:: Selectable
:members:
.. autoclass:: SelectBase
:members:
.. autoclass:: TableClause
:members:
:inherited-members:
+139
View File
@@ -0,0 +1,139 @@
Column Elements and Expressions
===============================
.. module:: sqlalchemy.sql.expression
The most fundamental part of the SQL expression API are the "column elements",
which allow for basic SQL expression support. The core of all SQL expression
constructs is the :class:`.ClauseElement`, which is the base for several
sub-branches. The :class:`.ColumnElement` class is the fundamental unit
used to construct any kind of typed SQL expression.
.. autofunction:: and_
.. autofunction:: asc
.. autofunction:: between
.. autofunction:: bindparam
.. autofunction:: case
.. autofunction:: cast
.. autofunction:: sqlalchemy.sql.expression.column
.. autofunction:: collate
.. autofunction:: desc
.. autofunction:: distinct
.. autofunction:: extract
.. autofunction:: false
.. autodata:: func
.. autofunction:: label
.. autofunction:: literal
.. autofunction:: literal_column
.. autofunction:: not_
.. autofunction:: null
.. autofunction:: nullsfirst
.. autofunction:: nullslast
.. autofunction:: or_
.. autofunction:: outparam
.. autofunction:: over
.. autofunction:: text
.. autofunction:: true
.. autofunction:: tuple_
.. autofunction:: type_coerce
.. autoclass:: BinaryExpression
:members:
.. autoclass:: BindParameter
:members:
.. autoclass:: Case
:members:
.. autoclass:: Cast
:members:
.. autoclass:: ClauseElement
:members:
.. autoclass:: ClauseList
:members:
.. autoclass:: ColumnClause
:members:
.. autoclass:: ColumnCollection
:members:
.. autoclass:: ColumnElement
:members:
:inherited-members:
:undoc-members:
.. autoclass:: sqlalchemy.sql.operators.ColumnOperators
:members:
:special-members:
:inherited-members:
.. autoclass:: Extract
:members:
.. autoclass:: sqlalchemy.sql.expression.False_
:members:
.. autoclass:: Label
:members:
.. autoclass:: sqlalchemy.sql.expression.Null
:members:
.. autoclass:: Over
:members:
.. autoclass:: TextClause
:members:
.. autoclass:: Tuple
:members:
.. autoclass:: sqlalchemy.sql.expression.True_
:members:
.. autoclass:: sqlalchemy.sql.operators.custom_op
:members:
.. autoclass:: sqlalchemy.sql.operators.Operators
:members:
:special-members:
.. autoclass:: UnaryExpression
:members:
+111 -19
View File
@@ -77,6 +77,28 @@ 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 what's being generated.
The return value of :func:`.create_engine` is an instance of
:class:`.Engine`, and it represents the core interface to the
database, adapted through a :term:`dialect` that handles the details
of the database and :term:`DBAPI` in use. In this case the SQLite
dialect will interpret instructions to the Python built-in ``sqlite3``
module.
.. sidebar:: Lazy Connecting
The :class:`.Engine`, when first returned by :func:`.create_engine`,
has not actually tried to connect to the database yet; that happens
only the first time it is asked to perform a task against the database.
The first time a method like :meth:`.Engine.execute` or :meth:`.Engine.connect`
is called, the :class:`.Engine` establishes a real :term:`DBAPI` connection to the
database, which is then used to emit the SQL.
.. seealso::
:ref:`database_urls` - includes examples of :func:`.create_engine`
connecting to several kinds of databases with links to more information.
Define and Create Tables
=========================
@@ -97,7 +119,7 @@ We define our tables all within a catalog called
:class:`~sqlalchemy.schema.Table` construct, which resembles regular SQL
CREATE TABLE statements. We'll make two tables, one of which represents
"users" in an application, and another which represents zero or more "email
addreses" for each row in the "users" table:
addresses" for each row in the "users" table:
.. sourcecode:: pycon+sql
@@ -238,7 +260,7 @@ we use the ``connect()`` method::
>>> conn #doctest: +ELLIPSIS
<sqlalchemy.engine.base.Connection object at 0x...>
The :class:`~sqlalchemy.engine.base.Connection` object represents an actively
The :class:`~sqlalchemy.engine.Connection` object represents an actively
checked out DBAPI connection resource. Lets feed it our
:class:`~sqlalchemy.sql.expression.Insert` object and see what happens:
@@ -252,7 +274,7 @@ checked out DBAPI connection resource. Lets feed it our
So the INSERT statement was now issued to the database. Although we got
positional "qmark" bind parameters instead of "named" bind parameters in the
output. How come ? Because when executed, the
:class:`~sqlalchemy.engine.base.Connection` used the SQLite **dialect** to
:class:`~sqlalchemy.engine.Connection` used the SQLite **dialect** to
help generate the statement; when we use the ``str()`` function, the statement
isn't aware of this dialect, and falls back onto a default which uses named
parameters. We can view this manually as follows:
@@ -264,9 +286,9 @@ parameters. We can view this manually as follows:
'INSERT INTO users (name, fullname) VALUES (?, ?)'
What about the ``result`` variable we got when we called ``execute()`` ? As
the SQLAlchemy :class:`~sqlalchemy.engine.base.Connection` object references a
the SQLAlchemy :class:`~sqlalchemy.engine.Connection` object references a
DBAPI connection, the result, known as a
:class:`~sqlalchemy.engine.result.ResultProxy` object, is analogous to the DBAPI
:class:`~sqlalchemy.engine.ResultProxy` object, is analogous to the DBAPI
cursor object. In the case of an INSERT, we can get important information from
it, such as the primary key values which were generated from our statement:
@@ -292,7 +314,7 @@ Our insert example above was intentionally a little drawn out to show some
various behaviors of expression language constructs. In the usual case, an
:class:`~sqlalchemy.sql.expression.Insert` statement is usually compiled
against the parameters sent to the ``execute()`` method on
:class:`~sqlalchemy.engine.base.Connection`, so that there's no need to use
:class:`~sqlalchemy.engine.Connection`, so that there's no need to use
the ``values`` keyword with :class:`~sqlalchemy.sql.expression.Insert`. Lets
create a generic :class:`~sqlalchemy.sql.expression.Insert` statement again
and use it in the "normal" way:
@@ -363,10 +385,10 @@ Above, we issued a basic :func:`.select` call, placing the ``users`` table
within the COLUMNS clause of the select, and then executing. SQLAlchemy
expanded the ``users`` table into the set of each of its columns, and also
generated a FROM clause for us. The result returned is again a
:class:`~sqlalchemy.engine.result.ResultProxy` object, which acts much like a
:class:`~sqlalchemy.engine.ResultProxy` object, which acts much like a
DBAPI cursor, including methods such as
:func:`~sqlalchemy.engine.result.ResultProxy.fetchone` and
:func:`~sqlalchemy.engine.result.ResultProxy.fetchall`. The easiest way to get
:func:`~sqlalchemy.engine.ResultProxy.fetchone` and
:func:`~sqlalchemy.engine.ResultProxy.fetchall`. The easiest way to get
rows from it is to just iterate:
.. sourcecode:: pycon+sql
@@ -414,7 +436,7 @@ But another way, whose usefulness will become apparent later on, is to use the
Result sets which have pending rows remaining should be explicitly closed
before discarding. While the cursor and connection resources referenced by the
:class:`~sqlalchemy.engine.result.ResultProxy` will be respectively closed and
:class:`~sqlalchemy.engine.ResultProxy` will be respectively closed and
returned to the connection pool when the object is garbage collected, it's
better to make it explicit as some database APIs are very picky about such
things:
@@ -883,7 +905,6 @@ to "correlate" the inner ``users`` table with the outer one:
Using Joins
============
We're halfway along to being able to construct any SELECT expression. The next
cornerstone of the SELECT is the JOIN expression. We've already been doing
joins in our examples, by just placing two tables in either the columns clause
@@ -958,6 +979,14 @@ would be using ``OracleDialect``) to use Oracle-specific SQL:
If you don't know what that SQL means, don't worry ! The secret tribe of
Oracle DBAs don't want their black magic being found out ;).
.. seealso::
:func:`.expression.join`
:func:`.expression.outerjoin`
:class:`.Join`
Everything Else
================
@@ -965,14 +994,19 @@ The concepts of creating SQL expressions have been introduced. What's left are
more variants of the same themes. So now we'll catalog the rest of the
important things we'll need to know.
.. _coretutorial_bind_param:
Bind Parameter Objects
----------------------
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
database dialect converts to the appropriate named or positional style, as
here where it converts to positional for SQLite:
parameters with your own names, and use the same statement repeatedly.
The :func:`.bindparam` construct is used to produce a bound parameter
with a given name. While SQLAlchemy always refers to bound parameters by
name on the API side, the
database dialect converts to the appropriate named or positional style
at execution time, as here where it converts to positional for SQLite:
.. sourcecode:: pycon+sql
@@ -985,7 +1019,7 @@ here where it converts to positional for SQLite:
('wendy',)
{stop}[(2, u'wendy', u'Wendy Williams')]
Another important aspect of bind parameters is that they may be assigned a
Another important aspect of :func:`.bindparam` is that it may be assigned a
type. The type of the bind parameter will determine its behavior within
expressions and also how the data bound to it is processed before being sent
off to the database:
@@ -1001,7 +1035,7 @@ off to the database:
{stop}[(2, u'wendy', u'Wendy Williams')]
Bind parameters of the same name can also be used multiple times, where only a
:func:`.bindparam` constructs of the same name can also be used multiple times, where only a
single named value is needed in the execute parameters:
.. sourcecode:: pycon+sql
@@ -1026,6 +1060,10 @@ single named value is needed in the execute parameters:
('jack', 'jack')
{stop}[(1, u'jack', u'Jack Jones', 1, 1, u'jack@yahoo.com'), (1, u'jack', u'Jack Jones', 2, 1, u'jack@msn.com')]
.. seealso::
:func:`.bindparam`
Functions
---------
@@ -1124,13 +1162,16 @@ of our selectable:
>>> s.compile().params
{u'x_2': 5, u'y_2': 12, u'y_1': 45, u'x_1': 17}
.. seealso::
:data:`.func`
Window Functions
-----------------
Any :class:`.FunctionElement`, including functions generated by
:data:`~.expression.func`, can be turned into a "window function", that is an
OVER clause, using the :meth:`~.FunctionElement.over` method:
OVER clause, using the :meth:`.FunctionElement.over` method:
.. sourcecode:: pycon+sql
@@ -1142,6 +1183,12 @@ OVER clause, using the :meth:`~.FunctionElement.over` method:
SELECT users.id, row_number() OVER (ORDER BY users.name) AS anon_1
FROM users
.. seealso::
:func:`.over`
:meth:`.FunctionElement.over`
Unions and Other Set Operations
-------------------------------
@@ -1234,6 +1281,20 @@ want the "union" to be stated as a subquery:
('%@yahoo.com', '%@msn.com', '%@msn.com')
{stop}[(1, 1, u'jack@yahoo.com')]
.. seealso::
:func:`.union`
:func:`.union_all`
:func:`.intersect`
:func:`.intersect_all`
:func:`.except_`
:func:`.except_all`
.. _scalar_selects:
Scalar Selects
@@ -1286,6 +1347,12 @@ it using :meth:`.SelectBase.label` instead:
()
{stop}[(u'jack', 2), (u'wendy', 2)]
.. seealso::
:meth:`.Select.as_scalar`
:meth:`.Select.label`
.. _correlated_subqueries:
Correlated Subqueries
@@ -1357,6 +1424,29 @@ as the argument:
('wendy',)
{stop}[(u'wendy',)]
We can also control correlation via exclusion, using the :meth:`.Select.correlate_except`
method. Such as, we can write our SELECT for the ``users`` table
by telling it to correlate all FROM clauses except for ``users``:
.. sourcecode:: pycon+sql
>>> stmt = select([users.c.id]).\
... where(users.c.id == addresses.c.user_id).\
... where(users.c.name == 'jack').\
... correlate_except(users)
>>> enclosing_stmt = select(
... [users.c.name, addresses.c.email_address]).\
... select_from(users.join(addresses)).\
... where(users.c.id == stmt)
>>> conn.execute(enclosing_stmt).fetchall() # doctest: +NORMALIZE_WHITESPACE
{opensql}SELECT users.name, addresses.email_address
FROM users JOIN addresses ON users.id = addresses.user_id
WHERE users.id = (SELECT users.id
FROM users
WHERE users.id = addresses.user_id AND users.name = ?)
('jack',)
{stop}[(u'jack', u'jack@yahoo.com'), (u'jack', u'jack@msn.com')]
Ordering, Grouping, Limiting, Offset...ing...
---------------------------------------------
@@ -1420,7 +1510,7 @@ method:
(4,)
{stop}[(u'wendy', 2)]
A common system of dealing with duplicates in composed SELECT statments
A common system of dealing with duplicates in composed SELECT statements
is the DISTINCT modifier. A simple DISTINCT clause can be added using the
:meth:`.Select.distinct` method:
@@ -1466,7 +1556,7 @@ Inserts, Updates and Deletes
We've seen :meth:`~.TableClause.insert` demonstrated
earlier in this tutorial. Where :meth:`~.TableClause.insert`
prodces INSERT, the :meth:`~.TableClause.update`
produces INSERT, the :meth:`~.TableClause.update`
method produces UPDATE. Both of these constructs feature
a method called :meth:`~.ValuesBase.values` which specifies
the VALUES or SET clause of the statement.
@@ -1570,6 +1660,8 @@ table, or the same table:
COMMIT
{stop}<sqlalchemy.engine.result.ResultProxy object at 0x...>
.. _multi_table_updates:
Multiple Table Updates
----------------------
+42 -58
View File
@@ -41,76 +41,58 @@ type is emitted in ``CREATE TABLE``, such as ``VARCHAR`` see `SQL
Standard Types`_ and the other sections of this chapter.
.. autoclass:: BigInteger
:show-inheritance:
:members:
:members:
.. autoclass:: Boolean
:show-inheritance:
:members:
:members:
.. autoclass:: Date
:show-inheritance:
:members:
:members:
.. autoclass:: DateTime
:show-inheritance:
:members:
.. autoclass:: Enum
:show-inheritance:
:members: __init__, create, drop
.. autoclass:: Float
:show-inheritance:
:members:
.. autoclass:: Integer
:show-inheritance:
:members:
.. autoclass:: Interval
:show-inheritance:
:members:
:members:
.. autoclass:: LargeBinary
:show-inheritance:
:members:
:members:
.. autoclass:: Numeric
:show-inheritance:
:members:
.. autoclass:: PickleType
:show-inheritance:
:members:
:members:
.. autoclass:: SchemaType
:show-inheritance:
:members:
:undoc-members:
.. autoclass:: SmallInteger
:show-inheritance:
:members:
:members:
.. autoclass:: String
:show-inheritance:
:members:
.. autoclass:: Text
:show-inheritance:
:members:
.. autoclass:: Time
:show-inheritance:
:members:
.. autoclass:: Unicode
:show-inheritance:
:members:
.. autoclass:: UnicodeText
:show-inheritance:
:members:
.. _types_sqlstandard:
@@ -123,70 +105,70 @@ name when ``CREATE TABLE`` is issued. Some types may not be supported
on all databases.
.. autoclass:: BIGINT
:show-inheritance:
.. 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:: NVARCHAR
:show-inheritance:
.. autoclass:: NUMERIC
:show-inheritance:
.. autoclass:: REAL
: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:
.. _types_vendor:
@@ -194,7 +176,7 @@ Vendor-Specific Types
---------------------
Database-specific types are also available for import from each
database's dialect module. See the :ref:`sqlalchemy.dialects_toplevel`
database's dialect module. See the :ref:`dialect_toplevel`
reference for the database you're interested in.
For example, MySQL has a ``BIGINT`` type and PostgreSQL has an
@@ -300,7 +282,7 @@ to and from the database is required.
.. autoclass:: TypeDecorator
:members:
:inherited-members:
:show-inheritance:
TypeDecorator Recipes
~~~~~~~~~~~~~~~~~~~~~
@@ -352,7 +334,7 @@ many decimal places. Here's a recipe that rounds them down::
def __init__(self, *arg, **kw):
TypeDecorator.__init__(self, *arg, **kw)
self.quantize_int = -(self.impl.precision - self.impl.scale)
self.quantize_int = - self.impl.scale
self.quantize = Decimal(10) ** self.quantize_int
def process_bind_param(self, value, dialect):
@@ -361,6 +343,8 @@ many decimal places. Here's a recipe that rounds them down::
value = value.quantize(self.quantize)
return value
.. _custom_guid_type:
Backend-agnostic GUID Type
^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -442,7 +426,7 @@ meaning, in-place changes to values will not be detected and will not be
flushed. Without further steps, you instead would need to replace the existing
value with a new one on each parent object to detect changes. Note that
there's nothing wrong with this, as many applications may not require that the
values are ever mutated once created. For those which do have this requirment,
values are ever mutated once created. For those which do have this requirement,
support for mutability is best applied using the ``sqlalchemy.ext.mutable``
extension - see the example in :ref:`mutable_toplevel`.
@@ -690,7 +674,7 @@ to integers::
class MyInt(Integer):
class comparator_factory(Integer.Comparator):
def log(self, other):
return func.log(self, other)
return func.log(self.expr, other)
Using the above type::
@@ -738,7 +722,7 @@ is needed, use :class:`.TypeDecorator` instead.
.. autoclass:: UserDefinedType
:members:
:show-inheritance:
.. _types_api:
@@ -747,20 +731,20 @@ Base Type API
.. autoclass:: AbstractType
:members:
:show-inheritance:
.. autoclass:: TypeEngine
:members:
:show-inheritance:
.. autoclass:: Concatenable
:members:
:inherited-members:
:show-inheritance:
.. autoclass:: NullType
:show-inheritance:
.. autoclass:: Variant
:show-inheritance:
:members: with_variant, __init__
+12 -12
View File
@@ -23,51 +23,51 @@ construction arguments, are as follows:
.. autoclass:: BIGINT
:members: __init__
:show-inheritance:
.. autoclass:: CHAR
:members: __init__
:show-inheritance:
.. autoclass:: DECIMAL
:members: __init__
:show-inheritance:
.. autoclass:: DOUBLE
:members: __init__
:show-inheritance:
.. autoclass:: ENUM
:members: __init__
:show-inheritance:
.. autoclass:: FLOAT
:members: __init__
:show-inheritance:
.. autoclass:: INTEGER
:members: __init__
:show-inheritance:
.. autoclass:: NUMERIC
:members: __init__
:show-inheritance:
.. autoclass:: REAL
:members: __init__
:show-inheritance:
.. autoclass:: TEXT
:members: __init__
:show-inheritance:
.. autoclass:: TIMESTAMP
:members: __init__
:show-inheritance:
.. autoclass:: VARCHAR
:members: __init__
:show-inheritance:
MySQL-Python
+26 -5
View File
@@ -42,11 +42,32 @@ External Dialects
Current external dialect projects for SQLAlchemy include:
* `ibm_db_sa <http://code.google.com/p/ibm-db/wiki/README>`_ - driver for IBM DB2, developed jointly by IBM and SQLAlchemy developers.
* `sqlalchemy-access <https://bitbucket.org/zzzeek/sqlalchemy-access>`_ - driver for Microsoft Access.
* `sqlalchemy-akiban <https://github.com/zzzeek/sqlalchemy_akiban>`_ - driver and ORM extensions for the `Akiban <http://www.akiban.com>`_ database.
* `sqlalchemy-cubrid <https://bitbucket.org/zzzeek/sqlalchemy-cubrid>`_ - driver for the CUBRID database.
* `sqlalchemy-maxdb <https://bitbucket.org/zzzeek/sqlalchemy-maxdb>`_ - driver for the MaxDB database.
Production Ready
^^^^^^^^^^^^^^^^
* `ibm_db_sa <http://code.google.com/p/ibm-db/wiki/README>`_ - driver for IBM DB2 and Informix, developed jointly by IBM and SQLAlchemy developers.
* `sqlalchemy-sqlany <https://github.com/sqlanywhere/sqlalchemy-sqlany>`_ - driver for SAP Sybase SQL Anywhere, developed by SAP.
* `sqlalchemy-monetdb <https://github.com/gijzelaerr/sqlalchemy-monetdb>`_ - driver for MonetDB.
Experimental / Incomplete
^^^^^^^^^^^^^^^^^^^^^^^^^^
Dialects that are in an incomplete state or are considered somewhat experimental.
* `CALCHIPAN <https://bitbucket.org/zzzeek/calchipan/>`_ - Adapts `Pandas <http://pandas.pydata.org/>`_ dataframes to SQLAlchemy.
* `sqlalchemy-cubrid <https://bitbucket.org/zzzeek/sqlalchemy-cubrid>`_ - driver for the CUBRID database.
Attic
^^^^^
Dialects in the "attic" are those that were contributed for SQLAlchemy long ago
but have received little attention or demand since then, and are now moved out to
their own repositories in at best a semi-working state.
Community members interested in these dialects should feel free to pick up on
their current codebase and fork off into working libraries.
* `sqlalchemy-access <https://bitbucket.org/zzzeek/sqlalchemy-access>`_ - driver for Microsoft Access.
* `sqlalchemy-informixdb <https://bitbucket.org/zzzeek/sqlalchemy-informixdb>`_ - driver for the informixdb DBAPI.
* `sqlalchemy-maxdb <https://bitbucket.org/zzzeek/sqlalchemy-maxdb>`_ - driver for the MaxDB database
+18 -18
View File
@@ -26,75 +26,75 @@ construction arguments, are as follows:
.. autoclass:: BIT
:members: __init__
:show-inheritance:
.. autoclass:: CHAR
:members: __init__
:show-inheritance:
.. autoclass:: DATETIME2
:members: __init__
:show-inheritance:
.. autoclass:: DATETIMEOFFSET
:members: __init__
:show-inheritance:
.. autoclass:: IMAGE
:members: __init__
:show-inheritance:
.. autoclass:: MONEY
:members: __init__
:show-inheritance:
.. autoclass:: NCHAR
:members: __init__
:show-inheritance:
.. autoclass:: NTEXT
:members: __init__
:show-inheritance:
.. autoclass:: NVARCHAR
:members: __init__
:show-inheritance:
.. autoclass:: REAL
:members: __init__
:show-inheritance:
.. autoclass:: SMALLDATETIME
:members: __init__
:show-inheritance:
.. autoclass:: SMALLMONEY
:members: __init__
:show-inheritance:
.. autoclass:: SQL_VARIANT
:members: __init__
:show-inheritance:
.. autoclass:: TEXT
:members: __init__
:show-inheritance:
.. autoclass:: TIME
:members: __init__
:show-inheritance:
.. autoclass:: TINYINT
:members: __init__
:show-inheritance:
.. autoclass:: UNIQUEIDENTIFIER
:members: __init__
:show-inheritance:
.. autoclass:: VARCHAR
:members: __init__
:show-inheritance:
PyODBC
+33 -33
View File
@@ -25,135 +25,135 @@ construction arguments, are as follows:
.. autoclass:: BIGINT
:members: __init__
:show-inheritance:
.. autoclass:: BINARY
:members: __init__
:show-inheritance:
.. autoclass:: BIT
:members: __init__
:show-inheritance:
.. autoclass:: BLOB
:members: __init__
:show-inheritance:
.. autoclass:: BOOLEAN
:members: __init__
:show-inheritance:
.. autoclass:: CHAR
:members: __init__
:show-inheritance:
.. autoclass:: DATE
:members: __init__
:show-inheritance:
.. autoclass:: DATETIME
:members: __init__
:show-inheritance:
.. autoclass:: DECIMAL
:members: __init__
:show-inheritance:
.. autoclass:: DOUBLE
:members: __init__
:show-inheritance:
.. autoclass:: ENUM
:members: __init__
:show-inheritance:
.. autoclass:: FLOAT
:members: __init__
:show-inheritance:
.. autoclass:: INTEGER
:members: __init__
:show-inheritance:
.. autoclass:: LONGBLOB
:members: __init__
:show-inheritance:
.. autoclass:: LONGTEXT
:members: __init__
:show-inheritance:
.. autoclass:: MEDIUMBLOB
:members: __init__
:show-inheritance:
.. autoclass:: MEDIUMINT
:members: __init__
:show-inheritance:
.. autoclass:: MEDIUMTEXT
:members: __init__
:show-inheritance:
.. autoclass:: NCHAR
:members: __init__
:show-inheritance:
.. autoclass:: NUMERIC
:members: __init__
:show-inheritance:
.. autoclass:: NVARCHAR
:members: __init__
:show-inheritance:
.. autoclass:: REAL
:members: __init__
:show-inheritance:
.. autoclass:: SET
:members: __init__
:show-inheritance:
.. autoclass:: SMALLINT
:members: __init__
:show-inheritance:
.. autoclass:: TEXT
:members: __init__
:show-inheritance:
.. autoclass:: TIME
:members: __init__
:show-inheritance:
.. autoclass:: TIMESTAMP
:members: __init__
:show-inheritance:
.. autoclass:: TINYBLOB
:members: __init__
:show-inheritance:
.. autoclass:: TINYINT
:members: __init__
:show-inheritance:
.. autoclass:: TINYTEXT
:members: __init__
:show-inheritance:
.. autoclass:: VARBINARY
:members: __init__
:show-inheritance:
.. autoclass:: VARCHAR
:members: __init__
:show-inheritance:
.. autoclass:: YEAR
:members: __init__
:show-inheritance:
MySQL-Python
--------------------
+7 -7
View File
@@ -25,31 +25,31 @@ construction arguments, are as follows:
.. autoclass:: BFILE
:members: __init__
:show-inheritance:
.. autoclass:: DOUBLE_PRECISION
:members: __init__
:show-inheritance:
.. autoclass:: INTERVAL
:members: __init__
:show-inheritance:
.. autoclass:: NCLOB
:members: __init__
:show-inheritance:
.. autoclass:: NUMBER
:members: __init__
:show-inheritance:
.. autoclass:: LONG
:members: __init__
:show-inheritance:
.. autoclass:: RAW
:members: __init__
:show-inheritance:
cx_Oracle
----------
+75 -14
View File
@@ -16,7 +16,8 @@ they originate from :mod:`sqlalchemy.types` or from the local dialect::
ARRAY, BIGINT, BIT, BOOLEAN, BYTEA, CHAR, CIDR, DATE, \
DOUBLE_PRECISION, ENUM, FLOAT, HSTORE, INET, INTEGER, \
INTERVAL, MACADDR, NUMERIC, REAL, SMALLINT, TEXT, TIME, \
TIMESTAMP, UUID, VARCHAR
TIMESTAMP, UUID, VARCHAR, INT4RANGE, INT8RANGE, NUMRANGE, \
DATERANGE, TSRANGE, TSTZRANGE
Types which are specific to PostgreSQL, or have PostgreSQL-specific
construction arguments, are as follows:
@@ -27,7 +28,7 @@ construction arguments, are as follows:
.. autoclass:: ARRAY
:members: __init__, Comparator
:show-inheritance:
.. autoclass:: Any
@@ -35,51 +36,111 @@ construction arguments, are as follows:
.. 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__, create, drop
:show-inheritance:
.. autoclass:: HSTORE
:members:
:show-inheritance:
.. autoclass:: hstore
:members:
: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:
Range Types
~~~~~~~~~~~
The new range column types founds in PostgreSQL 9.2 onwards are
catered for by the following types:
.. autoclass:: INT4RANGE
.. autoclass:: INT8RANGE
.. autoclass:: NUMRANGE
.. autoclass:: DATERANGE
.. autoclass:: TSRANGE
.. autoclass:: TSTZRANGE
The types above get most of their functionality from the following
mixin:
.. autoclass:: sqlalchemy.dialects.postgresql.ranges.RangeOperators
:members:
.. warning::
The range type DDL support should work with any Postgres DBAPI
driver, however the data types returned may vary. If you are using
``psycopg2``, it's recommended to upgrade to version 2.5 or later
before using these column types.
PostgreSQL Constraint Types
---------------------------
SQLAlchemy supports Postgresql EXCLUDE constraints via the
:class:`ExcludeConstraint` class:
.. autoclass:: ExcludeConstraint
:members: __init__
For example::
from sqlalchemy.dialects.postgresql import ExcludeConstraint, TSRANGE
class RoomBookings(Base):
room = Column(Integer(), primary_key=True)
during = Column(TSRANGE())
__table_args__ = (
ExcludeConstraint(('room', '='), ('during', '&&')),
)
psycopg2
--------------
+1211
View File
File diff suppressed because it is too large Load Diff
+764 -6
View File
@@ -1,3 +1,5 @@
:orphan:
.. _glossary:
========
@@ -34,6 +36,12 @@ Glossary
in terms of one particular table alias or another, based on its position
within the join expression.
crud
An acronym meaning "Create, Update, Delete". The term in SQL refers to the
set of operations that create, modify and delete data from the database,
also known as :term:`DML`, and typically refers to the ``INSERT``,
``UPDATE``, and ``DELETE`` statements.
descriptor
descriptors
In Python, a descriptor is an object attribute with “binding behavior”, one whose attribute access has been overridden by methods in the `descriptor protocol <http://docs.python.org/howto/descriptor.html>`_.
@@ -73,6 +81,18 @@ Glossary
>>> inspect(m1).attrs.data.history.added
"some data"
DDL
An acronym for *Data Definition Language*. DDL is the subset
of SQL that relational databases use to configure tables, constraints,
and other permanent objects within a database schema. SQLAlchemy
provides a rich API for constructing and emitting DDL expressions.
.. seealso::
:ref:`metadata_toplevel`
`DDL (via Wikipedia) <http://en.wikipedia.org/wiki/Data_definition_language>`_
discriminator
A result-set column which is used during :term:`polymorphic` loading
to determine what kind of mapped class should be applied to a particular
@@ -95,6 +115,23 @@ Glossary
class which each represent a particular database column
or relationship to a related class.
identity map
A mapping between Python objects and their database identities.
The identity map is a collection that's associated with an
ORM :term:`session` object, and maintains a single instance
of every database object keyed to its identity. The advantage
to this pattern is that all operations which occur for a particular
database identity are transparently coordinated onto a single
object instance. When using an identity map in conjunction with
an :term:`isolated` transaction, having a reference
to an object that's known to have a particular primary key can
be considered from a practical standpoint to be a
proxy to the actual database row.
.. seealso::
Martin Fowler - Identity Map - http://martinfowler.com/eaaCatalog/identityMap.html
lazy load
lazy loads
In object relational mapping, a "lazy load" refers to an
@@ -263,6 +300,112 @@ Glossary
:doc:`orm/session`
Session
The container or scope for ORM database operations. Sessions
load instances from the database, track changes to mapped
instances and persist changes in a single unit of work when
flushed.
.. seealso::
:doc:`orm/session`
columns clause
The portion of the ``SELECT`` statement which enumerates the
SQL expressions to be returned in the result set. The expressions
follow the ``SELECT`` keyword directly and are a comma-separated
list of individual expressions.
E.g.:
.. sourcecode:: sql
SELECT user_account.name, user_account.email
FROM user_account WHERE user_account.name = 'fred'
Above, the list of columns ``user_acount.name``,
``user_account.email`` is the columns clause of the ``SELECT``.
WHERE clause
The portion of the ``SELECT`` statement which indicates criteria
by which rows should be filtered. It is a single SQL expression
which follows the keyword ``WHERE``.
.. sourcecode:: sql
SELECT user_account.name, user_account.email
FROM user_account
WHERE user_account.name = 'fred' AND user_account.status = 'E'
Above, the phrase ``WHERE user_account.name = 'fred' AND user_account.status = 'E'``
comprises the WHERE clause of the ``SELECT``.
FROM clause
The portion of the ``SELECT`` statement which incicates the initial
source of rows.
A simple ``SELECT`` will feature one or more table names in its
FROM clause. Multiple sources are separated by a comma:
.. sourcecode:: sql
SELECT user.name, address.email_address
FROM user, address
WHERE user.id=address.user_id
The FROM clause is also where explicit joins are specified. We can
rewrite the above ``SELECT`` using a single ``FROM`` element which consists
of a ``JOIN`` of the two tables:
.. sourcecode:: sql
SELECT user.name, address.email_address
FROM user JOIN address ON user.id=address.user_id
subquery
Refers to a ``SELECT`` statement that is embedded within an enclosing
``SELECT``.
A subquery comes in two general flavors, one known as a "scalar select"
which specifically must return exactly one row and one column, and the
other form which acts as a "derived table" and serves as a source of
rows for the FROM clause of another select. A scalar select is eligible
to be placed in the :term:`WHERE clause`, :term:`columns clause`,
ORDER BY clause or HAVING clause of the enclosing select, whereas the
derived table form is eligible to be placed in the FROM clause of the
enclosing ``SELECT``.
Examples:
1. a scalar subquery placed in the :term:`columns clause` of an enclosing
``SELECT``. The subquery in this example is a :term:`correlated subquery` because part
of the rows which it selects from are given via the enclosing statement.
.. sourcecode:: sql
SELECT id, (SELECT name FROM address WHERE address.user_id=user.id)
FROM user
2. a scalar subquery placed in the :term:`WHERE clause` of an enclosing
``SELECT``. This subquery in this example is not correlated as it selects a fixed result.
.. sourcecode:: sql
SELECT id, name FROM user
WHERE status=(SELECT status_id FROM status_code WHERE code='C')
3. a derived table subquery placed in the :term:`FROM clause` of an enclosing
``SELECT``. Such a subquery is almost always given an alias name.
.. sourcecode:: sql
SELECT user.id, user.name, ad_subq.email_address
FROM
user JOIN
(select user_id, email_address FROM address WHERE address_type='Q') AS ad_subq
ON user.id = ad_subq.user_id
correlates
correlated subquery
correlated subqueries
@@ -287,11 +430,626 @@ Glossary
The above subquery refers to the ``user_account`` table, which is not itself
in the ``FROM`` clause of this nested query. Instead, the ``user_account``
table is recieved from the enclosing query, where each row selected from
table is received from the enclosing query, where each row selected from
``user_account`` results in a distinct execution of the subquery.
A correlated subquery is nearly always present in the :term:`WHERE clause`
or :term:`columns clause` of the enclosing ``SELECT`` statement, and never
in the :term:`FROM clause`; this is because
the correlation can only proceed once the original source rows from the enclosing
statement's FROM clause are available.
A correlated subquery is in most cases present in the :term:`WHERE clause`
or :term:`columns clause` of the immediately enclosing ``SELECT``
statement, as well as in the ORDER BY or HAVING clause.
In less common cases, a correlated subquery may be present in the
:term:`FROM clause` of an enclosing ``SELECT``; in these cases the
correlation is typically due to the enclosing ``SELECT`` itself being
enclosed in the WHERE,
ORDER BY, columns or HAVING clause of another ``SELECT``, such as:
.. sourcecode:: sql
SELECT parent.id FROM parent
WHERE EXISTS (
SELECT * FROM (
SELECT child.id AS id, child.parent_id AS parent_id, child.pos AS pos
FROM child
WHERE child.parent_id = parent.id ORDER BY child.pos
LIMIT 3)
WHERE id = 7)
Correlation from one ``SELECT`` directly to one which encloses the correlated
query via its ``FROM``
clause is not possible, because the correlation can only proceed once the
original source rows from the enclosing statement's FROM clause are available.
ACID
ACID model
An acronym for "Atomicity, Consistency, Isolation,
Durability"; a set of properties that guarantee that
database transactions are processed reliably.
(via Wikipedia)
.. seealso::
:term:`atomicity`
:term:`consistency`
:term:`isolation`
:term:`durability`
http://en.wikipedia.org/wiki/ACID_Model
atomicity
Atomicity is one of the components of the :term:`ACID` model,
and requires that each transaction is "all or nothing":
if one part of the transaction fails, the entire transaction
fails, and the database state is left unchanged. An atomic
system must guarantee atomicity in each and every situation,
including power failures, errors, and crashes.
(via Wikipedia)
.. seealso::
:term:`ACID`
http://en.wikipedia.org/wiki/Atomicity_(database_systems)
consistency
Consistency is one of the compoments of the :term:`ACID` model,
and ensures that any transaction will
bring the database from one valid state to another. Any data
written to the database must be valid according to all defined
rules, including but not limited to :term:`constraints`, cascades,
triggers, and any combination thereof.
(via Wikipedia)
.. seealso::
:term:`ACID`
http://en.wikipedia.org/wiki/Consistency_(database_systems)
isolation
isolated
The isolation property of the :term:`ACID` model
ensures that the concurrent execution
of transactions results in a system state that would be
obtained if transactions were executed serially, i.e. one
after the other. Each transaction must execute in total
isolation i.e. if T1 and T2 execute concurrently then each
should remain independent of the other.
(via Wikipedia)
.. seealso::
:term:`ACID`
http://en.wikipedia.org/wiki/Isolation_(database_systems)
durability
Durability is a property of the :term:`ACID` model
which means that once a transaction has been committed,
it will remain so, even in the event of power loss, crashes,
or errors. In a relational database, for instance, once a
group of SQL statements execute, the results need to be stored
permanently (even if the database crashes immediately
thereafter).
(via Wikipedia)
.. seealso::
:term:`ACID`
http://en.wikipedia.org/wiki/Durability_(database_systems)
RETURNING
This is a non-SQL standard clause provided in various forms by
certain backends, which provides the service of returning a result
set upon execution of an INSERT, UPDATE or DELETE statement. Any set
of columns from the matched rows can be returned, as though they were
produced from a SELECT statement.
The RETURNING clause provides both a dramatic performance boost to
common update/select scenarios, including retrieval of inline- or
default- generated primary key values and defaults at the moment they
were created, as well as a way to get at server-generated
default values in an atomic way.
An example of RETURNING, idiomatic to Postgresql, looks like::
INSERT INTO user_account (name) VALUES ('new name') RETURNING id, timestamp
Above, the INSERT statement will provide upon execution a result set
which includes the values of the columns ``user_account.id`` and
``user_account.timestamp``, which above should have been generated as default
values as they are not included otherwise (but note any series of columns
or SQL expressions can be placed into RETURNING, not just default-value columns).
The backends that currently support
RETURNING or a similar construct are Postgresql, SQL Server, Oracle,
and Firebird. The Postgresql and Firebird implementations are generally
full featured, whereas the implementations of SQL Server and Oracle
have caveats. On SQL Server, the clause is known as "OUTPUT INSERTED"
for INSERT and UPDATE statements and "OUTPUT DELETED" for DELETE statements;
the key caveat is that triggers are not supported in conjunction with this
keyword. On Oracle, it is known as "RETURNING...INTO", and requires that the
value be placed into an OUT paramter, meaning not only is the syntax awkward,
but it can also only be used for one row at a time.
SQLAlchemy's :meth:`.UpdateBase.returning` system provides a layer of abstraction
on top of the RETURNING systems of these backends to provide a consistent
interface for returning columns. The ORM also includes many optimizations
that make use of RETURNING when available.
one to many
A style of :func:`~sqlalchemy.orm.relationship` which links
the primary key of the parent mapper's table to the foreign
key of a related table. Each unique parent object can
then refer to zero or more unique related objects.
The related objects in turn will have an implicit or
explicit :term:`many to one` relationship to their parent
object.
An example one to many schema (which, note, is identical
to the :term:`many to one` schema):
.. sourcecode:: sql
CREATE TABLE department (
id INTEGER PRIMARY KEY,
name VARCHAR(30)
)
CREATE TABLE employee (
id INTEGER PRIMARY KEY,
name VARCHAR(30),
dep_id INTEGER REFERENCES department(id)
)
The relationship from ``department`` to ``employee`` is
one to many, since many employee records can be associated with a
single department. A SQLAlchemy mapping might look like::
class Department(Base):
__tablename__ = 'department'
id = Column(Integer, primary_key=True)
name = Column(String(30))
employees = relationship("Employee")
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(30))
dep_id = Column(Integer, ForeignKey('department.id'))
.. seealso::
:term:`relationship`
:term:`many to one`
:term:`backref`
many to one
A style of :func:`~sqlalchemy.orm.relationship` which links
a foreign key in the parent mapper's table to the primary
key of a related table. Each parent object can
then refer to exactly zero or one related object.
The related objects in turn will have an implicit or
explicit :term:`one to many` relationship to any number
of parent objects that refer to them.
An example many to one schema (which, note, is identical
to the :term:`one to many` schema):
.. sourcecode:: sql
CREATE TABLE department (
id INTEGER PRIMARY KEY,
name VARCHAR(30)
)
CREATE TABLE employee (
id INTEGER PRIMARY KEY,
name VARCHAR(30),
dep_id INTEGER REFERENCES department(id)
)
The relationship from ``employee`` to ``department`` is
many to one, since many employee records can be associated with a
single department. A SQLAlchemy mapping might look like::
class Department(Base):
__tablename__ = 'department'
id = Column(Integer, primary_key=True)
name = Column(String(30))
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(30))
dep_id = Column(Integer, ForeignKey('department.id'))
department = relationship("Department")
.. seealso::
:term:`relationship`
:term:`one to many`
:term:`backref`
backref
bidirectional relationship
An extension to the :term:`relationship` system whereby two
distinct :func:`~sqlalchemy.orm.relationship` objects can be
mutually associated with each other, such that they coordinate
in memory as changes occur to either side. The most common
way these two relationships are constructed is by using
the :func:`~sqlalchemy.orm.relationship` function explicitly
for one side and specifying the ``backref`` keyword to it so that
the other :func:`~sqlalchemy.orm.relationship` is created
automatically. We can illustrate this against the example we've
used in :term:`one to many` as follows::
class Department(Base):
__tablename__ = 'department'
id = Column(Integer, primary_key=True)
name = Column(String(30))
employees = relationship("Employee", backref="department")
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(30))
dep_id = Column(Integer, ForeignKey('department.id'))
A backref can be applied to any relationship, including one to many,
many to one, and :term:`many to many`.
.. seealso::
:term:`relationship`
:term:`one to many`
:term:`many to one`
:term:`many to many`
many to many
A style of :func:`sqlalchemy.orm.relationship` which links two tables together
via an intermediary table in the middle. Using this configuration,
any number of rows on the left side may refer to any number of
rows on the right, and vice versa.
A schema where employees can be associated with projects:
.. sourcecode:: sql
CREATE TABLE employee (
id INTEGER PRIMARY KEY,
name VARCHAR(30)
)
CREATE TABLE project (
id INTEGER PRIMARY KEY,
name VARCHAR(30)
)
CREATE TABLE employee_project (
employee_id INTEGER PRIMARY KEY,
project_id INTEGER PRIMARY KEY,
FOREIGN KEY employee_id REFERENCES employee(id),
FOREIGN KEY project_id REFERENCES project(id)
)
Above, the ``employee_project`` table is the many-to-many table,
which naturally forms a composite primary key consisting
of the primary key from each related table.
In SQLAlchemy, the :func:`sqlalchemy.orm.relationship` function
can represent this style of relationship in a mostly
transparent fashion, where the many-to-many table is
specified using plain table metadata::
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key)
name = Column(String(30))
projects = relationship(
"Project",
secondary=Table('employee_project', Base.metadata,
Column("employee_id", Integer, ForeignKey('employee.id'),
primary_key=True),
Column("project_id", Integer, ForeignKey('project.id'),
primary_key=True)
),
backref="employees"
)
class Project(Base):
__tablename__ = 'project'
id = Column(Integer, primary_key)
name = Column(String(30))
Above, the ``Employee.projects`` and back-referencing ``Project.employees``
collections are defined::
proj = Project(name="Client A")
emp1 = Employee(name="emp1")
emp2 = Employee(name="emp2")
proj.employees.extend([emp1, emp2])
.. seealso::
:term:`association relationship`
:term:`relationship`
:term:`one to many`
:term:`many to one`
relationship
relationships
A connecting unit between two mapped classes, corresponding
to some relationship between the two tables in the database.
The relationship is defined using the SQLAlchemy function
:func:`~sqlalchemy.orm.relationship`. Once created, SQLAlchemy
inspects the arguments and underlying mappings involved
in order to classify the relationship as one of three types:
:term:`one to many`, :term:`many to one`, or :term:`many to many`.
With this classification, the relationship construct
handles the task of persisting the appropriate linkages
in the database in response to in-memory object associations,
as well as the job of loading object references and collections
into memory based on the current linkages in the
database.
.. seealso::
:ref:`relationship_config_toplevel`
association relationship
A two-tiered :term:`relationship` which links two tables
together using an association table in the middle. The
association relationship differs from a :term:`many to many`
relationship in that the many-to-many table is mapped
by a full class, rather than invisibly handled by the
:func:`sqlalchemy.orm.relationship` construct as in the case
with many-to-many, so that additional attributes are
explicitly available.
For example, if we wanted to associate employees with
projects, also storing the specific role for that employee
with the project, the relational schema might look like:
.. sourcecode:: sql
CREATE TABLE employee (
id INTEGER PRIMARY KEY,
name VARCHAR(30)
)
CREATE TABLE project (
id INTEGER PRIMARY KEY,
name VARCHAR(30)
)
CREATE TABLE employee_project (
employee_id INTEGER PRIMARY KEY,
project_id INTEGER PRIMARY KEY,
role_name VARCHAR(30),
FOREIGN KEY employee_id REFERENCES employee(id),
FOREIGN KEY project_id REFERENCES project(id)
)
A SQLAlchemy declarative mapping for the above might look like::
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key)
name = Column(String(30))
class Project(Base):
__tablename__ = 'project'
id = Column(Integer, primary_key)
name = Column(String(30))
class EmployeeProject(Base):
__tablename__ = 'employee_project'
employee_id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
project_id = Column(Integer, ForeignKey('project.id'), primary_key=True)
role_name = Column(String(30))
project = relationship("Project", backref="project_employees")
employee = relationship("Employee", backref="employee_projects")
Employees can be added to a project given a role name::
proj = Project(name="Client A")
emp1 = Employee(name="emp1")
emp2 = Employee(name="emp2")
proj.project_employees.extend([
EmployeeProject(employee=emp1, role="tech lead"),
EmployeeProject(employee=emp2, role="account executive")
])
.. seealso::
:term:`many to many`
constraint
constraints
constrained
Rules established within a relational database that ensure
the validity and consistency of data. Common forms
of constraint include :term:`primary key constraint`,
:term:`foreign key constraint`, and :term:`check constraint`.
candidate key
A :term:`relational algebra` term referring to an attribute or set
of attributes that form a uniquely identifying key for a
row. A row may have more than one candidate key, each of which
is suitable for use as the primary key of that row.
The primary key of a table is always a candidate key.
.. seealso::
:term:`primary key`
http://en.wikipedia.org/wiki/Candidate_key
primary key
primary key constraint
A :term:`constraint` that uniquely defines the characteristics
of each :term:`row`. The primary key has to consist of
characteristics that cannot be duplicated by any other row.
The primary key may consist of a single attribute or
multiple attributes in combination.
(via Wikipedia)
The primary key of a table is typically, though not always,
defined within the ``CREATE TABLE`` :term:`DDL`:
.. sourcecode:: sql
CREATE TABLE employee (
emp_id INTEGER,
emp_name VARCHAR(30),
dep_id INTEGER,
PRIMARY KEY (emp_id)
)
.. seealso::
http://en.wikipedia.org/wiki/Primary_Key
foreign key constraint
A referential constraint between two tables. A foreign key is a field or set of fields in a
relational table that matches a :term:`candidate key` of another table.
The foreign key can be used to cross-reference tables.
(via Wikipedia)
A foreign key constraint can be added to a table in standard
SQL using :term:`DDL` like the following:
.. sourcecode:: sql
ALTER TABLE employee ADD CONSTRAINT dep_id_fk
FOREIGN KEY (employee) REFERENCES department (dep_id)
.. seealso::
http://en.wikipedia.org/wiki/Foreign_key_constraint
check constraint
A check constraint is a
condition that defines valid data when adding or updating an
entry in a table of a relational database. A check constraint
is applied to each row in the table.
(via Wikipedia)
A check constraint can be added to a table in standard
SQL using :term:`DDL` like the following:
.. sourcecode:: sql
ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5);
.. seealso::
http://en.wikipedia.org/wiki/Check_constraint
unique constraint
unique key index
A unique key index can uniquely identify each row of data
values in a database table. A unique key index comprises a
single column or a set of columns in a single database table.
No two distinct rows or data records in a database table can
have the same data value (or combination of data values) in
those unique key index columns if NULL values are not used.
Depending on its design, a database table may have many unique
key indexes but at most one primary key index.
(via Wikipedia)
.. seealso::
http://en.wikipedia.org/wiki/Unique_key#Defining_unique_keys
transient
This describes one of the four major object states which
an object can have within a :term:`session`; a transient object
is a new object that doesn't have any database identity
and has not been associated with a session yet. When the
object is added to the session, it moves to the
:term:`pending` state.
.. seealso::
:ref:`session_object_states`
pending
This describes one of the four major object states which
an object can have within a :term:`session`; a pending object
is a new object that doesn't have any database identity,
but has been recently associated with a session. When
the session emits a flush and the row is inserted, the
object moves to the :term:`persistent` state.
.. seealso::
:ref:`session_object_states`
persistent
This describes one of the four major object states which
an object can have within a :term:`session`; a persistent object
is an object that has a database identity (i.e. a primary key)
and is currently associated with a session. Any object
that was previously :term:`pending` and has now been inserted
is in the persistent state, as is any object that's
been loaded by the session from the database. When a
persistent object is removed from a session, it is known
as :term:`detached`.
.. seealso::
:ref:`session_object_states`
detached
This describes one of the four major object states which
an object can have within a :term:`session`; a detached object
is an object that has a database identity (i.e. a primary key)
but is not associated with any session. An object that
was previously :term:`persistent` and was removed from its
session either because it was expunged, or the owning
session was closed, moves into the detached state.
The detached state is generally used when objects are being
moved between sessions or when being moved to/from an external
object cache.
.. seealso::
:ref:`session_object_states`
+9 -6
View File
@@ -1,3 +1,5 @@
:orphan:
.. _index_toplevel:
========================
@@ -9,8 +11,9 @@ Getting Started
A high level view and getting set up.
:ref:`Overview <overview>` |
:doc:`Overview <intro>` |
:ref:`Installation Guide <installation>` |
:doc:`Frequently Asked Questions <faq>` |
:doc:`Migration from 0.7 <changelog/migration_08>` |
:doc:`Glossary <glossary>` |
:doc:`Changelog catalog <changelog/index>`
@@ -74,11 +77,11 @@ are documented here. In contrast to the ORM's domain-centric mode of usage, the
:doc:`Connection Pooling <core/pooling>`
* **Schema Definition:**
:ref:`Tables and Columns <metadata_describing>` |
:ref:`Database Introspection (Reflection) <metadata_reflection>` |
:ref:`Insert/Update Defaults <metadata_defaults>` |
:ref:`Constraints and Indexes <metadata_constraints>` |
:ref:`Using Data Definition Language (DDL) <metadata_ddl>`
:ref:`Tables and Columns <metadata_describing_toplevel>` |
:ref:`Database Introspection (Reflection) <metadata_reflection_toplevel>` |
:ref:`Insert/Update Defaults <metadata_defaults_toplevel>` |
:ref:`Constraints and Indexes <metadata_constraints_toplevel>` |
:ref:`Using Data Definition Language (DDL) <metadata_ddl_toplevel>`
* **Datatypes:**
:ref:`Overview <types_toplevel>` |
+29 -20
View File
@@ -1,11 +1,7 @@
.. _overview_toplevel:
========
Overview
========
.. _overview:
========
Overview
========
@@ -93,11 +89,7 @@ SQLAlchemy supports installation using standard Python "distutils" or
using the ``setup.py`` script. The C extensions as well as Python 3 builds are supported.
* **Standard Setuptools** - When using `setuptools <http://pypi.python.org/pypi/setuptools/>`_,
SQLAlchemy can be installed via ``setup.py`` or ``easy_install``, and the C
extensions are supported. setuptools is not supported on Python 3 at the time
of this writing.
* **Distribute** - With `distribute <http://pypi.python.org/pypi/distribute/>`_,
SQLAlchemy can be installed via ``setup.py`` or ``easy_install``, and the C
extensions as well as Python 3 builds are supported.
extensions are supported.
* **pip** - `pip <http://pypi.python.org/pypi/pip/>`_ is an installer that
rides on top of ``setuptools`` or ``distribute``, replacing the usage
of ``easy_install``. It is often preferred for its simpler mode of usage.
@@ -117,6 +109,11 @@ Or with pip::
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.
.. note::
Beta releases of SQLAlchemy may not be present on Pypi, and may instead
require a direct download first.
Installing using setup.py
----------------------------------
@@ -131,26 +128,38 @@ SQLAlchemy includes C extensions which provide an extra speed boost for
dealing with result sets. Currently, the extensions are only supported on the
2.xx series of cPython, not Python 3 or Pypy.
setup.py will automatically build the extensions if an appropriate platform is
``setup.py`` will automatically build the extensions if an appropriate platform is
detected. If the build of the C extensions fails, due to missing compiler or
other issue, the setup process will output a warning message, and re-run the
build without the C extensions, upon completion reporting final status.
To run the build/install without even attempting to compile the C extensions,
pass the flag ``--without-cextensions`` to the ``setup.py`` script::
the ``DISABLE_SQLALCHEMY_CEXT`` environment variable may be specified. The
use case for this is either for special testing circumstances, or in the rare
case of compatibility/build issues not overcome by the usual "rebuild"
mechanism::
python setup.py --without-cextensions install
# *** only in SQLAlchemy 0.9.4 / 0.8.6 or greater ***
export DISABLE_SQLALCHEMY_CEXT=1; python setup.py install
Or with pip::
.. versionadded:: 0.9.4,0.8.6 Support for disabling the build of
C extensions using the ``DISABLE_SQLALCHEMY_CEXT`` environment variable
has been added. This allows control of C extension building whether or not
setuptools is available, and additionally works around the fact that
setuptools will possibly be **removing support** for command-line switches
such as ``--without-extensions`` in a future release.
pip install --global-option='--without-cextensions' SQLAlchemy
For versions of SQLAlchemy prior to 0.9.4 or 0.8.6, the
``--without-cextensions`` option may be used to disable the attempt to build
C extensions, provided setupools is in use, and provided the ``Feature``
construct is supported by the installed version of setuptools::
.. note::
python setup.py --without-cextensions install
Or with pip::
pip install --global-option='--without-cextensions' SQLAlchemy
The ``--without-cextensions`` flag is available **only** if ``setuptools``
or ``distribute`` is installed. It is not available on a plain Python ``distutils``
installation. The library will still install without the C extensions if they
cannot be built, however.
Installing on Python 3
----------------------------------
+3 -3
View File
@@ -112,7 +112,7 @@ from the database, the ``children`` collection stays empty.
Using Passive Deletes
----------------------
Use ``passive_deletes=True`` to disable child object loading on a DELETE
Use :paramref:`~.relationship.passive_deletes` to disable child object loading on a DELETE
operation, in conjunction with "ON DELETE (CASCADE|SET NULL)" on your database
to automatically cascade deletes to child objects::
@@ -142,7 +142,7 @@ to automatically cascade deletes to child objects::
* When using SQLite, foreign key support must be enabled explicitly.
See :ref:`sqlite_foreign_keys` for details.
When ``passive_deletes`` is applied, the ``children`` relationship will not be
When :paramref:`~.relationship.passive_deletes` is applied, the ``children`` relationship will not be
loaded into memory when an instance of ``MyClass`` is marked for deletion. The
``cascade="all, delete-orphan"`` *will* take effect for instances of
``MyOtherClass`` which are currently present in the session; however for
@@ -172,7 +172,7 @@ this collection is a ``list``::
Collections are not limited to lists. Sets, mutable sequences and almost any
other Python object that can act as a container can be used in place of the
default list, by specifying the ``collection_class`` option on
default list, by specifying the :paramref:`~.relationship.collection_class` option on
:func:`~sqlalchemy.orm.relationship`::
class Parent(Base):
+2
View File
@@ -1,3 +1,5 @@
:orphan:
.. _dep_interfaces_orm_toplevel:
Deprecated ORM Event Interfaces
+1 -1
View File
@@ -6,7 +6,7 @@ ORM Events
The ORM includes a wide variety of hooks available for subscription.
.. versionadded:: 0.7
The event supercedes the previous system of "extension" classes.
The event supersedes the previous system of "extension" classes.
For an introduction to the event API, see :ref:`event_toplevel`. Non-ORM events
such as those regarding connections and low-level statement execution are described in
-1
View File
@@ -2,5 +2,4 @@ ORM Exceptions
==============
.. automodule:: sqlalchemy.orm.exc
:show-inheritance:
:members:
+2
View File
@@ -10,6 +10,8 @@ API Reference
.. autofunction:: declarative_base
.. autofunction:: as_declarative
.. autoclass:: declared_attr
.. autofunction:: sqlalchemy.ext.declarative.api._declarative_constructor
+1 -1
View File
@@ -15,7 +15,7 @@ API Reference
:members:
.. autoclass:: Comparator
:show-inheritance:
.. autodata:: HYBRID_METHOD
+1 -1
View File
@@ -17,7 +17,7 @@ API Reference
.. autodata:: instrumentation_finders
.. autoclass:: ExtendedInstrumentationRegistry
:show-inheritance:
:members:
+2 -3
View File
@@ -12,15 +12,14 @@ API Reference
:members: _parents, coerce
.. autoclass:: Mutable
:show-inheritance:
:members:
.. autoclass:: MutableComposite
:show-inheritance:
:members:
.. autoclass:: MutableDict
:show-inheritance:
:members:
+25 -6
View File
@@ -227,7 +227,7 @@ subclasses:
# use with Query
session.query(entity).all()
It also accepts a second argument ``selectable`` which replaces the automatic
It also accepts a third argument ``selectable`` which replaces the automatic
join creation and instead selects directly from the selectable given. This
feature is normally used with "concrete" inheritance, described later, but can
be used with any kind of inheritance setup in the case that specialized SQL
@@ -469,10 +469,11 @@ subselect back to the parent ``companies`` table.
:func:`.orm.aliased` and :func:`.orm.with_polymorphic` constructs in conjunction
with :meth:`.Query.join`, ``any()`` and ``has()``.
Eager Loading of Specific Subtypes
++++++++++++++++++++++++++++++++++
Eager Loading of Specific or Polymorphic Subtypes
++++++++++++++++++++++++++++++++++++++++++++++++++
The :func:`.joinedload` and :func:`.subqueryload` options also support
The :func:`.joinedload`, :func:`.subqueryload`, :func:`.contains_eager` and
other loading-related options also support
paths which make use of :func:`~sqlalchemy.orm.interfaces.PropComparator.of_type`.
Below we load ``Company`` rows while eagerly loading related ``Engineer``
objects, querying the ``employee`` and ``engineer`` tables simultaneously::
@@ -481,10 +482,28 @@ objects, querying the ``employee`` and ``engineer`` tables simultaneously::
options(subqueryload_all(Company.employees.of_type(Engineer),
Engineer.machines))
As is the case with :meth:`.Query.join`, :func:`~sqlalchemy.orm.interfaces.PropComparator.of_type`
also can be used with eager loading and :func:`.orm.with_polymorphic`
at the same time, so that all sub-attributes of all referenced subtypes
can be loaded::
manager_and_engineer = with_polymorphic(
Employee, [Manager, Engineer],
aliased=True)
session.query(Company).\
options(
joinedload(Company.employees.of_type(manager_and_engineer))
)
)
.. versionadded:: 0.8
:func:`.joinedload` and :func:`.subqueryload` support
:func:`.joinedload`, :func:`.subqueryload`, :func:`.contains_eager`
and related loader options support
paths that are qualified with
:func:`~sqlalchemy.orm.interfaces.PropComparator.of_type`.
:func:`~sqlalchemy.orm.interfaces.PropComparator.of_type`, supporting
single target types as well as :func:`.orm.with_polymorphic` targets.
Single Table Inheritance
------------------------
+6 -14
View File
@@ -10,62 +10,54 @@ sections, are listed here.
.. autoclass:: sqlalchemy.orm.state.AttributeState
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: sqlalchemy.orm.instrumentation.ClassManager
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: sqlalchemy.orm.properties.ColumnProperty
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: sqlalchemy.orm.descriptor_props.CompositeProperty
:members:
:show-inheritance:
.. autoclass:: sqlalchemy.orm.interfaces._InspectionAttr
:members:
:show-inheritance:
.. autoclass:: sqlalchemy.orm.state.InstanceState
:members:
:show-inheritance:
.. autoclass:: sqlalchemy.orm.attributes.InstrumentedAttribute
:members: __get__, __set__, __delete__
:show-inheritance:
:undoc-members:
.. autoclass:: sqlalchemy.orm.interfaces.MapperProperty
:members:
:show-inheritance:
.. autodata:: sqlalchemy.orm.interfaces.NOT_EXTENSION
.. autoclass:: sqlalchemy.orm.interfaces.PropComparator
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: sqlalchemy.orm.properties.RelationshipProperty
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: sqlalchemy.orm.descriptor_props.SynonymProperty
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: sqlalchemy.orm.query.QueryContext
:members:
:show-inheritance:
.. autoclass:: sqlalchemy.orm.attributes.QueryableAttribute
:members:
:show-inheritance:
:inherited-members:
+11 -5
View File
@@ -1,3 +1,5 @@
.. _loading_toplevel:
.. currentmodule:: sqlalchemy.orm
Relationship Loading Techniques
@@ -170,15 +172,16 @@ the string ``'*'`` as the argument to any of these options::
session.query(MyClass).options(lazyload('*'))
Above, the ``lazyload('*')`` option will supercede the ``lazy`` setting
Above, the ``lazyload('*')`` option will supersede the ``lazy`` setting
of all :func:`.relationship` constructs in use for that query,
except for those which use the ``'dynamic'`` style of loading.
If some relationships specify
``lazy='joined'`` or ``lazy='subquery'``, for example,
using ``default_strategy(lazy='select')`` will unilaterally
cause all those relationships to use ``'select'`` loading.
using ``lazyload('*')`` will unilaterally
cause all those relationships to use ``'select'`` loading, e.g. emit a
SELECT statement when each attribute is accessed.
The option does not supercede loader options stated in the
The option does not supersede loader options stated in the
query, such as :func:`.eagerload`,
:func:`.subqueryload`, etc. The query below will still use joined loading
for the ``widget`` relationship::
@@ -326,6 +329,8 @@ joined eager loading goes the extra mile, and then ten miles further, to
absolutely ensure that it does not affect the end result of the query, only
the way collections and related objects are loaded, no matter what the format of the query is.
.. _what_kind_of_loading:
What Kind of Loading to Use ?
-----------------------------
@@ -373,7 +378,8 @@ references a scalar many-to-one reference.
* When using joined loading, the load of 100 objects will emit only one SQL statement. The join
will be a LEFT OUTER JOIN, and the total number of rows will be equal to 100 in all cases.
If you know that each parent definitely has a child (i.e. the foreign
key reference is NOT NULL), the joined load can be configured with ``innerjoin=True``, which is
key reference is NOT NULL), the joined load can be configured with
:paramref:`~.relationship.innerjoin` set to ``True``, which is
usually specified within the :func:`~sqlalchemy.orm.relationship`. For a load of objects where
there are many possible target references which may have not been loaded already, joined loading
with an INNER JOIN is extremely efficient.
+183 -8
View File
@@ -88,13 +88,19 @@ named according to the name of the column itself (specifically, the ``key``
attribute of :class:`.Column`). This behavior can be
modified in several ways.
.. _mapper_column_distinct_names:
Naming Columns Distinctly from Attribute Names
----------------------------------------------
A mapping by default shares the same name for a
:class:`.Column` as that of the mapped attribute.
The name assigned to the :class:`.Column` can be different,
as we illustrate here in a Declarative mapping::
:class:`.Column` as that of the mapped attribute - specifically
it matches the :attr:`.Column.key` attribute on :class:`.Column`, which
by default is the same as the :attr:`.Column.name`.
The name assigned to the Python attribute which maps to
:class:`.Column` can be different from either :attr:`.Column.name` or :attr:`.Column.key`
just by assigning it that way, as we illustrate here in a Declarative mapping::
class User(Base):
__tablename__ = 'user'
@@ -120,14 +126,50 @@ with the desired key::
'name': user_table.c.user_name,
})
In the next section we'll examine the usage of ``.key`` more closely.
.. _mapper_automated_reflection_schemes:
Automating Column Naming Schemes from Reflected Tables
------------------------------------------------------
In the previous section :ref:`mapper_column_distinct_names`, we showed how
a :class:`.Column` explicitly mapped to a class can have a different attribute
name than the column. But what if we aren't listing out :class:`.Column`
objects explicitly, and instead are automating the production of :class:`.Table`
objects using reflection (e.g. as described in :ref:`metadata_reflection_toplevel`)?
In this case we can make use of the :meth:`.DDLEvents.column_reflect` event
to intercept the production of :class:`.Column` objects and provide them
with the :attr:`.Column.key` of our choice::
@event.listens_for(Table, "column_reflect")
def column_reflect(inspector, table, column_info):
# set column.key = "attr_<lower_case_name>"
column_info['key'] = "attr_%s" % column_info['name'].lower()
With the above event, the reflection of :class:`.Column` objects will be intercepted
with our event that adds a new ".key" element, such as in a mapping as below::
class MyClass(Base):
__table__ = Table("some_table", Base.metadata,
autoload=True, autoload_with=some_engine)
If we want to qualify our event to only react for the specific :class:`.MetaData`
object above, we can check for it in our event::
@event.listens_for(Table, "column_reflect")
def column_reflect(inspector, table, column_info):
if table.metadata is Base.metadata:
# set column.key = "attr_<lower_case_name>"
column_info['key'] = "attr_%s" % column_info['name'].lower()
.. _column_prefix:
Naming All Columns with a Prefix
--------------------------------
A way to automate the assignment of a prefix to
the mapped attribute names relative to the column name
is to use ``column_prefix``::
A quick approach to prefix column names, typically when mapping
to an existing :class:`.Table` object, is to use ``column_prefix``::
class User(Base):
__table__ = user_table
@@ -136,9 +178,10 @@ is to use ``column_prefix``::
The above will place attribute names such as ``_user_id``, ``_user_name``,
``_password`` etc. on the mapped ``User`` class.
The classical version of the above::
This approach is uncommon in modern usage. For dealing with reflected
tables, a more flexible approach is to use that described in
:ref:`mapper_automated_reflection_schemes`.
mapper(User, user_table, column_prefix='_')
Using column_property for column level options
-----------------------------------------------
@@ -1055,6 +1098,9 @@ for each target table. SQLAlchemy refers to this as the "entity name"
pattern, which is described as a recipe at `Entity Name
<http://www.sqlalchemy.org/trac/wiki/UsageRecipes/EntityName>`_.
.. _mapping_constructors:
Constructors and Object Initialization
=======================================
@@ -1110,6 +1156,135 @@ of these events.
.. autofunction:: reconstructor
.. _mapper_version_counter:
Configuring a Version Counter
=============================
The :class:`.Mapper` supports management of a :term:`version id column`, which
is a single table column that increments or otherwise updates its value
each time an ``UPDATE`` to the mapped table occurs. This value is checked each
time the ORM emits an ``UPDATE`` or ``DELETE`` against the row to ensure that
the value held in memory matches the database value.
.. warning::
Because the versioning feature relies upon comparison of the **in memory**
record of an object, the feature only applies to the :meth:`.Session.flush`
process, where the ORM flushes individual in-memory rows to the database.
It does **not** take effect when performing
a multirow UPDATE or DELETE using :meth:`.Query.update` or :meth:`.Query.delete`
methods, as these methods only emit an UPDATE or DELETE statement but otherwise
do not have direct access to the contents of those rows being affected.
The purpose of this feature is to detect when two concurrent transactions
are modifying the same row at roughly the same time, or alternatively to provide
a guard against the usage of a "stale" row in a system that might be re-using
data from a previous transaction without refreshing (e.g. if one sets ``expire_on_commit=False``
with a :class:`.Session`, it is possible to re-use the data from a previous
transaction).
.. topic:: Concurrent transaction updates
When detecting concurrent updates within transactions, it is typically the
case that the database's transaction isolation level is below the level of
:term:`repeatable read`; otherwise, the transaction will not be exposed
to a new row value created by a concurrent update which conflicts with
the locally updated value. In this case, the SQLAlchemy versioning
feature will typically not be useful for in-transaction conflict detection,
though it still can be used for cross-transaction staleness detection.
The database that enforces repeatable reads will typically either have locked the
target row against a concurrent update, or is employing some form
of multi version concurrency control such that it will emit an error
when the transaction is committed. SQLAlchemy's version_id_col is an alternative
which allows version tracking to occur for specific tables within a transaction
that otherwise might not have this isolation level set.
.. seealso::
`Repeatable Read Isolation Level <http://www.postgresql.org/docs/9.1/static/transaction-iso.html#XACT-REPEATABLE-READ>`_ - Postgresql's implementation of repeatable read, including a description of the error condition.
Simple Version Counting
-----------------------
The most straightforward way to track versions is to add an integer column
to the mapped table, then establish it as the ``version_id_col`` within the
mapper options::
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
version_id = Column(Integer, nullable=False)
name = Column(String(50), nullable=False)
__mapper_args__ = {
"version_id_col": version_id
}
Above, the ``User`` mapping tracks integer versions using the column
``version_id``. When an object of type ``User`` is first flushed, the
``version_id`` column will be given a value of "1". Then, an UPDATE
of the table later on will always be emitted in a manner similar to the
following::
UPDATE user SET version_id=:version_id, name=:name
WHERE user.id = :user_id AND user.version_id = :user_version_id
{"name": "new name", "version_id": 2, "user_id": 1, "user_version_id": 1}
The above UPDATE statement is updating the row that not only matches
``user.id = 1``, it also is requiring that ``user.version_id = 1``, where "1"
is the last version identifier we've been known to use on this object.
If a transaction elsewhere has modifed the row independently, this version id
will no longer match, and the UPDATE statement will report that no rows matched;
this is the condition that SQLAlchemy tests, that exactly one row matched our
UPDATE (or DELETE) statement. If zero rows match, that indicates our version
of the data is stale, and a :class:`.StaleDataError` is raised.
.. _custom_version_counter:
Custom Version Counters / Types
-------------------------------
Other kinds of values or counters can be used for versioning. Common types include
dates and GUIDs. When using an alternate type or counter scheme, SQLAlchemy
provides a hook for this scheme using the ``version_id_generator`` argument,
which accepts a version generation callable. This callable is passed the value of the current
known version, and is expected to return the subsequent version.
For example, if we wanted to track the versioning of our ``User`` class
using a randomly generated GUID, we could do this (note that some backends
support a native GUID type, but we illustrate here using a simple string)::
import uuid
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
version_uuid = Column(String(32))
name = Column(String(50), nullable=False)
__mapper_args__ = {
'version_id_col':version_uuid,
'version_id_generator':lambda version: uuid.uuid4().hex
}
The persistence engine will call upon ``uuid.uuid4()`` each time a
``User`` object is subject to an INSERT or an UPDATE. In this case, our
version generation function can disregard the incoming value of ``version``,
as the ``uuid4()`` function
generates identifiers without any prerequisite value. If we were using
a sequential versioning scheme such as numeric or a special character system,
we could make use of the given ``version`` in order to help determine the
subsequent value.
.. seealso::
:ref:`custom_guid_type`
Class Mapping API
=================
-1
View File
@@ -21,7 +21,6 @@ Following is the full interface for the :class:`.Query` object.
.. autoclass:: sqlalchemy.orm.query.Query
:members:
:undoc-members:
ORM-Specific Query Constructs
-----------------------------
+165 -134
View File
@@ -44,7 +44,7 @@ a collection of items represented by the child::
parent_id = Column(Integer, ForeignKey('parent.id'))
To establish a bidirectional relationship in one-to-many, where the "reverse"
side is a many to one, specify the ``backref`` option::
side is a many to one, specify the :paramref:`~.relationship.backref` option::
class Parent(Base):
__tablename__ = 'parent'
@@ -75,8 +75,9 @@ attribute will be created::
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
Bidirectional behavior is achieved by specifying ``backref="parents"``,
which will place a one-to-many collection on the ``Child`` class::
Bidirectional behavior is achieved by setting
:paramref:`~.relationship.backref` to the value ``"parents"``, which
will place a one-to-many collection on the ``Child`` class::
class Parent(Base):
__tablename__ = 'parent'
@@ -84,11 +85,13 @@ which will place a one-to-many collection on the ``Child`` class::
child_id = Column(Integer, ForeignKey('child.id'))
child = relationship("Child", backref="parents")
.. _relationships_one_to_one:
One To One
~~~~~~~~~~~
One To One is essentially a bidirectional relationship with a scalar
attribute on both sides. To achieve this, the ``uselist=False`` flag indicates
attribute on both sides. To achieve this, the :paramref:`~.relationship.uselist` flag indicates
the placement of a scalar attribute instead of a collection on the "many" side
of the relationship. To convert one-to-many into one-to-one::
@@ -121,7 +124,7 @@ Many To Many
~~~~~~~~~~~~~
Many to Many adds an association table between two classes. The association
table is indicated by the ``secondary`` argument to
table is indicated by the :paramref:`~.relationship.secondary` argument to
:func:`.relationship`. Usually, the :class:`.Table` uses the :class:`.MetaData`
object associated with the declarative base class, so that the :class:`.ForeignKey`
directives can locate the remote tables with which to link::
@@ -142,8 +145,8 @@ directives can locate the remote tables with which to link::
id = Column(Integer, primary_key=True)
For a bidirectional relationship, both sides of the relationship contain a
collection. The ``backref`` keyword will automatically use
the same ``secondary`` argument for the reverse relationship::
collection. The :paramref:`~.relationship.backref` keyword will automatically use
the same :paramref:`~.relationship.secondary` argument for the reverse relationship::
association_table = Table('association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
@@ -161,7 +164,7 @@ the same ``secondary`` argument for the reverse relationship::
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
The ``secondary`` argument of :func:`.relationship` also accepts a callable
The :paramref:`~.relationship.secondary` argument of :func:`.relationship` also accepts a callable
that returns the ultimate argument, which is evaluated only when mappers are
first used. Using this, we can define the ``association_table`` at a later
point, as long as it's available to the callable after all module initialization
@@ -184,10 +187,12 @@ is accepted as well, matching the name of the table as stored in ``Base.metadata
secondary="association",
backref="parents")
.. _relationships_many_to_many_deletion:
Deleting Rows from the Many to Many Table
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A behavior which is unique to the ``secondary`` argument to :func:`.relationship`
A behavior which is unique to the :paramref:`~.relationship.secondary` argument to :func:`.relationship`
is that the :class:`.Table` which is specified here is automatically subject
to INSERT and DELETE statements, as objects are added or removed from the collection.
There is **no need to delete from this table manually**. The act of removing a
@@ -221,13 +226,13 @@ There are several possibilities here:
this feature, the database itself can be made to automatically delete rows in the
"secondary" table as referencing rows in "child" are deleted. SQLAlchemy
can be instructed to forego actively loading in the ``Child.parents``
collection in this case using the ``passive_deletes=True`` directive
on :func:`.relationship`; see :ref:`passive_deletes` for more details
collection in this case using the :paramref:`~.relationship.passive_deletes`
directive on :func:`.relationship`; see :ref:`passive_deletes` for more details
on this.
Note again, these behaviors are *only* relevant to the ``secondary`` option
Note again, these behaviors are *only* relevant to the :paramref:`~.relationship.secondary` option
used with :func:`.relationship`. If dealing with association tables that
are mapped explicitly and are *not* present in the ``secondary`` option
are mapped explicitly and are *not* present in the :paramref:`~.relationship.secondary` option
of a relevant :func:`.relationship`, cascade rules can be used instead
to automatically delete entities in reaction to a related entity being
deleted - see :ref:`unitofwork_cascades` for information on this feature.
@@ -238,16 +243,17 @@ deleted - see :ref:`unitofwork_cascades` for information on this feature.
Association Object
~~~~~~~~~~~~~~~~~~
The association object pattern is a variant on many-to-many: it's
used when your association table contains additional columns beyond those
which are foreign keys to the left and right tables. Instead of using the
``secondary`` argument, you map a new class directly to the association table.
The left side of the relationship references the association object via
one-to-many, and the association class references the right side via
many-to-one. Below we illustrate an association table mapped to the
``Association`` class which includes a column called ``extra_data``,
which is a string value that is stored along with each association
between ``Parent`` and ``Child``::
The association object pattern is a variant on many-to-many: it's used
when your association table contains additional columns beyond those
which are foreign keys to the left and right tables. Instead of using
the :paramref:`~.relationship.secondary` argument, you map a new class
directly to the association table. The left side of the relationship
references the association object via one-to-many, and the association
class references the right side via many-to-one. Below we illustrate
an association table mapped to the ``Association`` class which
includes a column called ``extra_data``, which is a string value that
is stored along with each association between ``Parent`` and
``Child``::
class Association(Base):
__tablename__ = 'association'
@@ -309,15 +315,14 @@ associated object, and a second to a target attribute.
.. note::
When using the association object pattern, it is
advisable that the association-mapped table not be used
as the ``secondary`` argument on a :func:`.relationship`
elsewhere, unless that :func:`.relationship` contains
the option ``viewonly=True``. SQLAlchemy otherwise
may attempt to emit redundant INSERT and DELETE
statements on the same table, if similar state is detected
on the related attribute as well as the associated
object.
When using the association object pattern, it is advisable that the
association-mapped table not be used as the
:paramref:`~.relationship.secondary` argument on a
:func:`.relationship` elsewhere, unless that :func:`.relationship`
contains the option :paramref:`~.relationship.viewonly` set to
``True``. SQLAlchemy otherwise may attempt to emit redundant INSERT
and DELETE statements on the same table, if similar state is
detected on the related attribute as well as the associated object.
.. _self_referential:
@@ -475,12 +480,13 @@ looks like:
AND node_1.data = ?
['subchild1', 'child2']
:meth:`.Query.join` also includes a feature known as ``aliased=True`` that
can shorten the verbosity self-referential joins, at the expense
of query flexibility. This feature
performs a similar "aliasing" step to that above, without the need for an
explicit entity. Calls to :meth:`.Query.filter` and similar subsequent to
the aliased join will **adapt** the ``Node`` entity to be that of the alias:
:meth:`.Query.join` also includes a feature known as
:paramref:`.Query.join.aliased` that can shorten the verbosity self-
referential joins, at the expense of query flexibility. This feature
performs a similar "aliasing" step to that above, without the need for
an explicit entity. Calls to :meth:`.Query.filter` and similar
subsequent to the aliased join will **adapt** the ``Node`` entity to
be that of the alias:
.. sourcecode:: python+sql
@@ -496,8 +502,9 @@ the aliased join will **adapt** the ``Node`` entity to be that of the alias:
WHERE node.data = ? AND node_1.data = ?
['subchild1', 'child2']
To add criterion to multiple points along a longer join, add ``from_joinpoint=True``
to the additional :meth:`~.Query.join` calls:
To add criterion to multiple points along a longer join, add
:paramref:`.Query.join.from_joinpoint` to the additional
:meth:`~.Query.join` calls:
.. sourcecode:: python+sql
@@ -530,8 +537,11 @@ calls::
reset_joinpoint().\
filter(Node.data == 'bar')
For an example of using ``aliased=True`` to arbitrarily join along a chain of self-referential
nodes, see :ref:`examples_xmlpersistence`.
For an example of using :paramref:`.Query.join.aliased` to
arbitrarily join along a chain of self-referential nodes, see
:ref:`examples_xmlpersistence`.
.. _self_referential_eager_loading:
Configuring Self-Referential Eager Loading
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -545,7 +555,7 @@ when joining to related items, so are compatible with self-referential
joining. However, to use eager loading with a self-referential relationship,
SQLAlchemy needs to be told how many levels deep it should join and/or query;
otherwise the eager load will not take place at all. This depth setting is
configured via ``join_depth``:
configured via :paramref:`~.relationships.join_depth`:
.. sourcecode:: python+sql
@@ -580,7 +590,7 @@ configured via ``join_depth``:
Linking Relationships with Backref
----------------------------------
The ``backref`` keyword argument was first introduced in :ref:`ormtutorial_toplevel`, and has been
The :paramref:`~.relationship.backref` keyword argument was first introduced in :ref:`ormtutorial_toplevel`, and has been
mentioned throughout many of the examples here. What does it actually do ? Let's start
with the canonical ``User`` and ``Address`` scenario::
@@ -607,8 +617,8 @@ The above configuration establishes a collection of ``Address`` objects on ``Use
``User.addresses``. It also establishes a ``.user`` attribute on ``Address`` which will
refer to the parent ``User`` object.
In fact, the ``backref`` keyword is only a common shortcut for placing a second
``relationship`` onto the ``Address`` mapping, including the establishment
In fact, the :paramref:`~.relationship.backref` keyword is only a common shortcut for placing a second
:func:`.relationship` onto the ``Address`` mapping, including the establishment
of an event listener on both sides which will mirror attribute operations
in both directions. The above configuration is equivalent to::
@@ -634,7 +644,7 @@ in both directions. The above configuration is equivalent to::
user = relationship("User", back_populates="addresses")
Above, we add a ``.user`` relationship to ``Address`` explicitly. On
both relationships, the ``back_populates`` directive tells each relationship
both relationships, the :paramref:`~.relationship.back_populates` directive tells each relationship
about the other one, indicating that they should establish "bidirectional"
behavior between each other. The primary effect of this configuration
is that the relationship adds event handlers to both attributes
@@ -673,27 +683,27 @@ The manipulation of the ``.addresses`` collection and the ``.user`` attribute
occurs entirely in Python without any interaction with the SQL database.
Without this behavior, the proper state would be apparent on both sides once the
data has been flushed to the database, and later reloaded after a commit or
expiration operation occurs. The ``backref``/``back_populates`` behavior has the advantage
expiration operation occurs. The :paramref:`~.relationship.backref`/:paramref:`~.relationship.back_populates` behavior has the advantage
that common bidirectional operations can reflect the correct state without requiring
a database round trip.
Remember, when the ``backref`` keyword is used on a single relationship, it's
Remember, when the :paramref:`~.relationship.backref` keyword is used on a single relationship, it's
exactly the same as if the above two relationships were created individually
using ``back_populates`` on each.
using :paramref:`~.relationship.back_populates` on each.
Backref Arguments
~~~~~~~~~~~~~~~~~~
We've established that the ``backref`` keyword is merely a shortcut for building
We've established that the :paramref:`~.relationship.backref` keyword is merely a shortcut for building
two individual :func:`.relationship` constructs that refer to each other. Part of
the behavior of this shortcut is that certain configurational arguments applied to
the :func:`.relationship`
will also be applied to the other direction - namely those arguments that describe
the relationship at a schema level, and are unlikely to be different in the reverse
direction. The usual case
here is a many-to-many :func:`.relationship` that has a ``secondary`` argument,
or a one-to-many or many-to-one which has a ``primaryjoin`` argument (the
``primaryjoin`` argument is discussed in :ref:`relationship_primaryjoin`). Such
here is a many-to-many :func:`.relationship` that has a :paramref:`~.relationship.secondary` argument,
or a one-to-many or many-to-one which has a :paramref:`~.relationship.primaryjoin` argument (the
:paramref:`~.relationship.primaryjoin` argument is discussed in :ref:`relationship_primaryjoin`). Such
as if we limited the list of ``Address`` objects to those which start with "tony"::
from sqlalchemy import Integer, ForeignKey, String, Column
@@ -728,17 +738,22 @@ of the relationship have this join condition applied::
"user".id = address.user_id AND address.email LIKE :email_1 || '%%'
>>>
This reuse of arguments should pretty much do the "right thing" - it uses
only arguments that are applicable, and in the case of a many-to-many
relationship, will reverse the usage of ``primaryjoin`` and ``secondaryjoin``
to correspond to the other direction (see the example in :ref:`self_referential_many_to_many`
for this).
This reuse of arguments should pretty much do the "right thing" - it
uses only arguments that are applicable, and in the case of a many-to-
many relationship, will reverse the usage of
:paramref:`~.relationship.primaryjoin` and
:paramref:`~.relationship.secondaryjoin` to correspond to the other
direction (see the example in :ref:`self_referential_many_to_many` for
this).
It's very often the case however that we'd like to specify arguments that
are specific to just the side where we happened to place the "backref".
This includes :func:`.relationship` arguments like ``lazy``, ``remote_side``,
``cascade`` and ``cascade_backrefs``. For this case we use the :func:`.backref`
function in place of a string::
It's very often the case however that we'd like to specify arguments
that are specific to just the side where we happened to place the
"backref". This includes :func:`.relationship` arguments like
:paramref:`~.relationship.lazy`,
:paramref:`~.relationship.remote_side`,
:paramref:`~.relationship.cascade` and
:paramref:`~.relationship.cascade_backrefs`. For this case we use
the :func:`.backref` function in place of a string::
# <other imports>
from sqlalchemy.orm import backref
@@ -761,18 +776,22 @@ arguments to be applied to the new relationship it creates.
One Way Backrefs
~~~~~~~~~~~~~~~~~
An unusual case is that of the "one way backref". This is where the "back-populating"
behavior of the backref is only desirable in one direction. An example of this
is a collection which contains a filtering ``primaryjoin`` condition. We'd like to append
items to this collection as needed, and have them populate the "parent" object on the
incoming object. However, we'd also like to have items that are not part of the collection,
but still have the same "parent" association - these items should never be in the
collection.
An unusual case is that of the "one way backref". This is where the
"back-populating" behavior of the backref is only desirable in one
direction. An example of this is a collection which contains a
filtering :paramref:`~.relationship.primaryjoin` condition. We'd
like to append items to this collection as needed, and have them
populate the "parent" object on the incoming object. However, we'd
also like to have items that are not part of the collection, but still
have the same "parent" association - these items should never be in
the collection.
Taking our previous example, where we established a ``primaryjoin`` that limited the
collection only to ``Address`` objects whose email address started with the word ``tony``,
the usual backref behavior is that all items populate in both directions. We wouldn't
want this behavior for a case like the following::
Taking our previous example, where we established a
:paramref:`~.relationship.primaryjoin` that limited the collection
only to ``Address`` objects whose email address started with the word
``tony``, the usual backref behavior is that all items populate in
both directions. We wouldn't want this behavior for a case like the
following::
>>> u1 = User()
>>> a1 = Address(email='mary')
@@ -786,7 +805,7 @@ the transaction committed and their attributes expired for a re-load, the ``addr
collection will hit the database on next access and no longer have this ``Address`` object
present, due to the filtering condition. But we can do away with this unwanted side
of the "backref" behavior on the Python side by using two separate :func:`.relationship` constructs,
placing ``back_populates`` only on one side::
placing :paramref:`~.relationship.back_populates` only on one side::
from sqlalchemy import Integer, ForeignKey, String, Column
from sqlalchemy.ext.declarative import declarative_base
@@ -828,14 +847,17 @@ will not append the ``Address`` object to the collection::
>>> a2 in u1.addresses
False
Of course, we've disabled some of the usefulness of ``backref`` here, in that
when we do append an ``Address`` that corresponds to the criteria of ``email.startswith('tony')``,
it won't show up in the ``User.addresses`` collection until the session is flushed,
and the attributes reloaded after a commit or expire operation. While we could
consider an attribute event that checks this criterion in Python, this starts
to cross the line of duplicating too much SQL behavior in Python. The backref behavior
itself is only a slight transgression of this philosophy - SQLAlchemy tries to keep
these to a minimum overall.
Of course, we've disabled some of the usefulness of
:paramref:`~.relationship.backref` here, in that when we do append an
``Address`` that corresponds to the criteria of
``email.startswith('tony')``, it won't show up in the
``User.addresses`` collection until the session is flushed, and the
attributes reloaded after a commit or expire operation. While we
could consider an attribute event that checks this criterion in
Python, this starts to cross the line of duplicating too much SQL
behavior in Python. The backref behavior itself is only a slight
transgression of this philosophy - SQLAlchemy tries to keep these to a
minimum overall.
.. _relationship_configure_joins:
@@ -937,8 +959,8 @@ one :class:`.Column` we need::
.. versionchanged:: 0.8
:func:`.relationship` can resolve ambiguity between foreign key targets on the
basis of the ``foreign_keys`` argument alone; the ``primaryjoin`` argument is no
longer needed in this situation.
basis of the ``foreign_keys`` argument alone; the :paramref:`~.relationship.primaryjoin`
argument is no longer needed in this situation.
.. _relationship_primaryjoin:
@@ -967,7 +989,7 @@ load those ``Address`` objects which specify a city of "Boston"::
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String)
addresses = relationship("Address",
boston_addresses = relationship("Address",
primaryjoin="and_(User.id==Address.user_id, "
"Address.city=='Boston')")
@@ -988,19 +1010,23 @@ to just ``city='Boston'``. When using Declarative, rudimentary SQL functions l
:func:`.and_` are automatically available in the evaluated namespace of a string
:func:`.relationship` argument.
The custom criteria we use in a ``primaryjoin`` is generally only significant
when SQLAlchemy is rendering SQL in order to load or represent this relationship.
That is, it's used
in the SQL statement that's emitted in order to perform a per-attribute lazy load, or when a join is
constructed at query time, such as via :meth:`.Query.join`, or via the eager "joined" or "subquery"
styles of loading. When in-memory objects are being manipulated, we can place any ``Address`` object
we'd like into the ``boston_addresses`` collection, regardless of what the value of the ``.city``
attribute is. The objects will remain present in the collection until the attribute is expired
and re-loaded from the database where the criterion is applied. When
a flush occurs, the objects inside of ``boston_addresses`` will be flushed unconditionally, assigning
value of the primary key ``user.id`` column onto the foreign-key-holding ``address.user_id`` column
for each row. The ``city`` criteria has no effect here, as the flush process only cares about synchronizing primary
key values into referencing foreign key values.
The custom criteria we use in a :paramref:`~.relationship.primaryjoin`
is generally only significant when SQLAlchemy is rendering SQL in
order to load or represent this relationship. That is, it's used in
the SQL statement that's emitted in order to perform a per-attribute
lazy load, or when a join is constructed at query time, such as via
:meth:`.Query.join`, or via the eager "joined" or "subquery" styles of
loading. When in-memory objects are being manipulated, we can place
any ``Address`` object we'd like into the ``boston_addresses``
collection, regardless of what the value of the ``.city`` attribute
is. The objects will remain present in the collection until the
attribute is expired and re-loaded from the database where the
criterion is applied. When a flush occurs, the objects inside of
``boston_addresses`` will be flushed unconditionally, assigning value
of the primary key ``user.id`` column onto the foreign-key-holding
``address.user_id`` column for each row. The ``city`` criteria has no
effect here, as the flush process only cares about synchronizing
primary key values into referencing foreign key values.
.. _relationship_custom_foreign:
@@ -1008,14 +1034,17 @@ Creating Custom Foreign Conditions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Another element of the primary join condition is how those columns
considered "foreign" are determined. Usually, some subset
of :class:`.Column` objects will specify :class:`.ForeignKey`, or otherwise
be part of a :class:`.ForeignKeyConstraint` that's relevant to the join condition.
:func:`.relationship` looks to this foreign key status as it decides
how it should load and persist data for this relationship. However, the
``primaryjoin`` argument can be used to create a join condition that
doesn't involve any "schema" level foreign keys. We can combine ``primaryjoin``
along with ``foreign_keys`` and ``remote_side`` explicitly in order to
considered "foreign" are determined. Usually, some subset of
:class:`.Column` objects will specify :class:`.ForeignKey`, or
otherwise be part of a :class:`.ForeignKeyConstraint` that's relevant
to the join condition. :func:`.relationship` looks to this foreign key
status as it decides how it should load and persist data for this
relationship. However, the :paramref:`~.relationship.primaryjoin`
argument can be used to create a join condition that doesn't involve
any "schema" level foreign keys. We can combine
:paramref:`~.relationship.primaryjoin` along with
:paramref:`~.relationship.foreign_keys` and
:paramref:`~.relationship.remote_side` explicitly in order to
establish such a join.
Below, a class ``HostEntry`` joins to itself, equating the string ``content``
@@ -1052,10 +1081,10 @@ The above relationship will produce a join like::
ON host_entry_1.ip_address = CAST(host_entry.content AS INET)
An alternative syntax to the above is to use the :func:`.foreign` and
:func:`.remote` :term:`annotations`, inline within the ``primaryjoin`` expression.
:func:`.remote` :term:`annotations`, inline within the :paramref:`~.relationship.primaryjoin` expression.
This syntax represents the annotations that :func:`.relationship` normally
applies by itself to the join condition given the ``foreign_keys`` and
``remote_side`` arguments; the functions are provided in the API in the
applies by itself to the join condition given the :paramref:`~.relationship.foreign_keys` and
:paramref:`~.relationship.remote_side` arguments; the functions are provided in the API in the
rare case that :func:`.relationship` can't determine the exact location
of these features on its own::
@@ -1081,10 +1110,10 @@ of these features on its own::
Self-Referential Many-to-Many Relationship
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Many to many relationships can be customized by one or both of ``primaryjoin``
and ``secondaryjoin`` - the latter is significant for a relationship that
specifies a many-to-many reference using the ``secondary`` argument.
A common situation which involves the usage of ``primaryjoin`` and ``secondaryjoin``
Many to many relationships can be customized by one or both of :paramref:`~.relationship.primaryjoin`
and :paramref:`~.relationship.secondaryjoin` - the latter is significant for a relationship that
specifies a many-to-many reference using the :paramref:`~.relationship.secondary` argument.
A common situation which involves the usage of :paramref:`~.relationship.primaryjoin` and :paramref:`~.relationship.secondaryjoin`
is when establishing a many-to-many relationship from a class to itself, as shown below::
from sqlalchemy import Integer, ForeignKey, String, Column, Table
@@ -1110,11 +1139,11 @@ is when establishing a many-to-many relationship from a class to itself, as show
)
Where above, SQLAlchemy can't know automatically which columns should connect
to which for the ``right_nodes`` and ``left_nodes`` relationships. The ``primaryjoin``
and ``secondaryjoin`` arguments establish how we'd like to join to the association table.
to which for the ``right_nodes`` and ``left_nodes`` relationships. The :paramref:`~.relationship.primaryjoin`
and :paramref:`~.relationship.secondaryjoin` arguments establish how we'd like to join to the association table.
In the Declarative form above, as we are declaring these conditions within the Python
block that corresponds to the ``Node`` class, the ``id`` variable is available directly
as the ``Column`` object we wish to join with.
as the :class:`.Column` object we wish to join with.
A classical mapping situation here is similar, where ``node_to_node`` can be joined
to ``node.c.id``::
@@ -1145,9 +1174,12 @@ to ``node.c.id``::
)})
Note that in both examples, the ``backref`` keyword specifies a ``left_nodes``
backref - when :func:`.relationship` creates the second relationship in the reverse
direction, it's smart enough to reverse the ``primaryjoin`` and ``secondaryjoin`` arguments.
Note that in both examples, the :paramref:`~.relationship.backref`
keyword specifies a ``left_nodes`` backref - when
:func:`.relationship` creates the second relationship in the reverse
direction, it's smart enough to reverse the
:paramref:`~.relationship.primaryjoin` and
:paramref:`~.relationship.secondaryjoin` arguments.
Building Query-Enabled Properties
@@ -1155,11 +1187,11 @@ Building Query-Enabled Properties
Very ambitious custom join conditions may fail to be directly persistable, and
in some cases may not even load correctly. To remove the persistence part of
the equation, use the flag ``viewonly=True`` on the
the equation, use the flag :paramref:`~.relationship.viewonly` on the
:func:`~sqlalchemy.orm.relationship`, which establishes it as a read-only
attribute (data written to the collection will be ignored on flush()).
However, in extreme cases, consider using a regular Python property in
conjunction with :class:`~sqlalchemy.orm.query.Query` as follows:
conjunction with :class:`.Query` as follows:
.. sourcecode:: python+sql
@@ -1220,7 +1252,7 @@ identifiers were populated manually (again essentially bypassing
:func:`~sqlalchemy.orm.relationship`).
To enable the usage of a supplementary UPDATE statement,
we use the ``post_update`` option
we use the :paramref:`~.relationship.post_update` option
of :func:`.relationship`. This specifies that the linkage between the
two rows should be created using an UPDATE statement after both rows
have been INSERTED; it also causes the rows to be de-associated with
@@ -1228,7 +1260,7 @@ each other via UPDATE before a DELETE is emitted. The flag should
be placed on just *one* of the relationships, preferably the
many-to-one side. Below we illustrate
a complete example, including two :class:`.ForeignKey` constructs, one which
specifies ``use_alter=True`` to help with emitting CREATE TABLE statements::
specifies :paramref:`~.ForeignKey.use_alter` to help with emitting CREATE TABLE statements::
from sqlalchemy import Integer, ForeignKey, Column
from sqlalchemy.ext.declarative import declarative_base
@@ -1332,13 +1364,11 @@ as illustrated below::
The above mapping features a composite :class:`.ForeignKeyConstraint`
bridging the ``widget_id`` and ``favorite_entry_id`` columns. To ensure
that ``Widget.widget_id`` remains an "autoincrementing" column we specify
``autoincrement='ignore_fk'`` on :class:`.Column`, and additionally on each
:paramref:`~.Column.autoincrement` to the value ``"ignore_fk"``
on :class:`.Column`, and additionally on each
:func:`.relationship` we must limit those columns considered as part of
the foreign key for the purposes of joining and cross-population.
.. versionadded:: 0.7.4
``autoincrement='ignore_fk'`` on :class:`.Column`\ .
.. _passive_updates:
Mutable Primary Keys / Update Cascades
@@ -1354,13 +1384,13 @@ of sync for any moment.
For databases that don't support this, such as SQLite and
MySQL without their referential integrity options turned
on, the ``passive_updates`` flag can
on, the :paramref:`~.relationship.passive_updates` flag can
be set to ``False``, most preferably on a one-to-many or
many-to-many :func:`.relationship`, which instructs
SQLAlchemy to issue UPDATE statements individually for
objects referenced in the collection, loading them into
memory if not already locally present. The
``passive_updates`` flag can also be ``False`` in
:paramref:`~.relationship.passive_updates` flag can also be ``False`` in
conjunction with ON UPDATE CASCADE functionality,
although in that case the unit of work will be issuing
extra SELECT and UPDATE statements unnecessarily.
@@ -1385,16 +1415,17 @@ A typical mutable primary key setup might look like::
ForeignKey('user.username', onupdate="cascade")
)
``passive_updates`` is set to ``True`` by default,
:paramref:`~.relationship.passive_updates` is set to ``True`` by default,
indicating that ON UPDATE CASCADE is expected to be in
place in the usual case for foreign keys that expect
to have a mutating parent key.
``passive_updates=False`` may be configured on any
A :paramref:`~.relationship.passive_updates` setting of False may be configured on any
direction of relationship, i.e. one-to-many, many-to-one,
and many-to-many, although it is much more effective when
placed just on the one-to-many or many-to-many side.
Configuring the ``passive_updates=False`` only on the
Configuring the :paramref:`~.relationship.passive_updates`
to False only on the
many-to-one side will have only a partial effect, as the
unit of work searches only through the current identity
map for objects that may be referencing the one with a
+603 -264
View File
File diff suppressed because it is too large Load Diff
+59 -27
View File
@@ -64,10 +64,12 @@ 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 what's being generated.
The return value of :func:`.create_engine` is an instance of :class:`.Engine`, and it represents
the core interface to the database, adapted through a **dialect** that handles the details
of the database and DBAPI in use. In this case the SQLite dialect will interpret instructions
to the Python built-in ``sqlite3`` module.
The return value of :func:`.create_engine` is an instance of
:class:`.Engine`, and it represents the core interface to the
database, adapted through a :term:`dialect` that handles the details
of the database and :term:`DBAPI` in use. In this case the SQLite
dialect will interpret instructions to the Python built-in ``sqlite3``
module.
The :class:`.Engine` has not actually tried to connect to the database yet; that happens
only the first time it is asked to perform a task against the database. We can illustrate
@@ -86,6 +88,11 @@ connection pool where it will be reused on subsequent statement executions. Whi
:class:`.Engine` here, this isn't typically necessary when using the ORM, where the :class:`.Engine`,
once created, is used behind the scenes by the ORM as we'll see shortly.
.. seealso::
:ref:`database_urls` - includes examples of :func:`.create_engine`
connecting to several kinds of databases with links to more information.
Declare a Mapping
=================
@@ -424,7 +431,7 @@ We can add more ``User`` objects at once using
... User('mary', 'Mary Contrary', 'xxg527'),
... User('fred', 'Fred Flinstone', 'blah')])
Also, Ed has already decided his password isn't too secure, so lets change it:
Also, we've decided the password for Ed isn't too secure, so lets change it:
.. sourcecode:: python+sql
@@ -755,19 +762,19 @@ Common Filter Operators
Here's a rundown of some of the most common operators used in :func:`~sqlalchemy.orm.query.Query.filter`:
* equals::
* :meth:`equals <.ColumnOperators.__eq__>`::
query.filter(User.name == 'ed')
* not equals::
* :meth:`not equals <.ColumnOperators.__ne__>`::
query.filter(User.name != 'ed')
* LIKE::
* :meth:`LIKE <.ColumnOperators.like>`::
query.filter(User.name.like('%ed%'))
* IN::
* :meth:`IN <.ColumnOperators.in_>`::
query.filter(User.name.in_(['ed', 'wendy', 'jack']))
@@ -775,19 +782,25 @@ Here's a rundown of some of the most common operators used in :func:`~sqlalchemy
query.filter(User.name.in_(session.query(User.name).filter(User.name.like('%ed%'))))
* NOT IN::
* :meth:`NOT IN <.ColumnOperators.notin_>`::
query.filter(~User.name.in_(['ed', 'wendy', 'jack']))
* IS NULL::
* :meth:`IS NULL <.ColumnOperators.is_>`::
filter(User.name == None)
* IS NOT NULL::
# alternatively, if pep8/linters are a concern
query.filter(User.name.is_(None))
* :meth:`IS NOT NULL <.ColumnOperators.isnot>`::
filter(User.name != None)
* AND::
# alternatively, if pep8/linters are a concern
query.filter(User.name.isnot(None))
* :func:`AND <.sql.expression.and_>`::
from sqlalchemy import and_
filter(and_(User.name == 'ed', User.fullname == 'Ed Jones'))
@@ -795,16 +808,26 @@ Here's a rundown of some of the most common operators used in :func:`~sqlalchemy
# or call filter()/filter_by() multiple times
filter(User.name == 'ed').filter(User.fullname == 'Ed Jones')
* OR::
.. note:: Make sure you use :func:`.and_` and **not** the
Python ``and`` operator!
* :func:`OR <.sql.expression.or_>`::
from sqlalchemy import or_
filter(or_(User.name == 'ed', User.name == 'wendy'))
* match::
.. note:: Make sure you use :func:`.or_` and **not** the
Python ``or`` operator!
* :meth:`MATCH <.ColumnOperators.match>`::
query.filter(User.name.match('wendy'))
The contents of the match parameter are database backend specific.
.. note::
:meth:`~.ColumnOperators.match` uses a database-specific ``MATCH``
or ``CONTAINS`` function; its behavior will vary by backend and is not
available on some backends such as SQLite.
Returning Lists and Scalars
---------------------------
@@ -880,6 +903,8 @@ an error:
('%ed', 99)
{stop}No row was found for one()
.. _orm_tutorial_literal_sql:
Using Literal SQL
-----------------
@@ -1100,6 +1125,8 @@ of the ``User`` primary key directly:
()
{stop}4
.. _orm_tutorial_relationship:
Building a Relationship
=======================
@@ -1244,9 +1271,10 @@ using any SQL:
>>> jack.addresses[1].user
<User('jack','Jack Bean', 'gjffdd')>
Let's add and commit ``Jack Bean`` to the database. ``jack`` as well as the
two ``Address`` members in his ``addresses`` collection are both added to the
session at once, using a process known as **cascading**:
Let's add and commit ``Jack Bean`` to the database. ``jack`` as well
as the two ``Address`` members in the corresponding ``addresses``
collection are both added to the session at once, using a process
known as **cascading**:
.. sourcecode:: python+sql
@@ -1840,8 +1868,8 @@ including the cascade configuration (we'll leave the constructor out too)::
... def __repr__(self):
... return "<User('%s','%s', '%s')>" % (self.name, self.fullname, self.password)
Then we recreate ``Address``, noting that in this case we've created the ``Address.user`` relationship
via the ``User`` class already::
Then we recreate ``Address``, noting that in this case we've created
the ``Address.user`` relationship via the ``User`` class already::
>>> class Address(Base):
... __tablename__ = 'addresses'
@@ -1852,9 +1880,10 @@ via the ``User`` class already::
... def __repr__(self):
... return "<Address('%s')>" % self.email_address
Now when we load Jack (below using :meth:`~.Query.get`, which loads by primary key),
removing an address from his ``addresses`` collection will result in that
``Address`` being deleted:
Now when we load the user ``jack`` (below using :meth:`~.Query.get`,
which loads by primary key), removing an address from the
corresponding ``addresses`` collection will result in that ``Address``
being deleted:
.. sourcecode:: python+sql
@@ -1895,7 +1924,8 @@ removing an address from his ``addresses`` collection will result in that
('jack@google.com', 'j25@yahoo.com')
{stop}1
Deleting Jack will delete both Jack and his remaining ``Address``:
Deleting Jack will delete both Jack and the remaining ``Address`` associated
with the user:
.. sourcecode:: python+sql
@@ -1935,6 +1965,8 @@ Deleting Jack will delete both Jack and his remaining ``Address``:
the ``ON DELETE CASCADE`` functionality of the relational database.
See :ref:`passive_deletes` for details.
.. _orm_tutorial_many_to_many:
Building a Many To Many Relationship
====================================
@@ -2114,8 +2146,8 @@ keyword string 'firstpost'":
('firstpost',)
{stop}[BlogPost("Wendy's Blog Post", 'This is a test', <User('wendy','Wendy Williams', 'foobar')>)]
If we want to look up just Wendy's posts, we can tell the query to narrow down
to her as a parent:
If we want to look up posts owned by the user ``wendy``, we can tell
the query to narrow down to that ``User`` object as a parent:
.. sourcecode:: python+sql
+2
View File
@@ -1 +1,3 @@
mako
changelog>=0.3.2
sphinx-paramlinks
+7
View File
@@ -0,0 +1,7 @@
/**
* jQuery.browser.mobile (http://detectmobilebrowser.com/)
*
* jQuery.browser.mobile will be true if the browser is a mobile device
*
**/
(function(a){(jQuery.browser=jQuery.browser||{}).mobile=/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))})(navigator.userAgent||navigator.vendor||window.opera);
+188 -36
View File
@@ -1,8 +1,12 @@
/* global */
.body-background {
background-color: #FDFBFC;
}
body {
background-color: #FDFBFC;
margin:38px;
margin:0 38px;
color:#333333;
}
@@ -61,6 +65,8 @@ a.headerlink:hover {
#docs-container {
max-width:1000px;
margin: 0 auto;
position: relative;
}
@@ -77,21 +83,41 @@ a.headerlink:hover {
font-family:Verdana,sans-serif;
font-size:.9em;
position: relative;
}
#docs-top-navigation,
#docs-bottom-navigation {
#docs-sidebar-popout,
#docs-bottom-navigation,
#index-nav {
font-family: Verdana, sans-serif;
background-color: #FBFBEE;
border: solid 1px #CCC;
padding:10px;
font-size:.8em;
}
#docs-top-navigation {
margin:10px 0px 10px 0px;
line-height:1.2em;
#docs-bottom-navigation,
#index-nav {
padding:10px;
}
#docs-sidebar-popout {
font-size:.75em;
}
#docs-sidebar-popout p,
#docs-sidebar-popout form {
margin:5px 0 5px 0px;
}
#docs-sidebar-popout h3 {
margin:0 0 10px 0;
}
#docs-version-header {
position: absolute;
right: 0;
bottom: 0;
}
.docs-navigation-links {
@@ -120,13 +146,10 @@ a.headerlink:hover {
font-size:1.6em;
}
#docs-search {
float:right;
}
#docs-top-page-control {
float:right;
width:350px;
position: absolute;
right: 20px;
bottom: 14px;
}
#docs-top-page-control ul {
@@ -149,13 +172,11 @@ a.headerlink:hover {
/* content container, sidebar */
#docs-body-container {
background-color:#EFEFEF;
border: solid 1px #CCC;
}
#docs-body,
#docs-sidebar
#docs-sidebar,
#index-nav
{
/*font-family: helvetica, arial, sans-serif;
font-size:.9em;*/
@@ -166,15 +187,105 @@ a.headerlink:hover {
}
#docs-body {
min-height: 700px;
}
#docs-sidebar > ul {
font-size:.85em;
}
#fixed-sidebar {
position: relative;
}
#fixed-sidebar.withsidebar {
float: left;
width:224px;
}
#fixed-sidebar.preautomated {
position: fixed;
float: none;
top:0;
bottom: 0;
}
#fixed-sidebar.automated {
position: fixed;
float: none;
top: 120px;
min-height: 0;
}
#docs-sidebar {
float:left;
width:212px;
padding: 10px 0 0 15px;
font-size:.85em;
border: solid 1px #CCC;
z-index: 3;
background-color: #EFEFEF;
}
#index-nav {
position: relative;
margin-top:10px;
padding:0 10px;
}
#index-nav form {
padding-top:10px;
float:right;
}
#sidebar-paginate {
position: absolute;
bottom: 4.5em;
left: 10px;
}
#sidebar-topnav {
position: absolute;
bottom: 3em;
left: 10px;
}
#sidebar-search {
position: absolute;
bottom: 1em;
left: 10px;
}
#docs-sidebar {
top: 132px;
bottom: 0;
min-height: 0;
overflow-y: auto;
margin-top:5px;
width:212px;
padding-left:10px;
}
#docs-sidebar-popout {
height:120px;
max-height: 120px;
width:212px;
padding-left:10px;
padding-top:10px;
position: relative;
}
#fixed-sidebar.preautomated #docs-sidebar,
#fixed-sidebar.preautomated #docs-sidebar-popout {
position:absolute;
}
#fixed-sidebar.preautomated #docs-sidebar:after {
content: " ";
display:block;
height: 150px;
}
#docs-sidebar h3, #docs-sidebar h4 {
@@ -184,9 +295,13 @@ a.headerlink:hover {
font-size: 1.1em;
font-weight: normal;
margin: 10px 0 0 -15px;
padding: 5px 10px 5px 10px;
padding: 5px 10px 5px 15px;
text-shadow: 1px 1px 0 white;
width:210px;
/*width:210px;*/
}
#docs-sidebar h3:first-child {
margin-top: 0px;
}
#docs-sidebar h3 a, #docs-sidebar h4 a {
@@ -206,16 +321,22 @@ a.headerlink:hover {
margin-left: 20px;
}
#docs-body {
background-color:#FFFFFF;
padding:1px 10px 10px 10px;
border: solid 1px #CCC;
margin-top:10px;
}
#docs-body.withsidebar {
margin: 0 0 0 230px;
border-left:3px solid #DFDFDF;
margin-left: 230px;
}
#docs-body h1,
#docs-body h2,
#docs-body h3,
@@ -223,12 +344,13 @@ a.headerlink:hover {
font-family:Helvetica, Arial, sans-serif;
}
#docs-body h1 {
#docs-body #sqlalchemy-documentation h1 {
/* hide the <h1> for each content section. */
display:none;
font-size:2.0em;
}
#docs-body h2 {
font-size:1.8em;
border-top:1px solid;
@@ -324,7 +446,15 @@ div.note, div.warning, p.deprecated, div.topic, div.admonition {
}
div.admonition, div.topic, p.deprecated, p.versionadded, p.versionchanged {
div.faq {
background-color: #EFEFEF;
}
div.faq ul {
list-style: square outside none;
}
div.admonition, div.topic, .deprecated, .versionadded, .versionchanged {
border:1px solid #CCCCCC;
padding:5px 10px;
font-size:.9em;
@@ -332,6 +462,35 @@ div.admonition, div.topic, p.deprecated, p.versionadded, p.versionchanged {
box-shadow: 2px 2px 3px #DFDFDF;
}
div.sidebar {
background-color: #FFFFEE;
border: 1px solid #DDDDBB;
float: right;
margin: 10px 0 10px 1em;
padding: 7px 7px 0;
width: 40%;
font-size:.9em;
}
p.sidebar-title {
font-weight: bold;
}
/* grrr sphinx changing your document structures, removing classes.... */
.versionadded .versionmodified,
.versionchanged .versionmodified,
.deprecated .versionmodified,
.versionadded > p:first-child > span:first-child,
.versionchanged > p:first-child > span:first-child,
.deprecated > p:first-child > span:first-child
{
background-color: #ECF0F3;
color: #990000;
font-style: italic;
}
div.inherited-member {
border:1px solid #CCCCCC;
padding:5px 5px;
@@ -339,14 +498,6 @@ div.inherited-member {
box-shadow: 2px 2px 3px #DFDFDF;
}
p.versionadded span.versionmodified,
p.versionchanged span.versionmodified,
p.deprecated span.versionmodified {
background-color: #ECF0F3;
color: #990000;
font-style: italic;
}
div.warning .admonition-title {
color:#FF0000;
}
@@ -366,11 +517,12 @@ dl.method > dt,
dl.class > dt,
dl.exception > dt
{
background-color:#F0F0F0;
background-color: #EFEFEF;
margin:25px -10px 10px 10px;
padding: 0px 10px;
}
dl.glossary > dt {
font-weight:bold;
font-size:1.1em;
+60 -1
View File
@@ -4,10 +4,69 @@ function initSQLPopups() {
$('a.sql_link').click(function() {
$(this).nextAll('div.popup_sql:first').toggle();
return false;
})
});
}
function initFloatyThings() {
// we use a "fixed" positioning for the sidebar regardless
// of whether or not we are moving with the page or not because
// we want it to have an independently-moving scrollbar at all
// times.
// this unfortunately means we either have to keep it steady across
// page scrolls or deal with the fact that the text is flowing
// under it in some resize/side-scroll scenarios.
var automatedBreakpoint = $("#docs-container").position().top +
$("#docs-top-navigation-container").height();
var docsBodyOffset = $("#docs-body").offset().top;
// this turns on the whole thing, without this
// we are in graceful degradation assuming no JS
$("#fixed-sidebar.withsidebar").addClass("preautomated");
function setScroll() {
var scrolltop = $(window).scrollTop();
var fix = scrolltop >= automatedBreakpoint;
// when page is scrolled down past the top headers,
// sidebar stays fixed vertically
if (fix) {
$("#fixed-sidebar.withsidebar").css("top", 5);
}
else if (scrolltop < 0) {
// special trickery to deal with safari vs. chrome
// acting differently in this case, while avoiding using jquery's
// weird / slow? offset() setter
if ($("#fixed-sidebar.withsidebar").offset().top != docsBodyOffset) {
$("#fixed-sidebar.withsidebar").css(
"top", docsBodyOffset - scrolltop);
}
}
else {
$("#fixed-sidebar.withsidebar").css(
"top", docsBodyOffset - scrolltop);
}
// adjusting left scroll is also an option,
// but doesn't seem to be worth it, safari is the only browser
// that shows much of a change, and overall the adjustment here
// is jerky and error-prone esp. on lesser browsers like safari ipad.
// looking at our "mentor" documentation, they don't do this;
// they just have the whole layout such that you don't really notice
// the horizontal squeezing as much (nav is on the right, they don't
// have a border around the text making it obvious).
}
$(window).scroll(setScroll);
setScroll();
}
$(document).ready(function() {
initSQLPopups();
if (!$.browser.mobile) {
initFloatyThings();
}
});
+1 -1
View File
@@ -1,4 +1,4 @@
<%inherit file="${context['layout']}"/>
<%inherit file="layout.mako"/>
<%block name="show_title" filter="util.striptags">
${_('Index')}
+119 -86
View File
@@ -2,20 +2,26 @@
<%!
local_script_files = []
default_css_files = [
'_static/pygments.css',
'_static/docs.css',
]
%>
<%doc>
Structural elements are all prefixed with "docs-"
to prevent conflicts when the structure is integrated into the
to prevent conflicts when the structure is integrated into the
main site.
docs-container ->
docs-header ->
docs-search
docs-version-header
docs-top-navigation
docs-top-page-control
docs-navigation-banner
docs-top-navigation-container ->
docs-header ->
docs-version-header
docs-top-navigation
docs-top-page-control
docs-navigation-banner
docs-body-container ->
docs-sidebar
docs-body
@@ -25,22 +31,33 @@
<%inherit file="${context['base']}"/>
<%
if builder == 'epub':
next.body()
return
%>
<%
withsidebar = bool(toc) and current_page_name != 'index'
%>
<%block name="head_title">
% if current_page_name != 'index':
${capture(self.show_title) | util.striptags} &mdash;
${capture(self.show_title) | util.striptags} &mdash;
% endif
${docstitle|h}
</%block>
<div id="docs-container">
<%block 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" />
${parent.headers()}
<!-- begin layout.mako headers -->
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
@@ -50,9 +67,14 @@ withsidebar = bool(toc) and current_page_name != 'index'
FILE_SUFFIX: '${file_suffix}'
};
</script>
<!-- begin iterate through sphinx environment script_files -->
% for scriptfile in script_files + self.attr.local_script_files:
<script type="text/javascript" src="${pathto(scriptfile, 1)}"></script>
% endfor
<!-- end iterate through sphinx environment script_files -->
<script type="text/javascript" src="${pathto('_static/detectmobile.js', 1)}"></script>
<script type="text/javascript" src="${pathto('_static/init.js', 1)}"></script>
% if hasdoc('about'):
<link rel="author" title="${_('About these documents')}" href="${pathto('about')}" />
@@ -72,56 +94,105 @@ withsidebar = bool(toc) and current_page_name != 'index'
% if prevtopic:
<link rel="prev" title="${prevtopic['title']|util.striptags}" href="${prevtopic['link']|h}" />
% endif
<!-- end layout.mako headers -->
</%block>
<div id="docs-top-navigation-container" class="body-background">
<div id="docs-header">
<h1>${docstitle|h}</h1>
<div id="docs-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 id="docs-version-header">
Release: <span class="version-num">${release}</span> | Release Date: ${release_date}
% if pdf_url:
| <a href="${pdf_url}">Download PDF</a>
% endif
</div>
<h1>${docstitle|h}</h1>
</div>
</div>
<div id="docs-top-navigation">
<div id="docs-top-page-control" class="docs-navigation-links">
<ul>
% if prevtopic:
<li>Prev:
<a href="${prevtopic['link']|h}" title="${_('previous chapter')}">${prevtopic['title']}</a>
</li>
% endif
% if nexttopic:
<li>Next:
<a href="${nexttopic['link']|h}" title="${_('next chapter')}">${nexttopic['title']}</a>
</li>
% endif
<div id="docs-body-container">
<li>
<a href="${pathto('contents')}">Table of Contents</a> |
<div id="fixed-sidebar" class="${'withsidebar' if withsidebar else ''}">
% if not withsidebar:
<div id="index-nav">
<form class="search" action="${pathto('search')}" method="get">
<input type="text" name="q" size="12" /> <input type="submit" value="${_('Search')}" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
<p>
<a href="${pathto('index')}">Contents</a> |
<a href="${pathto('genindex')}">Index</a>
% if sourcename:
| <a href="${pathto('_sources/' + sourcename, True)|h}">${_('view source')}
% if pdf_url:
| <a href="${pdf_url}">Download as PDF</a>
% endif
</li>
</p>
</div>
% endif
% if withsidebar:
<div id="docs-sidebar-popout">
<h3><a href="${pathto('index')}">${docstitle|h}</a></h3>
<p id="sidebar-paginate">
% if parents:
<a href="${parents[-1]['link']|h}" title="${parents[-1]['title']}">Up</a> |
% else:
<a href="${pathto('index')}" title="${docstitle|h}">Up</a> |
% endif
% if prevtopic:
<a href="${prevtopic['link']|h}" title="${prevtopic['title']}">Prev</a> |
% endif
% if nexttopic:
<a href="${nexttopic['link']|h}" title="${nexttopic['title']}">Next</a>
% endif
</p>
<p id="sidebar-topnav">
<a href="${pathto('index')}">Contents</a> |
<a href="${pathto('genindex')}">Index</a>
% if pdf_url:
| <a href="${pdf_url}">PDF</a>
% endif
</p>
<div id="sidebar-search">
<form class="search" action="${pathto('search')}" method="get">
<input type="text" name="q" size="12" /> <input type="submit" value="${_('Search')}" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
<div id="docs-sidebar">
<h3><a href="#">\
<%block name="show_title">
${title}
</%block>
</a></h3>
${toc}
% if rtd:
<h4>Project Versions</h4>
<ul class="version-listing">
</ul>
% endif
</div>
% endif
</div>
<div id="docs-navigation-banner">
<%doc>
<div id="docs-top-navigation">
<a href="${pathto('index')}">${docstitle|h}</a>
% if parents:
% for parent in parents:
@@ -129,7 +200,7 @@ withsidebar = bool(toc) and current_page_name != 'index'
% endfor
% endif
% if current_page_name != 'index':
» ${self.show_title()}
» ${self.show_title()}
% endif
<h2>
@@ -137,47 +208,9 @@ withsidebar = bool(toc) and current_page_name != 'index'
${title}
</%block>
</h2>
</div>
</div>
<div id="docs-body-container">
% if withsidebar:
<div id="docs-sidebar">
<h3><a href="${pathto('index')}">Table of Contents</a></h3>
${toc}
% if prevtopic:
<h4>Previous Topic</h4>
<p>
<a href="${prevtopic['link']|h}" title="${_('previous chapter')}">${prevtopic['title']}</a>
</p>
% endif
% if nexttopic:
<h4>Next Topic</h4>
<p>
<a href="${nexttopic['link']|h}" title="${_('next chapter')}">${nexttopic['title']}</a>
</p>
% endif
% if rtd:
<h4>Project Versions</h4>
<ul class="version-listing">
</ul>
% endif
<h4>Quick Search</h4>
<p>
<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>
</p>
</div>
% endif
</%doc>
<div id="docs-body" class="${'withsidebar' if withsidebar else ''}" >
${next.body()}
+1 -1
View File
@@ -1,2 +1,2 @@
<%inherit file="${context['layout']}"/>
<%inherit file="layout.mako"/>
${body| util.strip_toplevel_anchors}
-164
View File
@@ -1,164 +0,0 @@
<%inherit file="/layout.mako"/>
<%
newscript = []
# strip out script files that RTD wants to provide
for script in script_files:
for token in ("jquery.js", "underscore.js", "doctools.js"):
if token in script:
break
else:
newscript.append(script)
script_files[:] = newscript
%>
<%block name="headers">
<!-- RTD <head> -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
<script type="text/javascript" src="${MEDIA_URL}javascript/underscore.js"></script>
<script type="text/javascript" src="${MEDIA_URL}javascript/doctools.js"></script>
<script type="text/javascript" src="${MEDIA_URL}javascript/searchtools.js"></script>
<script type="text/javascript">
var doc_version = "${current_version}";
var doc_slug = "${slug}";
var static_root = "${pathto('_static', 1)}"
</script>
<!-- end RTD <head> -->
${parent.headers()}
</%block>
${next.body()}
<%block name="footer">
${parent.footer()}
<!-- End original user content -->
## Keep this here, so that the RTD logo doesn't stomp on the bottom of the theme.
<br>
<br>
<br>
<style type="text/css">
.badge {
position: fixed;
display: block;
bottom: 5px;
height: 40px;
text-indent: -9999em;
border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 1px 0 rgba(255, 255, 255, 0.2) inset;
-moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 1px 0 rgba(255, 255, 255, 0.2) inset;
-webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 1px 0 rgba(255, 255, 255, 0.2) inset;
}
#version_menu {
position: fixed;
display: none;
bottom: 11px;
right: 166px;
list-style-type: none;
margin: 0;
}
.footer_popout:hover #version_menu {
display: block;
}
#version_menu li {
display: block;
float: right;
}
#version_menu li a {
display: block;
padding: 6px 10px 4px 10px;
margin: 7px 7px 0 0;
font-weight: bold;
font-size: 14px;
height: 20px;
line-height: 17px;
text-decoration: none;
color: #fff;
background: #8ca1af url(http://media.readthedocs.org/images/gradient-light.png) bottom left repeat-x;
border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
box-shadow: 0 1px 1px #465158;
-moz-box-shadow: 0 1px 1px #465158;
-webkit-box-shadow: 0 1px 1px #465158;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.5);
}
#version_menu li a:hover {
text-decoration: none;
background-color: #697983;
box-shadow: 0 1px 0px #465158;
-moz-box-shadow: 0 1px 0px #465158;
-webkit-box-shadow: 0 1px 0px #465158;
}
.badge.rtd {
background: #257597 url(http://media.readthedocs.org/images/badge-rtd.png) top left no-repeat;
border: 1px solid #282E32;
width: 160px;
right: 5px;
}
.badge.revsys { background: #465158 url(http://media.readthedocs.org/images/badge-revsys.png) top left no-repeat;
border: 1px solid #1C5871;
width: 290px;
right: 173px;
}
.badge.revsys-inline-sponsored {
position: inherit;
margin-left: auto;
margin-right: 175px;
margin-bottom: 5px;
background: #465158 url(http://media.readthedocs.org/images/badge-revsys.png) top left no-repeat;
border: 1px solid #1C5871;
width: 290px;
right: 173px;
}
.badge.revsys-inline {
position: inherit;
margin-left: auto;
margin-right: 175px;
margin-bottom: 5px;
background: #465158 url(http://media.readthedocs.org/images/badge-revsys-sm.png) top left no-repeat;
border: 1px solid #1C5871;
width: 205px;
right: 173px;
}
</style>
<div class="rtd_doc_footer">
<div class="footer_popout">
<a href="http://readthedocs.org/projects/${slug}/?fromdocs=${slug}" class="badge rtd">Brought to you by Read the Docs</a>
<ul id="version_menu">
## rtd fills this in client side
</ul>
</div>
</div>
<!-- RTD Analytics Code -->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-17997319-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
% if analytics_code:
<!-- User Analytics Code -->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '${analytics_code}']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
% endif
</%block>
+1 -10
View File
@@ -1,4 +1,4 @@
<%inherit file="${context['layout']}"/>
<%inherit file="layout.mako"/>
<%!
local_script_files = ['_static/searchtools.js']
@@ -14,15 +14,6 @@
</script>
</%block>
<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>
<%block name="footer">
+9
View File
@@ -9,6 +9,15 @@
<%block name="head_title">
</%block>
</title>
<%block name="css">
<!-- begin iterate through SQLA + sphinx environment css_files -->
% for cssfile in self.attr.default_css_files + css_files:
<link rel="stylesheet" href="${pathto(cssfile, 1)}" type="text/css" />
% endfor
<!-- end iterate through SQLA + sphinx environment css_files -->
</%block>
<%block name="headers"/>
</head>
<body>
+6 -5
View File
@@ -40,15 +40,16 @@ exactly one SQL statement against two tables will be emitted - the
displayed result however will utilize dozens of lazyloads that all
pull from cache.
The demo scripts themselves, in order of complexity, are run as follows::
The demo scripts themselves, in order of complexity, are run as Python
modules so that relative imports work::
python examples/dogpile_caching/helloworld.py
python -m examples.dogpile_caching.helloworld
python examples/dogpile_caching/relationship_caching.py
python -m examples.dogpile_caching.relationship_caching
python examples/dogpile_caching/advanced.py
python -m examples.dogpile_caching.advanced
python examples/dogpile_caching/local_session_caching.py
python -m examples.dogpile_caching.local_session_caching
Listing of files:
+5 -7
View File
@@ -1,15 +1,13 @@
"""advanced.py
Illustrate usage of Query combined with the FromCache option,
including front-end loading, cache invalidation, namespace techniques
and collection caching.
including front-end loading, cache invalidation and collection caching.
"""
from environment import Session
from model import Person, Address, cache_address_bits
from caching_query import FromCache, RelationshipCache
from sqlalchemy.orm import joinedload
from .environment import Session
from .model import Person, cache_address_bits
from .caching_query import FromCache, RelationshipCache
def load_name_range(start, end, invalidate=False):
"""Load Person objects on a range of names.
@@ -23,7 +21,7 @@ def load_name_range(start, end, invalidate=False):
The `Person.addresses` collections are also cached. Its basically
another level of tuning here, as that particular cache option
can be transparently replaced with joinedload(Person.addresses).
The effect is that each Person and his/her Address collection
The effect is that each Person and their Address collection
is cached either together or separately, affecting the kind of
SQL that emits for unloaded Person objects as well as the distribution
of data within the cache.
+7 -2
View File
@@ -10,7 +10,12 @@ from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from dogpile.cache.region import make_region
import os
import md5
from hashlib import md5
import sys
py2k = sys.version_info < (3, 0)
if py2k:
input = raw_input
# dogpile cache regions. A home base for cache configurations.
regions = {}
@@ -47,7 +52,7 @@ def md5_key_mangler(key):
distill them into an md5 hash.
"""
return md5.md5(key).hexdigest()
return md5(key.encode('ascii')).hexdigest()
# configure the "default" cache region.
regions['default'] = make_region(
+12 -12
View File
@@ -8,31 +8,31 @@ from environment import Session
from model import Person
from caching_query import FromCache
# load Person objects. cache the result under the namespace "all_people".
print "loading people...."
# load Person objects. cache the result in the "default" cache region
print("loading people....")
people = Session.query(Person).options(FromCache("default")).all()
# remove the Session. next query starts from scratch.
Session.remove()
# load again, using the same FromCache option. now they're cached
# under "all_people", no SQL is emitted.
print "loading people....again!"
# load again, using the same FromCache option. now they're cached,
# so no SQL is emitted.
print("loading people....again!")
people = Session.query(Person).options(FromCache("default")).all()
# want to load on some different kind of query ? change the namespace
# you send to FromCache
print "loading people two through twelve"
# Specifying a different query produces a different cache key, so
# these results are independently cached.
print("loading people two through twelve")
people_two_through_twelve = Session.query(Person).\
options(FromCache("default")).\
filter(Person.name.between("person 02", "person 12")).\
all()
# the data is cached under the "namespace" you send to FromCache, *plus*
# the data is cached under string structure of the SQL statement, *plus*
# the bind parameters of the query. So this query, having
# different literal parameters under "Person.name.between()" than the
# previous one, issues new SQL...
print "loading people five through fifteen"
print("loading people five through fifteen")
people_five_through_fifteen = Session.query(Person).\
options(FromCache("default")).\
filter(Person.name.between("person 05", "person 15")).\
@@ -40,7 +40,7 @@ people_five_through_fifteen = Session.query(Person).\
# ... but using the same params as are already cached, no SQL
print "loading people two through twelve...again!"
print("loading people two through twelve...again!")
people_two_through_twelve = Session.query(Person).\
options(FromCache("default")).\
filter(Person.name.between("person 02", "person 12")).\
@@ -51,7 +51,7 @@ people_two_through_twelve = Session.query(Person).\
# each Query, which includes at the very least the same FromCache,
# same list of objects to be loaded, and the same parameters in the
# same order, then call invalidate().
print "invalidating everything"
print("invalidating everything")
Session.query(Person).options(FromCache("default")).invalidate()
Session.query(Person).\
options(FromCache("default")).\
+6 -4
View File
@@ -16,10 +16,12 @@ The configurations include:
table per association.
* ``discriminator_on_association.py`` - shared collection table and shared
association table, including a discriminator column.
* ``generic_fk.py`` - imitates the approach taken by popular frameworks such
as Django and Ruby on Rails to create a so-called "generic foreign key".
The ``discriminator_on_association.py`` script in particular is a modernized
version of the "polymorphic associations" example present in older versions of
SQLAlchemy, originally from the blog post at
http://techspot.zzzeek.org/2007/05/29/polymorphic-associations-with-sqlalchemy/.
The ``discriminator_on_association.py`` and ``generic_fk.py`` scripts
are modernized versions of recipes presented in the 2007 blog post
`Polymorphic Associations with SQLAlchemy <http://techspot.zzzeek.org/2007/05/29/polymorphic-associations-with-sqlalchemy/>`_.
.
"""
@@ -3,26 +3,29 @@
The HasAddresses mixin will provide a relationship
to the fixed Address table based on a fixed association table.
The association table will also contain a "discriminator"
The association table contains a "discriminator"
which determines what type of parent object associates to the
Address row.
Address row. SQLAlchemy's single-table-inheritance feature is used
to target different association types.
This is a "polymorphic association". Even though a "discriminator"
that refers to a particular table is present, the extra association
table is used so that traditional foreign key constraints may be used.
This configuration has the advantage that a fixed set of tables
are used, with no extra-table-per-parent needed. The individual
Address record can also locate its parent with no need to scan
amongst many tables.
This configuration attempts to simulate a so-called "generic foreign key"
as closely as possible without actually foregoing the use of real
foreign keys. Unlike table-per-related and table-per-association,
it uses a fixed number of tables to serve any number of potential parent
objects, but is also slightly more complex.
"""
from sqlalchemy.ext.declarative import declarative_base, declared_attr
from sqlalchemy.ext.declarative import as_declarative, declared_attr
from sqlalchemy import create_engine, Integer, Column, \
String, ForeignKey, Table
String, ForeignKey
from sqlalchemy.orm import Session, relationship, backref
from sqlalchemy.ext.associationproxy import association_proxy
@as_declarative()
class Base(object):
"""Base class which provides automated table name
and surrogate primary key column.
@@ -32,7 +35,6 @@ class Base(object):
def __tablename__(cls):
return cls.__name__.lower()
id = Column(Integer, primary_key=True)
Base = declarative_base(cls=Base)
class AddressAssociation(Base):
"""Associates a collection of Address objects
@@ -41,22 +43,10 @@ class AddressAssociation(Base):
"""
__tablename__ = "address_association"
@classmethod
def creator(cls, discriminator):
"""Provide a 'creator' function to use with
the association proxy."""
return lambda addresses:AddressAssociation(
addresses=addresses,
discriminator=discriminator)
discriminator = Column(String)
"""Refers to the type of parent."""
@property
def parent(self):
"""Return the parent object."""
return getattr(self, "%s_parent" % self.discriminator)
__mapper_args__ = {"polymorphic_on": discriminator}
class Address(Base):
"""The Address class.
@@ -65,15 +55,11 @@ class Address(Base):
single table.
"""
association_id = Column(Integer,
ForeignKey("address_association.id")
)
association_id = Column(Integer, ForeignKey("address_association.id"))
street = Column(String)
city = Column(String)
zip = Column(String)
association = relationship(
"AddressAssociation",
backref="addresses")
association = relationship("AddressAssociation", backref="addresses")
parent = association_proxy("association", "parent")
@@ -89,19 +75,29 @@ class HasAddresses(object):
"""
@declared_attr
def address_association_id(cls):
return Column(Integer,
ForeignKey("address_association.id"))
return Column(Integer, ForeignKey("address_association.id"))
@declared_attr
def address_association(cls):
discriminator = cls.__name__.lower()
cls.addresses= association_proxy(
name = cls.__name__
discriminator = name.lower()
assoc_cls = type(
"%sAddressAssociation" % name,
(AddressAssociation, ),
dict(
__mapper_args__={
"polymorphic_identity": discriminator
}
)
)
cls.addresses = association_proxy(
"address_association", "addresses",
creator=AddressAssociation.creator(discriminator)
creator=lambda addresses: assoc_cls(addresses=addresses)
)
return relationship("AddressAssociation",
backref=backref("%s_parent" % discriminator,
uselist=False))
return relationship(assoc_cls,
backref=backref("parent", uselist=False))
class Customer(HasAddresses, Base):
@@ -145,4 +141,4 @@ session.commit()
for customer in session.query(Customer):
for address in customer.addresses:
print address
print address.parent
print address.parent
+141
View File
@@ -0,0 +1,141 @@
"""generic_fk.py
This example will emulate key aspects of the system used by popular
frameworks such as Django, ROR, etc.
It approaches the issue by bypassing standard referential integrity
practices, and producing a so-called "generic foreign key", which means
a database column that is not constrained to refer to any particular table.
In-application logic is used to determine which table is referenced.
This approach is not in line with SQLAlchemy's usual style, as foregoing
foreign key integrity means that the tables can easily contain invalid
references and also have no ability to use in-database cascade functionality.
However, due to the popularity of these systems, as well as that it uses
the fewest number of tables (which doesn't really offer any "advantage",
though seems to be comforting to many) this recipe remains in
high demand, so in the interests of having an easy StackOverflow answer
queued up, here it is. The author recommends "table_per_related"
or "table_per_association" instead of this approach.
.. versionadded:: 0.8.3
"""
from sqlalchemy.ext.declarative import as_declarative, declared_attr
from sqlalchemy import create_engine, Integer, Column, \
String, and_
from sqlalchemy.orm import Session, relationship, foreign, remote, backref
from sqlalchemy import event
@as_declarative()
class Base(object):
"""Base class which provides automated table name
and surrogate primary key column.
"""
@declared_attr
def __tablename__(cls):
return cls.__name__.lower()
id = Column(Integer, primary_key=True)
class Address(Base):
"""The Address class.
This represents all address records in a
single table.
"""
street = Column(String)
city = Column(String)
zip = Column(String)
discriminator = Column(String)
"""Refers to the type of parent."""
parent_id = Column(Integer)
"""Refers to the primary key of the parent.
This could refer to any table.
"""
@property
def parent(self):
"""Provides in-Python access to the "parent" by choosing
the appropriate relationship.
"""
return getattr(self, "parent_%s" % self.discriminator)
def __repr__(self):
return "%s(street=%r, city=%r, zip=%r)" % \
(self.__class__.__name__, self.street,
self.city, self.zip)
class HasAddresses(object):
"""HasAddresses mixin, creates a relationship to
the address_association table for each parent.
"""
@event.listens_for(HasAddresses, "mapper_configured", propagate=True)
def setup_listener(mapper, class_):
name = class_.__name__
discriminator = name.lower()
class_.addresses = relationship(Address,
primaryjoin=and_(
class_.id == foreign(remote(Address.parent_id)),
Address.discriminator == discriminator
),
backref=backref(
"parent_%s" % discriminator,
primaryjoin=remote(class_.id) == foreign(Address.parent_id)
)
)
@event.listens_for(class_.addresses, "append")
def append_address(target, value, initiator):
value.discriminator = discriminator
class Customer(HasAddresses, Base):
name = Column(String)
class Supplier(HasAddresses, Base):
company_name = Column(String)
engine = create_engine('sqlite://', echo=True)
Base.metadata.create_all(engine)
session = Session(engine)
session.add_all([
Customer(
name='customer 1',
addresses=[
Address(
street='123 anywhere street',
city="New York",
zip="10110"),
Address(
street='40 main street',
city="San Francisco",
zip="95732")
]
),
Supplier(
company_name="Ace Hammers",
addresses=[
Address(
street='2569 west elm',
city="Detroit",
zip="56785")
]
),
])
session.commit()
for customer in session.query(Customer):
for address in customer.addresses:
print(address)
print(address.parent)
@@ -12,11 +12,12 @@ has no dependency on the system.
"""
from sqlalchemy.ext.declarative import declarative_base, declared_attr
from sqlalchemy.ext.declarative import as_declarative, declared_attr
from sqlalchemy import create_engine, Integer, Column, \
String, ForeignKey, Table
from sqlalchemy.orm import Session, relationship
@as_declarative()
class Base(object):
"""Base class which provides automated table name
and surrogate primary key column.
@@ -26,7 +27,6 @@ class Base(object):
def __tablename__(cls):
return cls.__name__.lower()
id = Column(Integer, primary_key=True)
Base = declarative_base(cls=Base)
class Address(Base):
"""The Address class.
@@ -9,11 +9,19 @@ size for one type of parent will have no impact on other types
of parent. Navigation between parent and "Address" is simple,
direct, and bidirectional.
This recipe is the most efficient (speed wise and storage wise)
and simple of all of them.
The creation of many related tables may seem at first like an issue
but there really isn't any - the management and targeting of these tables
is completely automated.
"""
from sqlalchemy.ext.declarative import declarative_base, declared_attr
from sqlalchemy.ext.declarative import as_declarative, declared_attr
from sqlalchemy import create_engine, Integer, Column, String, ForeignKey
from sqlalchemy.orm import Session, relationship
@as_declarative()
class Base(object):
"""Base class which provides automated table name
and surrogate primary key column.
@@ -23,7 +31,6 @@ class Base(object):
def __tablename__(cls):
return cls.__name__.lower()
id = Column(Integer, primary_key=True)
Base = declarative_base(cls=Base)
class Address(object):
"""Define columns that will be present in each
@@ -54,11 +61,11 @@ class HasAddresses(object):
"%sAddress" % cls.__name__,
(Address, Base,),
dict(
__tablename__ = "%s_address" %
__tablename__="%s_address" %
cls.__tablename__,
parent_id = Column(Integer,
ForeignKey("%s.id" % cls.__tablename__)),
parent = relationship(cls)
parent_id=Column(Integer,
ForeignKey("%s.id" % cls.__tablename__)),
parent=relationship(cls)
)
)
return relationship(cls.Address)
@@ -104,4 +111,4 @@ session.commit()
for customer in session.query(Customer):
for address in customer.addresses:
print address
print address.parent
print address.parent
+1
View File
@@ -133,3 +133,4 @@ print session.query(Company).\
session.commit()
+2 -2
View File
@@ -88,13 +88,13 @@ session.commit()
print(session.query(Employee).all())
# 1. Find an employee and all his/her supervisors, no matter how deep the tree.
# 1. Find an employee and all their supervisors, no matter how deep the tree.
ealias = aliased(Employee)
print(session.query(Employee).\
filter(ealias.left.between(Employee.left, Employee.right)).\
filter(ealias.emp == 'Eddie').all())
#2. Find the employee and all his/her subordinates.
#2. Find the employee and all their subordinates.
# (This query has a nice symmetry with the first query.)
print(session.query(Employee).\
filter(Employee.left.between(ealias.left, ealias.right)).\
+2 -2
View File
@@ -109,7 +109,7 @@ def id_chooser(query, ident):
given a primary key, returns a list of shards
to search. here, we don't have any particular information from a
pk so we just return all shard ids. often, youd want to do some
pk so we just return all shard ids. often, you'd want to do some
kind of round-robin strategy here so that requests are evenly
distributed among DBs.
@@ -168,7 +168,7 @@ def _get_query_comparisons(query):
elif bind.callable:
# some ORM functions (lazy loading)
# place the bind's value as a
# callable for deferred evaulation.
# callable for deferred evaluation.
value = bind.callable()
else:
# just use .value
+9 -5
View File
@@ -44,9 +44,9 @@ def _history_mapper(local_mapper):
if super_mapper:
super_fks.append(('version', super_history_mapper.base_mapper.local_table.c.version))
cols.append(Column('version', Integer, primary_key=True))
cols.append(Column('version', Integer, primary_key=True, autoincrement=False))
else:
cols.append(Column('version', Integer, primary_key=True))
cols.append(Column('version', Integer, primary_key=True, autoincrement=False))
if super_fks:
cols.append(ForeignKeyConstraint(*zip(*super_fks)))
@@ -158,8 +158,12 @@ def create_version(obj, session, deleted = False):
for prop in obj_mapper.iterate_properties:
if isinstance(prop, RelationshipProperty) and \
attributes.get_history(obj, prop.key).has_changes():
obj_changed = True
break
for p in prop.local_columns:
if p.foreign_keys:
obj_changed = True
break
if obj_changed is True:
break
if not obj_changed and not deleted:
return
@@ -177,4 +181,4 @@ def versioned_session(session):
for obj in versioned_objects(session.dirty):
create_version(obj, session)
for obj in versioned_objects(session.deleted):
create_version(obj, session, deleted = True)
create_version(obj, session, deleted = True)
+62 -24
View File
@@ -2,10 +2,10 @@ from unittest import TestCase
from sqlalchemy.ext.declarative import declarative_base
from history_meta import Versioned, versioned_session
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import clear_mappers, sessionmaker, deferred, relationship
from sqlalchemy.orm import clear_mappers, Session, deferred, relationship
from _lib import ComparableEntity, eq_
engine = Session = None
engine = None
def setup():
@@ -14,27 +14,27 @@ def setup():
class TestVersioning(TestCase):
def setUp(self):
global Base, Session, Versioned
Base = declarative_base()
Session = sessionmaker(engine)
versioned_session(Session)
self.session = Session(engine)
self.Base = declarative_base()
versioned_session(self.session)
def tearDown(self):
self.session.close()
clear_mappers()
Base.metadata.drop_all(engine)
self.Base.metadata.drop_all(engine)
def create_tables(self):
Base.metadata.create_all(engine)
self.Base.metadata.create_all(engine)
def test_plain(self):
class SomeClass(Versioned, Base, ComparableEntity):
class SomeClass(Versioned, self.Base, ComparableEntity):
__tablename__ = 'sometable'
id = Column(Integer, primary_key=True)
name = Column(String(50))
self.create_tables()
sess = Session()
sess = self.session
sc = SomeClass(name='sc1')
sess.add(sc)
sess.commit()
@@ -91,14 +91,14 @@ class TestVersioning(TestCase):
)
def test_from_null(self):
class SomeClass(Versioned, Base, ComparableEntity):
class SomeClass(Versioned, self.Base, ComparableEntity):
__tablename__ = 'sometable'
id = Column(Integer, primary_key=True)
name = Column(String(50))
self.create_tables()
sess = Session()
sess = self.session
sc = SomeClass()
sess.add(sc)
sess.commit()
@@ -111,7 +111,7 @@ class TestVersioning(TestCase):
def test_deferred(self):
"""test versioning of unloaded, deferred columns."""
class SomeClass(Versioned, Base, ComparableEntity):
class SomeClass(Versioned, self.Base, ComparableEntity):
__tablename__ = 'sometable'
id = Column(Integer, primary_key=True)
@@ -119,7 +119,7 @@ class TestVersioning(TestCase):
data = deferred(Column(String(25)))
self.create_tables()
sess = Session()
sess = self.session
sc = SomeClass(name='sc1', data='somedata')
sess.add(sc)
sess.commit()
@@ -142,7 +142,7 @@ class TestVersioning(TestCase):
def test_joined_inheritance(self):
class BaseClass(Versioned, Base, ComparableEntity):
class BaseClass(Versioned, self.Base, ComparableEntity):
__tablename__ = 'basetable'
id = Column(Integer, primary_key=True)
@@ -169,7 +169,7 @@ class TestVersioning(TestCase):
__mapper_args__ = {'polymorphic_identity':'same'}
self.create_tables()
sess = Session()
sess = self.session
sep1 = SubClassSeparatePk(name='sep1', subdata1='sep1subdata')
base1 = BaseClass(name='base1')
@@ -219,7 +219,7 @@ class TestVersioning(TestCase):
)
def test_single_inheritance(self):
class BaseClass(Versioned, Base, ComparableEntity):
class BaseClass(Versioned, self.Base, ComparableEntity):
__tablename__ = 'basetable'
id = Column(Integer, primary_key=True)
@@ -233,7 +233,7 @@ class TestVersioning(TestCase):
__mapper_args__ = {'polymorphic_identity':'sub'}
self.create_tables()
sess = Session()
sess = self.session
b1 = BaseClass(name='b1')
sc = SubClass(name='s1', subname='sc1')
@@ -270,7 +270,7 @@ class TestVersioning(TestCase):
sess.flush()
def test_unique(self):
class SomeClass(Versioned, Base, ComparableEntity):
class SomeClass(Versioned, self.Base, ComparableEntity):
__tablename__ = 'sometable'
id = Column(Integer, primary_key=True)
@@ -278,7 +278,7 @@ class TestVersioning(TestCase):
data = Column(String(50))
self.create_tables()
sess = Session()
sess = self.session
sc = SomeClass(name='sc1', data='sc1')
sess.add(sc)
sess.commit()
@@ -295,23 +295,23 @@ class TestVersioning(TestCase):
def test_relationship(self):
class SomeRelated(Base, ComparableEntity):
class SomeRelated(self.Base, ComparableEntity):
__tablename__ = 'somerelated'
id = Column(Integer, primary_key=True)
class SomeClass(Versioned, Base, ComparableEntity):
class SomeClass(Versioned, self.Base, ComparableEntity):
__tablename__ = 'sometable'
id = Column(Integer, primary_key=True)
name = Column(String(50))
related_id = Column(Integer, ForeignKey('somerelated.id'))
related = relationship("SomeRelated")
related = relationship("SomeRelated", backref='classes')
SomeClassHistory = SomeClass.__history_mapper__.class_
self.create_tables()
sess = Session()
sess = self.session
sc = SomeClass(name='sc1')
sess.add(sc)
sess.commit()
@@ -341,3 +341,41 @@ class TestVersioning(TestCase):
assert sc.version == 3
def test_backref_relationship(self):
class SomeRelated(self.Base, ComparableEntity):
__tablename__ = 'somerelated'
id = Column(Integer, primary_key=True)
name = Column(String(50))
related_id = Column(Integer, ForeignKey('sometable.id'))
related = relationship("SomeClass", backref='related')
class SomeClass(Versioned, self.Base, ComparableEntity):
__tablename__ = 'sometable'
id = Column(Integer, primary_key=True)
self.create_tables()
sess = self.session
sc = SomeClass()
sess.add(sc)
sess.commit()
assert sc.version == 1
sr = SomeRelated(name='sr', related=sc)
sess.add(sr)
sess.commit()
assert sc.version == 1
sr.name = 'sr2'
sess.commit()
assert sc.version == 1
sess.delete(sr)
sess.commit()
assert sc.version == 1
+2 -2
View File
@@ -1,5 +1,5 @@
# sqlalchemy/__init__.py
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -120,7 +120,7 @@ from .engine import create_engine, engine_from_config
__all__ = sorted(name for name, obj in locals().items()
if not (name.startswith('_') or _inspect.ismodule(obj)))
__version__ = '0.8.2'
__version__ = '0.8.7'
del _inspect, sys
+1 -1
View File
@@ -1,6 +1,6 @@
/*
processors.c
Copyright (C) 2010-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
Copyright (C) 2010-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
Copyright (C) 2010 Gaetan de Menten gdementen@gmail.com
This module is part of SQLAlchemy and is released under
+1 -1
View File
@@ -1,6 +1,6 @@
/*
resultproxy.c
Copyright (C) 2010-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
Copyright (C) 2010-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
Copyright (C) 2010 Gaetan de Menten gdementen@gmail.com
This module is part of SQLAlchemy and is released under
+1 -1
View File
@@ -1,6 +1,6 @@
/*
utils.c
Copyright (C) 2012-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
Copyright (C) 2012-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
This module is part of SQLAlchemy and is released under
the MIT License: http://www.opensource.org/licenses/mit-license.php
+1 -1
View File
@@ -1,5 +1,5 @@
# connectors/__init__.py
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
+1 -1
View File
@@ -1,5 +1,5 @@
# connectors/mxodbc.py
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php

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