Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5ba003858 | ||
|
|
d636f6c167 | ||
|
|
1ac5521029 | ||
|
|
e66297ea37 | ||
|
|
31c0f3a474 | ||
|
|
4ae3de7214 | ||
|
|
ce0aff9eb5 | ||
|
|
8496aee4a3 | ||
|
|
3c3dc2bce9 | ||
|
|
2c893b520a | ||
|
|
d5971b4430 | ||
|
|
69618c0d56 | ||
|
|
a2fc048775 | ||
|
|
e83d2f6fa6 | ||
|
|
8aafe64b30 | ||
|
|
8db30d7a2b | ||
|
|
6434b6b9ac | ||
|
|
1f93a505b8 | ||
|
|
0264ab6988 | ||
|
|
57dc058146 | ||
|
|
89d21f64c6 | ||
|
|
807680a9ec | ||
|
|
367341c95f | ||
|
|
40fe737f4c | ||
|
|
a7ed17e714 | ||
|
|
f024ce3b29 | ||
|
|
74d38623ff | ||
|
|
a81d3a23c0 | ||
|
|
8be54fe1de | ||
|
|
703cda62d7 | ||
|
|
53b72c35f4 | ||
|
|
a96267639a | ||
|
|
50cb349294 | ||
|
|
034912c1d8 | ||
|
|
7b5d45988a | ||
|
|
6f9a8034ca | ||
|
|
01eb52b516 | ||
|
|
7475cfd443 | ||
|
|
8f29b1fa49 | ||
|
|
24c8a242dd | ||
|
|
b832bf7e67 | ||
|
|
83b2e9e8d2 | ||
|
|
21cb06d1c0 | ||
|
|
5cf8dbeb5e | ||
|
|
96b24ec545 | ||
|
|
480d395c44 | ||
|
|
47e90e266e | ||
|
|
c0c4cf78a9 | ||
|
|
cf1ba8e101 | ||
|
|
64fbbde940 | ||
|
|
39b2bd0680 | ||
|
|
7d15b4fb38 | ||
|
|
41a334b3d8 | ||
|
|
3b936983de | ||
|
|
ac68e85e54 | ||
|
|
93e11a9d7d | ||
|
|
43d99ac61b | ||
|
|
517e22e476 | ||
|
|
6b23eb0f54 | ||
|
|
8093eb546b | ||
|
|
76682cff7d | ||
|
|
9c59faf74e | ||
|
|
6577df9ef9 | ||
|
|
280758acc5 | ||
|
|
0fd2fce8f8 | ||
|
|
9f72da7658 | ||
|
|
df7fe021a9 | ||
|
|
6bb74b6ede | ||
|
|
3bb9de09aa | ||
|
|
7473b54574 | ||
|
|
91de02cc66 | ||
|
|
b4eeebbabe | ||
|
|
e789a75f9a | ||
|
|
eb94838c72 | ||
|
|
f6614aed66 | ||
|
|
d8aa3d91d7 | ||
|
|
0dddcf924e | ||
|
|
cb51fe3ef8 | ||
|
|
5f69d83d6c | ||
|
|
66d090be9c | ||
|
|
c5005f7e4b | ||
|
|
987759aec5 | ||
|
|
ce3505ea8b | ||
|
|
149584d322 | ||
|
|
14b39c8dd9 | ||
|
|
cd3c5dd45e | ||
|
|
14c74a8fb5 | ||
|
|
3ba1385520 | ||
|
|
3dca38608b | ||
|
|
a3ad92de4b | ||
|
|
1366ef9f1c | ||
|
|
39c0485a21 | ||
|
|
0c3fdb808a | ||
|
|
cd0d29cef4 | ||
|
|
bfdb51ac6a | ||
|
|
f9059f6cd8 | ||
|
|
0d81c6c7a3 | ||
|
|
4f29ebc728 | ||
|
|
dd6884eafa | ||
|
|
56364c636e | ||
|
|
17d7e98b5e | ||
|
|
506817a84e | ||
|
|
4ac0558e11 | ||
|
|
df288504d9 | ||
|
|
beb567ba6f | ||
|
|
28ec4fdfc8 | ||
|
|
dd6362aed7 | ||
|
|
0dc4beaa08 | ||
|
|
929ff92598 | ||
|
|
1a7b5d55c9 | ||
|
|
30febd7c19 | ||
|
|
a7e0d3e1e3 | ||
|
|
b87935ed44 | ||
|
|
50d60cfb99 | ||
|
|
0470c5c5a6 | ||
|
|
99717570ff | ||
|
|
b77f958b8b | ||
|
|
cd1baf6572 | ||
|
|
2ac2bbd350 | ||
|
|
5f144b2e43 | ||
|
|
1cf18e5cbb | ||
|
|
e7f906f5e6 | ||
|
|
95c370cc65 | ||
|
|
5283b0cb31 | ||
|
|
a1f8479215 | ||
|
|
66fe0880ba | ||
|
|
b05adc9507 | ||
|
|
da4256d3dc | ||
|
|
4a7acd40ce | ||
|
|
7680ba30eb | ||
|
|
a52b2da3d0 | ||
|
|
81a6e04f8a | ||
|
|
271d7cd9b3 | ||
|
|
267fe8bc5d | ||
|
|
91eec74a6c | ||
|
|
802b9adc41 | ||
|
|
2866577231 | ||
|
|
28fd486bd4 | ||
|
|
21e012121a | ||
|
|
df06bd1803 | ||
|
|
3509ea9685 | ||
|
|
070c745a86 | ||
|
|
aace3b0e96 | ||
|
|
26b899e9ac | ||
|
|
a78110a21c | ||
|
|
6e4e1a8369 | ||
|
|
cc82ea33a0 | ||
|
|
a1d2e4d491 | ||
|
|
a6905c8252 | ||
|
|
a48a0f6a44 | ||
|
|
db0bfa0676 | ||
|
|
1ed633c229 | ||
|
|
4a4a4beffb | ||
|
|
819ad89276 | ||
|
|
c24e825c37 | ||
|
|
ab1ab82f16 | ||
|
|
320bc4861c | ||
|
|
b5b84ae20d | ||
|
|
2900b6e4ec | ||
|
|
351d0b6f21 | ||
|
|
4211207abf | ||
|
|
ae3c57f492 | ||
|
|
2042493228 | ||
|
|
3c4a565f2d | ||
|
|
0fefc6e226 | ||
|
|
dcb7e7759a | ||
|
|
37ebd3f2f3 | ||
|
|
e294767148 | ||
|
|
329aefb11e | ||
|
|
296f648e39 | ||
|
|
9c41c2cdc6 | ||
|
|
730f585dc6 | ||
|
|
69154698d2 | ||
|
|
a6eaf1cceb | ||
|
|
6fb499599a | ||
|
|
746a4e8f61 | ||
|
|
b576359dba | ||
|
|
157dc421f9 | ||
|
|
6817592180 | ||
|
|
2d0573970c | ||
|
|
7e78fb05c7 | ||
|
|
b113202eec | ||
|
|
71a9947410 | ||
|
|
507a33319f | ||
|
|
661b1158b4 | ||
|
|
187db36b74 | ||
|
|
2f18f9b2f5 | ||
|
|
2b3073c76b | ||
|
|
37a534f764 | ||
|
|
e8458ab96b | ||
|
|
f2f54e04a5 | ||
|
|
f7e3a3a36c | ||
|
|
c23b111f30 | ||
|
|
c002b87f22 | ||
|
|
8ebcbc0be9 | ||
|
|
ad85ab12d6 | ||
|
|
9d848680f4 | ||
|
|
bad8be3bde | ||
|
|
038f93a49b | ||
|
|
7d6f6972ab | ||
|
|
3a3aeb431e | ||
|
|
34478fbb0b | ||
|
|
33a2ebc514 | ||
|
|
0f9e364b96 | ||
|
|
e816958f92 | ||
|
|
f8fe32d5b4 | ||
|
|
a9fb549d4a | ||
|
|
db5ea00887 | ||
|
|
bf4c99664a | ||
|
|
98251ebe11 | ||
|
|
45db19e9ea | ||
|
|
8ddb4e2959 | ||
|
|
92490e0c5d | ||
|
|
97e8ac88ff | ||
|
|
93cb0436e6 | ||
|
|
db71e192bd | ||
|
|
de35f51b72 | ||
|
|
1f5236cae1 | ||
|
|
2576b5cdfb | ||
|
|
ac73cb304a | ||
|
|
264b650d62 | ||
|
|
06985a7346 | ||
|
|
eb58107417 | ||
|
|
64e32bda52 | ||
|
|
abb4e5314a | ||
|
|
f1905b619d | ||
|
|
785e44c48b | ||
|
|
622772b469 | ||
|
|
d0c2b9593a | ||
|
|
45ad8af3f9 | ||
|
|
bb6df3f45f | ||
|
|
4187fb2811 | ||
|
|
f498fc3d48 | ||
|
|
5ae388b077 | ||
|
|
cf1ac72bca | ||
|
|
621c22ca31 | ||
|
|
5389bc5d8d | ||
|
|
b26679e06a | ||
|
|
28a3491357 | ||
|
|
0cea9fd61c | ||
|
|
70fdd3efa7 | ||
|
|
d8c17e2a6f | ||
|
|
e488bb47e4 | ||
|
|
79f55eb20b | ||
|
|
c1a69ab841 | ||
|
|
92d477e453 | ||
|
|
b4165f9e0d | ||
|
|
396186ef39 | ||
|
|
51c92e1132 | ||
|
|
a0b8ce4ecf | ||
|
|
1e75bb2fab | ||
|
|
bace4c949e | ||
|
|
bb12d79f7b | ||
|
|
4ef40dd85b | ||
|
|
38e71f52ec | ||
|
|
3e947c2c06 | ||
|
|
6b30416686 | ||
|
|
5d1bd984e6 | ||
|
|
de60e3feb0 | ||
|
|
2008344aab | ||
|
|
573b542747 | ||
|
|
bedfeee764 | ||
|
|
d64ab47d76 | ||
|
|
004710b1e0 | ||
|
|
be594493e6 | ||
|
|
cd6cfe36d2 | ||
|
|
f0ac7e20d8 | ||
|
|
9fa0088d43 | ||
|
|
58617ff5ff | ||
|
|
f8061236b8 | ||
|
|
85ed24f79e | ||
|
|
f27992de27 | ||
|
|
a71e5c68ca | ||
|
|
702be8a7b8 | ||
|
|
09b93477d2 | ||
|
|
78c339a113 | ||
|
|
8665cf0f62 | ||
|
|
b9c14221e7 | ||
|
|
0e47ee3627 | ||
|
|
822b85d114 | ||
|
|
944f99a3b1 | ||
|
|
241379a446 | ||
|
|
06fd903af8 | ||
|
|
36de3538c1 | ||
|
|
22cd5c5cd5 | ||
|
|
01aeeb96f9 | ||
|
|
ef73845ba7 | ||
|
|
6028f82345 | ||
|
|
17274222f1 | ||
|
|
4b1ca23027 | ||
|
|
e71c84729d | ||
|
|
ea787dceca | ||
|
|
4f0bda7c45 | ||
|
|
5cbf8be95d | ||
|
|
22b2fe56eb | ||
|
|
4ead87dd3b | ||
|
|
4997485643 | ||
|
|
f085167865 | ||
|
|
7779c22e40 | ||
|
|
f3fc584055 | ||
|
|
ca078368ca | ||
|
|
3302187fea | ||
|
|
8aa5631107 | ||
|
|
312764983b | ||
|
|
d060052b52 | ||
|
|
834d18ca04 | ||
|
|
9fcb5348af | ||
|
|
cbb6f6d4f3 | ||
|
|
8290d96eea | ||
|
|
0655e69c82 | ||
|
|
37fc3e3bf8 | ||
|
|
a6069c75c2 | ||
|
|
90d992f2da | ||
|
|
436ba1601d | ||
|
|
7da90f41b2 | ||
|
|
a5d9e13675 | ||
|
|
2bef496632 | ||
|
|
645a8bfda2 | ||
|
|
5b65af0983 | ||
|
|
4018a03e7a | ||
|
|
2edc13a6b8 | ||
|
|
3c363e125c | ||
|
|
6c85d5bc0c | ||
|
|
5fb1a537e9 | ||
|
|
574dba8e61 | ||
|
|
1e332e03d3 | ||
|
|
9a95a39b49 | ||
|
|
124db0eb91 | ||
|
|
10c8867225 | ||
|
|
8373d7599e | ||
|
|
76290f87cc | ||
|
|
3f75c6aff6 | ||
|
|
4a9d88b1cf | ||
|
|
57da644320 | ||
|
|
55b664622d | ||
|
|
52293e3d4c | ||
|
|
64e019f392 | ||
|
|
e68377b95b | ||
|
|
fabd199246 | ||
|
|
a4e59730d5 | ||
|
|
9c5a13f049 | ||
|
|
2180cf44e5 | ||
|
|
761414a07b | ||
|
|
1bdd0bac3f | ||
|
|
b8046baf74 | ||
|
|
63f157b18a | ||
|
|
cc299b2d1c | ||
|
|
78cc4d9c34 | ||
|
|
d4b850fa6b | ||
|
|
1d67b2a233 | ||
|
|
ead7d9a680 | ||
|
|
cf60290e7b | ||
|
|
acc3c69464 | ||
|
|
627a8cecf6 | ||
|
|
169c88c7a5 | ||
|
|
7e8f1341fc | ||
|
|
fb8d23fb09 | ||
|
|
f57fc6836f | ||
|
|
69179e009c | ||
|
|
d64c945eae | ||
|
|
b4697e9e18 | ||
|
|
699fdd4330 | ||
|
|
fee954f253 | ||
|
|
59c01813ad | ||
|
|
23805f50ed | ||
|
|
e3e56ce70e | ||
|
|
decc72ee0e | ||
|
|
564c4eac71 | ||
|
|
aa5cc5a2cc | ||
|
|
6bdeab2e4c | ||
|
|
d42faf26b7 | ||
|
|
f52d45672b | ||
|
|
c05ef592bf | ||
|
|
2bb1a3f8b4 | ||
|
|
9fda3588ac | ||
|
|
6286f715db | ||
|
|
f7e6112d0f | ||
|
|
7a3a446d02 | ||
|
|
abd17f4daf | ||
|
|
aee826890c | ||
|
|
0b2c7765a0 | ||
|
|
01b7b9ae29 | ||
|
|
c0ee99da9e | ||
|
|
9dba65b381 | ||
|
|
6d0b2f34d2 | ||
|
|
0deebb51c8 | ||
|
|
06a2ce55a1 | ||
|
|
614e8dc617 | ||
|
|
3f6006953e | ||
|
|
1b231ff271 | ||
|
|
45f8ff88c9 | ||
|
|
cc1ea5da10 | ||
|
|
c2bbdf7770 | ||
|
|
1fc2611e20 | ||
|
|
f38e2aa460 | ||
|
|
33d3e11cbf | ||
|
|
c6f54ae434 | ||
|
|
77001c5a2d | ||
|
|
81e786f50f | ||
|
|
489312998f | ||
|
|
6ec33758f6 | ||
|
|
a5bacf91f0 | ||
|
|
ff96d23a90 | ||
|
|
792facf539 | ||
|
|
e14632ce13 | ||
|
|
20f567cba5 | ||
|
|
6717290d3d | ||
|
|
ca7dc2a2ab | ||
|
|
ff8e043669 | ||
|
|
361376f634 | ||
|
|
1f9c95333e | ||
|
|
7d16ab9103 | ||
|
|
07f7a5b0be | ||
|
|
d1b089c67b | ||
|
|
5a843390ef | ||
|
|
e0e44ddd67 |
+10
-4
@@ -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,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
@@ -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
@@ -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::
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -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 ../."
|
||||
|
||||
|
||||
Vendored
+38
-4
@@ -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)
|
||||
|
||||
Vendored
-296
@@ -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)
|
||||
Vendored
+17
-35
@@ -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)
|
||||
|
||||
Vendored
+17
-16
@@ -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:
|
||||
|
||||
Vendored
+18
-18
@@ -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:
|
||||
|
||||
Vendored
+40
-41
@@ -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
|
||||
|
||||
Vendored
+34
-36
@@ -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.
|
||||
|
||||
Vendored
+11
-11
@@ -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
|
||||
|
||||
Vendored
+42
-36
@@ -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.
|
||||
|
||||
|
||||
Vendored
+125
-41
@@ -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::
|
||||
|
||||
Vendored
+1336
-93
File diff suppressed because it is too large
Load Diff
Vendored
+4
-4
@@ -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
|
||||
|
||||
Vendored
+2
-2
@@ -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.
|
||||
|
||||
Vendored
+1
-1
@@ -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()```.
|
||||
|
||||
|
||||
Vendored
+5
-5
@@ -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::
|
||||
|
||||
|
||||
Vendored
+27
-3
@@ -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:
|
||||
|
||||
::
|
||||
|
||||
Vendored
+9
-6
@@ -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.
|
||||
|
||||
Vendored
+3
-1
@@ -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
|
||||
|
||||
Vendored
-6
@@ -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:
|
||||
|
||||
|
||||
Vendored
+385
@@ -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:
|
||||
Vendored
+287
@@ -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:
|
||||
|
||||
|
||||
Vendored
+345
@@ -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:
|
||||
Vendored
+35
@@ -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:
|
||||
|
||||
|
||||
Vendored
+55
-43
@@ -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
|
||||
|
||||
Vendored
+1
-1
@@ -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
|
||||
|
||||
Vendored
+4
-1
@@ -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
|
||||
|
||||
Vendored
-1
@@ -2,5 +2,4 @@ Core Exceptions
|
||||
===============
|
||||
|
||||
.. automodule:: sqlalchemy.exc
|
||||
:show-inheritance:
|
||||
:members:
|
||||
Vendored
+7
-243
@@ -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
|
||||
|
||||
|
||||
Vendored
+27
@@ -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:
|
||||
|
||||
|
||||
|
||||
Vendored
+1
-2
@@ -13,11 +13,10 @@ Language provides a schema-centric usage paradigm.
|
||||
|
||||
tutorial
|
||||
expression_api
|
||||
schema
|
||||
engines
|
||||
connections
|
||||
pooling
|
||||
schema
|
||||
types
|
||||
event
|
||||
events
|
||||
compiler
|
||||
|
||||
Vendored
+4
-6
@@ -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:
|
||||
|
||||
|
||||
|
||||
Vendored
+328
@@ -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:
|
||||
|
||||
|
||||
Vendored
+9
-7
@@ -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
|
||||
|
||||
Vendored
+166
@@ -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.
|
||||
|
||||
Vendored
+10
-1452
File diff suppressed because it is too large
Load Diff
Vendored
+79
@@ -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:
|
||||
Vendored
+139
@@ -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:
|
||||
|
||||
|
||||
|
||||
Vendored
+111
-19
@@ -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
|
||||
----------------------
|
||||
|
||||
|
||||
Vendored
+42
-58
@@ -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__
|
||||
|
||||
Vendored
+12
-12
@@ -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
|
||||
|
||||
Vendored
+26
-5
@@ -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
|
||||
|
||||
|
||||
|
||||
Vendored
+18
-18
@@ -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
|
||||
|
||||
Vendored
+33
-33
@@ -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
|
||||
--------------------
|
||||
|
||||
Vendored
+7
-7
@@ -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
|
||||
----------
|
||||
|
||||
Vendored
+75
-14
@@ -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
|
||||
--------------
|
||||
|
||||
Vendored
+1211
File diff suppressed because it is too large
Load Diff
Vendored
+764
-6
@@ -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`
|
||||
|
||||
Vendored
+9
-6
@@ -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>` |
|
||||
|
||||
Vendored
+29
-20
@@ -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
|
||||
----------------------------------
|
||||
|
||||
Vendored
+3
-3
@@ -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):
|
||||
|
||||
Vendored
+2
@@ -1,3 +1,5 @@
|
||||
:orphan:
|
||||
|
||||
.. _dep_interfaces_orm_toplevel:
|
||||
|
||||
Deprecated ORM Event Interfaces
|
||||
|
||||
Vendored
+1
-1
@@ -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
|
||||
|
||||
Vendored
-1
@@ -2,5 +2,4 @@ ORM Exceptions
|
||||
==============
|
||||
|
||||
.. automodule:: sqlalchemy.orm.exc
|
||||
:show-inheritance:
|
||||
:members:
|
||||
+2
@@ -10,6 +10,8 @@ API Reference
|
||||
|
||||
.. autofunction:: declarative_base
|
||||
|
||||
.. autofunction:: as_declarative
|
||||
|
||||
.. autoclass:: declared_attr
|
||||
|
||||
.. autofunction:: sqlalchemy.ext.declarative.api._declarative_constructor
|
||||
|
||||
Vendored
+1
-1
@@ -15,7 +15,7 @@ API Reference
|
||||
:members:
|
||||
|
||||
.. autoclass:: Comparator
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
.. autodata:: HYBRID_METHOD
|
||||
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ API Reference
|
||||
.. autodata:: instrumentation_finders
|
||||
|
||||
.. autoclass:: ExtendedInstrumentationRegistry
|
||||
:show-inheritance:
|
||||
|
||||
:members:
|
||||
|
||||
|
||||
|
||||
Vendored
+2
-3
@@ -12,15 +12,14 @@ API Reference
|
||||
:members: _parents, coerce
|
||||
|
||||
.. autoclass:: Mutable
|
||||
:show-inheritance:
|
||||
:members:
|
||||
|
||||
.. autoclass:: MutableComposite
|
||||
:show-inheritance:
|
||||
|
||||
:members:
|
||||
|
||||
.. autoclass:: MutableDict
|
||||
:show-inheritance:
|
||||
|
||||
:members:
|
||||
|
||||
|
||||
|
||||
Vendored
+25
-6
@@ -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
|
||||
------------------------
|
||||
|
||||
Vendored
+6
-14
@@ -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:
|
||||
|
||||
Vendored
+11
-5
@@ -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.
|
||||
|
||||
Vendored
+183
-8
@@ -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
|
||||
=================
|
||||
|
||||
|
||||
Vendored
-1
@@ -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
|
||||
-----------------------------
|
||||
|
||||
Vendored
+165
-134
@@ -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
|
||||
|
||||
Vendored
+603
-264
File diff suppressed because it is too large
Load Diff
Vendored
+59
-27
@@ -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
|
||||
|
||||
|
||||
Vendored
+2
@@ -1 +1,3 @@
|
||||
mako
|
||||
changelog>=0.3.2
|
||||
sphinx-paramlinks
|
||||
|
||||
Vendored
+7
@@ -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);
|
||||
Vendored
+188
-36
@@ -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;
|
||||
|
||||
Vendored
+60
-1
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
<%inherit file="${context['layout']}"/>
|
||||
<%inherit file="layout.mako"/>
|
||||
|
||||
<%block name="show_title" filter="util.striptags">
|
||||
${_('Index')}
|
||||
|
||||
Vendored
+119
-86
@@ -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} —
|
||||
${capture(self.show_title) | util.striptags} —
|
||||
% 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()}
|
||||
|
||||
Vendored
+1
-1
@@ -1,2 +1,2 @@
|
||||
<%inherit file="${context['layout']}"/>
|
||||
<%inherit file="layout.mako"/>
|
||||
${body| util.strip_toplevel_anchors}
|
||||
Vendored
-164
@@ -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>
|
||||
Vendored
+1
-10
@@ -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">
|
||||
|
||||
Vendored
+9
@@ -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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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")).\
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -133,3 +133,4 @@ print session.query(Company).\
|
||||
|
||||
session.commit()
|
||||
|
||||
|
||||
|
||||
@@ -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)).\
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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,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,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,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,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
Reference in New Issue
Block a user