diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-06-06 20:40:43 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-06-10 15:29:01 -0400 |
commit | b0cfa7379cf8513a821a3dbe3028c4965d9f85bd (patch) | |
tree | 19a79632b4f159092d955765ff9f7e842808bce7 /doc/build | |
parent | 3ab2364e78641c4f0e4b6456afc2cbed39b0d0e6 (diff) | |
download | sqlalchemy-b0cfa7379cf8513a821a3dbe3028c4965d9f85bd.tar.gz |
Turn on caching everywhere, add logging
A variety of caching issues found by running
all tests with statement caching turned on.
The cache system now has a more conservative approach where
any subclass of a SQL element will by default invalidate
the cache key unless it adds the flag inherit_cache=True
at the class level, or if it implements its own caching.
Add working caching to a few elements that were
omitted previously; fix some caching implementations
to suit lesser used edge cases such as json casts
and array slices.
Refine the way BaseCursorResult and CursorMetaData
interact with caching; to suit cases like Alembic
modifying table structures, don't cache the
cursor metadata if it were created against a
cursor.description using non-positional matching,
e.g. "select *". if a table re-ordered its columns
or added/removed, now that data is obsolete.
Additionally we have to adapt the cursor metadata
_keymap regardless of if we just processed
cursor.description, because if we ran against
a cached SQLCompiler we won't have the right
columns in _keymap.
Other refinements to how and when we do this
adaption as some weird cases
were exposed in the Postgresql dialect,
a text() construct that names just one column that
is not actually in the statement. Fixed that
also as it looks like a cut-and-paste artifact
that doesn't actually affect anything.
Various issues with re-use of compiled result maps
and cursor metadata in conjunction with tables being
changed, such as change in order of columns.
mappers can be cleared but the class remains, meaning
a mapper has to use itself as the cache key not the class.
lots of bound parameter / literal issues, due to Alembic
creating a straight subclass of bindparam that renders
inline directly. While we can update Alembic to not
do this, we have to assume other people might be doing
this, so bindparam() implements the inherit_cache=True
logic as well that was a bit involved.
turn on cache stats in logging.
Includes a fix to subqueryloader which moves all setup to
the create_row_processor() phase and elminates any storage
within the compiled context. This includes some changes
to create_row_processor() signature and a revising of the
technique used to determine if the loader can participate
in polymorphic queries, which is also applied to
selectinloading.
DML update.values() and ordered_values() now coerces the
keys as we have tests that pass an arbitrary class here
which only includes __clause_element__(), so the
key can't be cached unless it is coerced. this in turn
changed how composite attributes support bulk update
to use the standard approach of ClauseElement with
annotations that are parsed in the ORM context.
memory profiling successfully caught that the Session
from Query was getting passed into _statement_20()
so that was a big win for that test suite.
Apparently Compiler had .execute() and .scalar() methods
stuck on it, these date back to version 0.4 and there
was a single test in the PostgreSQL dialect tests
that exercised it for no apparent reason. Removed
these methods as well as the concept of a Compiler
holding onto a "bind".
Fixes: #5386
Change-Id: I990b43aab96b42665af1b2187ad6020bee778784
Diffstat (limited to 'doc/build')
-rw-r--r-- | doc/build/changelog/unreleased_14/bind_removed_from_compiler.rst | 8 | ||||
-rw-r--r-- | doc/build/core/tutorial.rst | 110 | ||||
-rw-r--r-- | doc/build/orm/tutorial.rst | 156 |
3 files changed, 141 insertions, 133 deletions
diff --git a/doc/build/changelog/unreleased_14/bind_removed_from_compiler.rst b/doc/build/changelog/unreleased_14/bind_removed_from_compiler.rst new file mode 100644 index 000000000..77c02f517 --- /dev/null +++ b/doc/build/changelog/unreleased_14/bind_removed_from_compiler.rst @@ -0,0 +1,8 @@ +.. change:: + :tags: change, engine + + Removed the concept of a bound engine from the :class:`.Compiler` object, + and removed the ``.execute()`` and ``.scalar()`` methods from :class:`.Compiler`. + These were essentially forgotten methods from over a decade ago and had no + practical use, and it's not appropriate for the :class:`.Compiler` object + itself to be maintaining a reference to an :class:`.Engine`. diff --git a/doc/build/core/tutorial.rst b/doc/build/core/tutorial.rst index a504f8578..7db84c7d6 100644 --- a/doc/build/core/tutorial.rst +++ b/doc/build/core/tutorial.rst @@ -157,7 +157,7 @@ each table first before creating, so it's safe to call multiple times: fullname VARCHAR, PRIMARY KEY (id) ) - () + [...] () COMMIT CREATE TABLE addresses ( id INTEGER NOT NULL, @@ -166,7 +166,7 @@ each table first before creating, so it's safe to call multiple times: PRIMARY KEY (id), FOREIGN KEY(user_id) REFERENCES users (id) ) - () + [...] () COMMIT .. note:: @@ -263,7 +263,7 @@ checked out DBAPI connection resource. Lets feed it our >>> result = conn.execute(ins) {opensql}INSERT INTO users (name, fullname) VALUES (?, ?) - ('jack', 'Jack Jones') + [...] ('jack', 'Jack Jones') COMMIT So the INSERT statement was now issued to the database. Although we got @@ -325,7 +325,7 @@ and use it in the "normal" way: >>> ins = users.insert() >>> conn.execute(ins, id=2, name='wendy', fullname='Wendy Williams') {opensql}INSERT INTO users (id, name, fullname) VALUES (?, ?, ?) - (2, 'wendy', 'Wendy Williams') + [...] (2, 'wendy', 'Wendy Williams') COMMIT {stop}<sqlalchemy.engine.cursor.LegacyCursorResult object at 0x...> @@ -349,7 +349,7 @@ inserted, as we do here to add some email addresses: ... {'user_id': 2, 'email_address' : 'wendy@aol.com'}, ... ]) {opensql}INSERT INTO addresses (user_id, email_address) VALUES (?, ?) - ((1, 'jack@yahoo.com'), (1, 'jack@msn.com'), (2, 'www@www.org'), (2, 'wendy@aol.com')) + [...] ((1, 'jack@yahoo.com'), (1, 'jack@msn.com'), (2, 'www@www.org'), (2, 'wendy@aol.com')) COMMIT {stop}<sqlalchemy.engine.cursor.LegacyCursorResult object at 0x...> @@ -384,7 +384,7 @@ statements is the :func:`_expression.select` function: >>> result = conn.execute(s) {opensql}SELECT users.id, users.name, users.fullname FROM users - () + [...] () Above, we issued a basic :func:`_expression.select` call, placing the ``users`` table within the COLUMNS clause of the select, and then executing. SQLAlchemy @@ -414,7 +414,7 @@ of these tuples as rows are fetched is through tuple assignment: {sql}>>> result = conn.execute(s) SELECT users.id, users.name, users.fullname FROM users - () + [...] () {stop}>>> for id, name, fullname in result: ... print("name:", name, "; fullname: ", fullname) @@ -430,7 +430,7 @@ access: {sql}>>> result = conn.execute(s) SELECT users.id, users.name, users.fullname FROM users - () + [...] () {stop}>>> for row in result: ... print("name:", row.name, "; fullname: ", row.fullname) @@ -446,7 +446,7 @@ progammatically generated, or contains non-ascii characters, the {sql}>>> result = conn.execute(s) SELECT users.id, users.name, users.fullname FROM users - () + [...] () {stop}>>> row = result.fetchone() >>> print("name:", row._mapping['name'], "; fullname:", row._mapping['fullname']) @@ -490,7 +490,7 @@ collection: ... print("name:", row._mapping[users.c.name], "; fullname:", row._mapping[users.c.fullname]) SELECT users.id, users.name, users.fullname FROM users - () + [...] () {stop}name: jack ; fullname: Jack Jones name: wendy ; fullname: Wendy Williams @@ -528,7 +528,7 @@ the ``c`` attribute of the :class:`~sqlalchemy.schema.Table` object: {sql}>>> result = conn.execute(s) SELECT users.name, users.fullname FROM users - () + [...] () {stop}>>> for row in result: ... print(row) (u'jack', u'Jack Jones') @@ -546,7 +546,7 @@ our :func:`_expression.select` statement: ... print(row) SELECT users.id, users.name, users.fullname, addresses.id, addresses.user_id, addresses.email_address FROM users, addresses - () + [...] () {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') (1, u'jack', u'Jack Jones', 3, 2, u'www@www.org') @@ -571,7 +571,7 @@ WHERE clause. We do that using :meth:`_expression.Select.where`: addresses.user_id, addresses.email_address FROM users, addresses WHERE users.id = addresses.user_id - () + [...] () {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') (2, u'wendy', u'Wendy Williams', 3, 2, u'www@www.org') @@ -801,7 +801,7 @@ not have a name: FROM users, addresses WHERE users.id = addresses.user_id AND users.name BETWEEN ? AND ? AND (addresses.email_address LIKE ? OR addresses.email_address LIKE ?) - (', ', 'm', 'z', '%@aol.com', '%@msn.com') + [...] (', ', 'm', 'z', '%@aol.com', '%@msn.com') {stop}[(u'Wendy Williams, wendy@aol.com',)] Once again, SQLAlchemy figured out the FROM clause for our statement. In fact @@ -830,7 +830,7 @@ A shortcut to using :func:`.and_` is to chain together multiple FROM users, addresses WHERE users.id = addresses.user_id AND users.name BETWEEN ? AND ? AND (addresses.email_address LIKE ? OR addresses.email_address LIKE ?) - (', ', 'm', 'z', '%@aol.com', '%@msn.com') + [...] (', ', 'm', 'z', '%@aol.com', '%@msn.com') {stop}[(u'Wendy Williams, wendy@aol.com',)] The way that we can build up a :func:`_expression.select` construct through successive @@ -865,7 +865,7 @@ unchanged. Below, we create a :func:`_expression.text` object and execute it: FROM users, addresses WHERE users.id = addresses.user_id AND users.name BETWEEN ? AND ? AND (addresses.email_address LIKE ? OR addresses.email_address LIKE ?) - ('m', 'z', '%@aol.com', '%@msn.com') + [...] ('m', 'z', '%@aol.com', '%@msn.com') {stop}[(u'Wendy Williams, wendy@aol.com',)] Above, we can see that bound parameters are specified in @@ -944,7 +944,7 @@ result column names in the textual SQL: {opensql}SELECT users.id, addresses.id, users.id, users.name, addresses.email_address AS email FROM users JOIN addresses ON users.id=addresses.user_id WHERE users.id = 1 - () + [...] () {stop} Above, there's three columns in the result that are named "id", but since @@ -1020,7 +1020,7 @@ need to refer to any pre-established :class:`_schema.Table` metadata: FROM users, addresses WHERE users.id = addresses.user_id AND users.name BETWEEN 'm' AND 'z' AND (addresses.email_address LIKE ? OR addresses.email_address LIKE ?) - ('%@aol.com', '%@msn.com') + [...] ('%@aol.com', '%@msn.com') {stop}[(u'Wendy Williams, wendy@aol.com',)] .. versionchanged:: 1.0.0 @@ -1074,7 +1074,7 @@ be quoted: WHERE users.id = addresses.user_id AND users.name BETWEEN 'm' AND 'z' AND (addresses.email_address LIKE ? OR addresses.email_address LIKE ?) - (', ', '%@aol.com', '%@msn.com') + [...] (', ', '%@aol.com', '%@msn.com') {stop}[(u'Wendy Williams, wendy@aol.com',)] Ordering or Grouping by a Label @@ -1101,7 +1101,7 @@ are rendered fully: {sql}>>> conn.execute(stmt).fetchall() SELECT addresses.user_id, count(addresses.id) AS num_addresses FROM addresses GROUP BY addresses.user_id ORDER BY addresses.user_id, num_addresses - () + [...] () {stop}[(1, 2), (2, 2)] We can use modifiers like :func:`.asc` or :func:`.desc` by passing the string @@ -1118,7 +1118,7 @@ name: {sql}>>> conn.execute(stmt).fetchall() SELECT addresses.user_id, count(addresses.id) AS num_addresses FROM addresses GROUP BY addresses.user_id ORDER BY addresses.user_id, num_addresses DESC - () + [...] () {stop}[(1, 2), (2, 2)] Note that the string feature here is very much tailored to when we have @@ -1141,7 +1141,7 @@ by a column name that appears more than once: users_2.name, users_2.fullname FROM users AS users_1, users AS users_2 WHERE users_1.name > users_2.name ORDER BY users_1.name - () + [...] () {stop}[(2, u'wendy', u'Wendy Williams', 1, u'jack', u'Jack Jones')] @@ -1193,7 +1193,7 @@ once for each address. We create two :class:`_expression.Alias` constructs aga AND users.id = addresses_2.user_id AND addresses_1.email_address = ? AND addresses_2.email_address = ? - ('jack@msn.com', 'jack@yahoo.com') + [...] ('jack@msn.com', 'jack@yahoo.com') {stop}[(1, u'jack', u'Jack Jones')] Note that the :class:`_expression.Alias` construct generated the names ``addresses_1`` and @@ -1235,7 +1235,7 @@ by making :class:`.Subquery` of the entire statement: AND addresses_1.email_address = ? AND addresses_2.email_address = ?) AS anon_1 WHERE users.id = anon_1.id - ('jack@msn.com', 'jack@yahoo.com') + [...] ('jack@msn.com', 'jack@yahoo.com') {stop}[(u'jack',)] .. versionchanged:: 1.4 Added the :class:`.Subquery` object and created more of a @@ -1291,7 +1291,7 @@ here we make use of the :meth:`_expression.Select.select_from` method: {sql}>>> conn.execute(s).fetchall() SELECT users.fullname FROM users JOIN addresses ON addresses.email_address LIKE users.name || ? - ('%',) + [...] ('%',) {stop}[(u'Jack Jones',), (u'Jack Jones',), (u'Wendy Williams',)] The :meth:`_expression.FromClause.outerjoin` method creates ``LEFT OUTER JOIN`` constructs, @@ -1350,7 +1350,7 @@ typically acquires using the :meth:`_expression.Select.cte` method on a SELECT addresses.id, addresses.user_id, addresses.email_address FROM addresses, anon_1 WHERE addresses.user_id = anon_1.id ORDER BY addresses.id - ('wendy',) + [...] ('wendy',) {stop}[(3, 2, 'www@www.org'), (4, 2, 'wendy@aol.com')] The CTE construct is a great way to provide a source of rows that is @@ -1388,7 +1388,7 @@ this form looks like: SELECT addresses.id, addresses.user_id, addresses.email_address FROM addresses, anon_1 WHERE addresses.user_id = anon_1.id ORDER BY addresses.id - () + [...] () {stop}[(1, 1, 'jack@yahoo.com'), (2, 1, 'jack@msn.com'), (3, 2, 'www@www.org'), (4, 2, 'wendy@aol.com')] @@ -1421,7 +1421,7 @@ at execution time, as here where it converts to positional for SQLite: SELECT users.id, users.name, users.fullname FROM users WHERE users.name = ? - ('wendy',) + [...] ('wendy',) {stop}[(2, u'wendy', u'Wendy Williams')] Another important aspect of :func:`.bindparam` is that it may be assigned a @@ -1436,7 +1436,7 @@ off to the database: SELECT users.id, users.name, users.fullname FROM users WHERE users.name LIKE ? || '%' - ('wendy',) + [...] ('wendy',) {stop}[(2, u'wendy', u'Wendy Williams')] @@ -1462,7 +1462,7 @@ single named value is needed in the execute parameters: FROM users LEFT OUTER JOIN addresses ON users.id = addresses.user_id WHERE users.name LIKE ? || '%' OR addresses.email_address LIKE ? || '@%' ORDER BY addresses.id - ('jack', 'jack') + [...] ('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:: @@ -1531,7 +1531,7 @@ not important in this case: ... ).scalar() {opensql}SELECT max(addresses.email_address) AS maxemail FROM addresses - () + [...] () {stop}u'www@www.org' Databases such as PostgreSQL and Oracle which support functions that return @@ -1648,7 +1648,7 @@ object as arguments: >>> conn.execute(s).fetchall() {opensql}SELECT CAST(users.id AS VARCHAR) AS id FROM users - () + [...] () {stop}[('1',), ('2',)] The :func:`.cast` function is used not just when converting between datatypes, @@ -1724,7 +1724,7 @@ module level functions :func:`_expression.union` and SELECT addresses.id, addresses.user_id, addresses.email_address FROM addresses WHERE addresses.email_address LIKE ? ORDER BY email_address - ('foo@bar.com', '%@yahoo.com') + [...] ('foo@bar.com', '%@yahoo.com') {stop}[(1, 1, u'jack@yahoo.com')] Also available, though not supported on all databases, are @@ -1750,7 +1750,7 @@ Also available, though not supported on all databases, are SELECT addresses.id, addresses.user_id, addresses.email_address FROM addresses WHERE addresses.email_address LIKE ? - ('%@%.com', '%@msn.com') + [...] ('%@%.com', '%@msn.com') {stop}[(1, 1, u'jack@yahoo.com'), (4, 2, u'wendy@aol.com')] A common issue with so-called "compound" selectables arises due to the fact @@ -1788,7 +1788,7 @@ want the "union" to be stated as a subquery: SELECT addresses.id, addresses.user_id, addresses.email_address FROM addresses WHERE addresses.email_address LIKE ? - ('%@yahoo.com', '%@msn.com', '%@msn.com') + [...] ('%@yahoo.com', '%@msn.com', '%@msn.com') {stop}[(1, 1, u'jack@yahoo.com')] .. seealso:: @@ -1868,7 +1868,7 @@ other column within another :func:`_expression.select`: FROM addresses WHERE users.id = addresses.user_id) AS anon_1 FROM users - () + [...] () {stop}[(u'jack', 2), (u'wendy', 2)] To apply a non-anonymous column name to our scalar select, we create @@ -1884,7 +1884,7 @@ it using :meth:`_expression.SelectBase.label` instead: FROM addresses WHERE users.id = addresses.user_id) AS address_count FROM users - () + [...] () {stop}[(u'jack', 2), (u'wendy', 2)] .. seealso:: @@ -1918,7 +1918,7 @@ still have at least one FROM clause of its own. For example: FROM addresses WHERE addresses.user_id = users.id AND addresses.email_address = ?) - ('jack@yahoo.com',) + [...] ('jack@yahoo.com',) {stop}[(u'jack',)] Auto-correlation will usually do what's expected, however it can also be controlled. @@ -1943,7 +1943,7 @@ may be correlated: WHERE users.id = (SELECT users.id FROM users WHERE users.id = addresses.user_id AND users.name = ?) - ('jack',) + [...] ('jack',) {stop}[(u'jack', u'jack@yahoo.com'), (u'jack', u'jack@msn.com')] To entirely disable a statement from correlating, we can pass ``None`` @@ -1962,7 +1962,7 @@ as the argument: WHERE users.id = (SELECT users.id FROM users WHERE users.name = ?) - ('wendy',) + [...] ('wendy',) {stop}[(u'wendy',)] We can also control correlation via exclusion, using the :meth:`_expression.Select.correlate_except` @@ -1985,7 +1985,7 @@ by telling it to correlate all FROM clauses except for ``users``: WHERE users.id = (SELECT users.id FROM users WHERE users.id = addresses.user_id AND users.name = ?) - ('jack',) + [...] ('jack',) {stop}[(u'jack', u'jack@yahoo.com'), (u'jack', u'jack@msn.com')] .. _lateral_selects: @@ -2070,7 +2070,7 @@ Ordering is done by passing column expressions to the >>> conn.execute(stmt).fetchall() {opensql}SELECT users.name FROM users ORDER BY users.name - () + [...] () {stop}[(u'jack',), (u'wendy',)] Ascending or descending can be controlled using the :meth:`_expression.ColumnElement.asc` @@ -2082,7 +2082,7 @@ and :meth:`_expression.ColumnElement.desc` modifiers: >>> conn.execute(stmt).fetchall() {opensql}SELECT users.name FROM users ORDER BY users.name DESC - () + [...] () {stop}[(u'wendy',), (u'jack',)] Grouping refers to the GROUP BY clause, and is usually used in conjunction @@ -2099,7 +2099,7 @@ This is provided via the :meth:`_expression.SelectBase.group_by` method: FROM users JOIN addresses ON users.id = addresses.user_id GROUP BY users.name - () + [...] () {stop}[(u'jack', 2), (u'wendy', 2)] HAVING can be used to filter results on an aggregate value, after GROUP BY has @@ -2118,7 +2118,7 @@ method: ON users.id = addresses.user_id GROUP BY users.name HAVING length(users.name) > ? - (4,) + [...] (4,) {stop}[(u'wendy', 2)] A common system of dealing with duplicates in composed SELECT statements @@ -2135,7 +2135,7 @@ is the DISTINCT modifier. A simple DISTINCT clause can be added using the {opensql}SELECT DISTINCT users.name FROM users, addresses WHERE (addresses.email_address LIKE '%' || users.name || '%') - () + [...] () {stop}[(u'jack',), (u'wendy',)] Most database backends support a system of limiting how many rows @@ -2156,7 +2156,7 @@ into the current backend's methodology: {opensql}SELECT users.name, addresses.email_address FROM users JOIN addresses ON users.id = addresses.user_id LIMIT ? OFFSET ? - (1, 1) + [...] (1, 1) {stop}[(u'jack', u'jack@msn.com')] @@ -2181,7 +2181,7 @@ as a value: ... values(fullname="Fullname: " + users.c.name) >>> conn.execute(stmt) {opensql}UPDATE users SET fullname=(? || users.name) - ('Fullname: ',) + [...] ('Fullname: ',) COMMIT {stop}<sqlalchemy.engine.cursor.LegacyCursorResult object at 0x...> @@ -2210,7 +2210,7 @@ as in the example below: ... {'id':6, '_name':'name3'}, ... ]) {opensql}INSERT INTO users (id, name) VALUES (?, (? || ?)) - ((4, 'name1', ' .. name'), (5, 'name2', ' .. name'), (6, 'name3', ' .. name')) + [...] ((4, 'name1', ' .. name'), (5, 'name2', ' .. name'), (6, 'name3', ' .. name')) COMMIT <sqlalchemy.engine.cursor.LegacyCursorResult object at 0x...> @@ -2226,7 +2226,7 @@ that can be specified: >>> conn.execute(stmt) {opensql}UPDATE users SET name=? WHERE users.name = ? - ('ed', 'jack') + [...] ('ed', 'jack') COMMIT {stop}<sqlalchemy.engine.cursor.LegacyCursorResult object at 0x...> @@ -2246,7 +2246,7 @@ used to achieve this: ... {'oldname':'jim', 'newname':'jake'}, ... ]) {opensql}UPDATE users SET name=? WHERE users.name = ? - (('ed', 'jack'), ('mary', 'wendy'), ('jake', 'jim')) + [...] (('ed', 'jack'), ('mary', 'wendy'), ('jake', 'jim')) COMMIT {stop}<sqlalchemy.engine.cursor.LegacyCursorResult object at 0x...> @@ -2268,7 +2268,7 @@ subquery using :meth:`_expression.Select.scalar_subquery`: FROM addresses WHERE addresses.user_id = users.id LIMIT ? OFFSET ?) - (1, 0) + [...] (1, 0) COMMIT {stop}<sqlalchemy.engine.cursor.LegacyCursorResult object at 0x...> @@ -2385,13 +2385,13 @@ Finally, a delete. This is accomplished easily enough using the >>> conn.execute(addresses.delete()) {opensql}DELETE FROM addresses - () + [...] () COMMIT {stop}<sqlalchemy.engine.cursor.LegacyCursorResult object at 0x...> >>> conn.execute(users.delete().where(users.c.name > 'm')) {opensql}DELETE FROM users WHERE users.name > ? - ('m',) + [...] ('m',) COMMIT {stop}<sqlalchemy.engine.cursor.LegacyCursorResult object at 0x...> @@ -2439,7 +2439,7 @@ The value is available as :attr:`_engine.CursorResult.rowcount`: >>> result = conn.execute(users.delete()) {opensql}DELETE FROM users - () + [...] () COMMIT {stop}>>> result.rowcount 1 diff --git a/doc/build/orm/tutorial.rst b/doc/build/orm/tutorial.rst index c08daa7fd..8ea8af4a2 100644 --- a/doc/build/orm/tutorial.rst +++ b/doc/build/orm/tutorial.rst @@ -210,16 +210,16 @@ the actual ``CREATE TABLE`` statement: >>> Base.metadata.create_all(engine) PRAGMA main.table_info("users") - () + [...] () PRAGMA temp.table_info("users") - () + [...] () CREATE TABLE users ( id INTEGER NOT NULL, name VARCHAR, fullname VARCHAR, nickname VARCHAR, PRIMARY KEY (id) ) - () + [...] () COMMIT .. topic:: Minimal Table Descriptions vs. Full Descriptions @@ -373,7 +373,7 @@ added: {sql}>>> our_user = session.query(User).filter_by(name='ed').first() # doctest:+NORMALIZE_WHITESPACE BEGIN (implicit) INSERT INTO users (name, fullname, nickname) VALUES (?, ?, ?) - ('ed', 'Ed Jones', 'edsnickname') + [...] ('ed', 'Ed Jones', 'edsnickname') SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, @@ -381,7 +381,7 @@ added: FROM users WHERE users.name = ? LIMIT ? OFFSET ? - ('ed', 1, 0) + [...] ('ed', 1, 0) {stop}>>> our_user <User(name='ed', fullname='Ed Jones', nickname='edsnickname')> @@ -448,13 +448,13 @@ three new ``User`` objects we've added: {sql}>>> session.commit() UPDATE users SET nickname=? WHERE users.id = ? - ('eddie', 1) + [...] ('eddie', 1) INSERT INTO users (name, fullname, nickname) VALUES (?, ?, ?) - ('wendy', 'Wendy Williams', 'windy') + [...] ('wendy', 'Wendy Williams', 'windy') INSERT INTO users (name, fullname, nickname) VALUES (?, ?, ?) - ('mary', 'Mary Contrary', 'mary') + [...] ('mary', 'Mary Contrary', 'mary') INSERT INTO users (name, fullname, nickname) VALUES (?, ?, ?) - ('fred', 'Fred Flintstone', 'freddy') + [...] ('fred', 'Fred Flintstone', 'freddy') COMMIT :meth:`~.Session.commit` flushes the remaining changes to the @@ -475,7 +475,7 @@ If we look at Ed's ``id`` attribute, which earlier was ``None``, it now has a va users.nickname AS users_nickname FROM users WHERE users.id = ? - (1,) + [...] (1,) {stop}1 After the :class:`~sqlalchemy.orm.session.Session` inserts new rows in the @@ -519,16 +519,16 @@ Querying the session, we can see that they're flushed into the current transacti {sql}>>> session.query(User).filter(User.name.in_(['Edwardo', 'fakeuser'])).all() UPDATE users SET name=? WHERE users.id = ? - ('Edwardo', 1) + [...] ('Edwardo', 1) INSERT INTO users (name, fullname, nickname) VALUES (?, ?, ?) - ('fakeuser', 'Invalid', '12345') + [...] ('fakeuser', 'Invalid', '12345') SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.nickname AS users_nickname FROM users WHERE users.name IN (?, ?) - ('Edwardo', 'fakeuser') + [...] ('Edwardo', 'fakeuser') {stop}[<User(name='Edwardo', fullname='Ed Jones', nickname='eddie')>, <User(name='fakeuser', fullname='Invalid', nickname='12345')>] Rolling back, we can see that ``ed_user``'s name is back to ``ed``, and @@ -548,7 +548,7 @@ Rolling back, we can see that ``ed_user``'s name is back to ``ed``, and users.nickname AS users_nickname FROM users WHERE users.id = ? - (1,) + [...] (1,) {stop}u'ed' >>> fake_user in session False @@ -564,7 +564,7 @@ issuing a SELECT illustrates the changes made to the database: users.nickname AS users_nickname FROM users WHERE users.name IN (?, ?) - ('ed', 'fakeuser') + [...] ('ed', 'fakeuser') {stop}[<User(name='ed', fullname='Ed Jones', nickname='eddie')>] .. _ormtutorial_querying: @@ -590,7 +590,7 @@ returned: users.fullname AS users_fullname, users.nickname AS users_nickname FROM users ORDER BY users.id - () + [...] () {stop}ed Ed Jones wendy Wendy Williams mary Mary Contrary @@ -609,7 +609,7 @@ is expressed as tuples: SELECT users.name AS users_name, users.fullname AS users_fullname FROM users - () + [...] () {stop}ed Ed Jones wendy Wendy Williams mary Mary Contrary @@ -630,7 +630,7 @@ class: users.fullname AS users_fullname, users.nickname AS users_nickname FROM users - () + [...] () {stop}<User(name='ed', fullname='Ed Jones', nickname='eddie')> ed <User(name='wendy', fullname='Wendy Williams', nickname='windy')> wendy <User(name='mary', fullname='Mary Contrary', nickname='mary')> mary @@ -647,7 +647,7 @@ is mapped to one (such as ``User.name``): ... print(row.name_label) SELECT users.name AS name_label FROM users - (){stop} + [...] (){stop} ed wendy mary @@ -669,7 +669,7 @@ entities are present in the call to :meth:`~.Session.query`, can be controlled u user_alias.fullname AS user_alias_fullname, user_alias.nickname AS user_alias_nickname FROM users AS user_alias - (){stop} + [...] (){stop} <User(name='ed', fullname='Ed Jones', nickname='eddie')> <User(name='wendy', fullname='Wendy Williams', nickname='windy')> <User(name='mary', fullname='Mary Contrary', nickname='mary')> @@ -689,7 +689,7 @@ conjunction with ORDER BY: users.nickname AS users_nickname FROM users ORDER BY users.id LIMIT ? OFFSET ? - (2, 1){stop} + [...] (2, 1){stop} <User(name='wendy', fullname='Wendy Williams', nickname='windy')> <User(name='mary', fullname='Mary Contrary', nickname='mary')> @@ -703,7 +703,7 @@ and filtering results, which is accomplished either with ... print(name) SELECT users.name AS users_name FROM users WHERE users.fullname = ? - ('Ed Jones',) + [...] ('Ed Jones',) {stop}ed ...or :func:`~sqlalchemy.orm.query.Query.filter`, which uses more flexible SQL @@ -717,7 +717,7 @@ operators with the class-level attributes on your mapped class: ... print(name) SELECT users.name AS users_name FROM users WHERE users.fullname = ? - ('Ed Jones',) + [...] ('Ed Jones',) {stop}ed The :class:`~sqlalchemy.orm.query.Query` object is fully **generative**, meaning @@ -739,7 +739,7 @@ users named "ed" with a full name of "Ed Jones", you can call users.nickname AS users_nickname FROM users WHERE users.name = ? AND users.fullname = ? - ('ed', 'Ed Jones') + [...] ('ed', 'Ed Jones') {stop}<User(name='ed', fullname='Ed Jones', nickname='eddie')> Common Filter Operators @@ -861,7 +861,7 @@ database results. Here's a brief tour: users.nickname AS users_nickname FROM users WHERE users.name LIKE ? ORDER BY users.id - ('%ed',) + [...] ('%ed',) {stop}[<User(name='ed', fullname='Ed Jones', nickname='eddie')>, <User(name='fred', fullname='Fred Flintstone', nickname='freddy')>] @@ -893,7 +893,7 @@ database results. Here's a brief tour: FROM users WHERE users.name LIKE ? ORDER BY users.id LIMIT ? OFFSET ? - ('%ed', 1, 0) + [...] ('%ed', 1, 0) {stop}<User(name='ed', fullname='Ed Jones', nickname='eddie')> * :meth:`_query.Query.one()` fully fetches all rows, and if not @@ -937,7 +937,7 @@ database results. Here's a brief tour: SELECT users.id AS users_id FROM users WHERE users.name = ? ORDER BY users.id - ('ed',) + [...] ('ed',) {stop}1 .. _orm_tutorial_literal_sql: @@ -965,7 +965,7 @@ by most applicable methods. For example, users.nickname AS users_nickname FROM users WHERE id<224 ORDER BY id - () + [...] () {stop}ed wendy mary @@ -985,7 +985,7 @@ method: users.nickname AS users_nickname FROM users WHERE id<? and name=? ORDER BY users.id - (224, 'fred') + [...] (224, 'fred') {stop}<User(name='fred', fullname='Fred Flintstone', nickname='freddy')> To use an entirely string-based statement, a :func:`_expression.text` construct @@ -999,7 +999,7 @@ returned by the SQL statement based on column name:: {sql}>>> session.query(User).from_statement( ... text("SELECT * FROM users where name=:name")).params(name='ed').all() SELECT * FROM users where name=? - ('ed',) + [...] ('ed',) {stop}[<User(name='ed', fullname='Ed Jones', nickname='eddie')>] For better targeting of mapped columns to a textual SELECT, as well as to @@ -1013,7 +1013,7 @@ columns are passed in the desired order to :meth:`_expression.TextClause.columns >>> stmt = stmt.columns(User.name, User.id, User.fullname, User.nickname) {sql}>>> session.query(User).from_statement(stmt).params(name='ed').all() SELECT name, id, fullname, nickname FROM users where name=? - ('ed',) + [...] ('ed',) {stop}[<User(name='ed', fullname='Ed Jones', nickname='eddie')>] When selecting from a :func:`_expression.text` construct, the :class:`_query.Query` @@ -1028,7 +1028,7 @@ any other case: {sql}>>> session.query(User.id, User.name).\ ... from_statement(stmt).params(name='ed').all() SELECT name, id FROM users where name=? - ('ed',) + [...] ('ed',) {stop}[(1, u'ed')] .. seealso:: @@ -1052,7 +1052,7 @@ counting called :meth:`~sqlalchemy.orm.query.Query.count()`: users.nickname AS users_nickname FROM users WHERE users.name LIKE ?) AS anon_1 - ('%ed',) + [...] ('%ed',) {stop}2 .. sidebar:: Counting on ``count()`` @@ -1085,7 +1085,7 @@ use it to return the count of each distinct user name: {sql}>>> session.query(func.count(User.name), User.name).group_by(User.name).all() SELECT count(users.name) AS count_1, users.name AS users_name FROM users GROUP BY users.name - () + [...] () {stop}[(1, u'ed'), (1, u'fred'), (1, u'mary'), (1, u'wendy')] To achieve our simple ``SELECT count(*) FROM table``, we can apply it as: @@ -1095,7 +1095,7 @@ To achieve our simple ``SELECT count(*) FROM table``, we can apply it as: {sql}>>> session.query(func.count('*')).select_from(User).scalar() SELECT count(?) AS count_1 FROM users - ('*',) + [...] ('*',) {stop}4 The usage of :meth:`_query.Query.select_from` can be removed if we express the count in terms @@ -1106,7 +1106,7 @@ of the ``User`` primary key directly: {sql}>>> session.query(func.count(User.id)).scalar() SELECT count(users.id) AS count_1 FROM users - () + [...] () {stop}4 .. _orm_tutorial_relationship: @@ -1223,7 +1223,7 @@ already been created: PRIMARY KEY (id), FOREIGN KEY(user_id) REFERENCES users (id) ) - () + [...] () COMMIT Working with Related Objects @@ -1272,11 +1272,11 @@ known as **cascading**: >>> session.add(jack) {sql}>>> session.commit() INSERT INTO users (name, fullname, nickname) VALUES (?, ?, ?) - ('jack', 'Jack Bean', 'gjffdd') + [...] ('jack', 'Jack Bean', 'gjffdd') INSERT INTO addresses (email_address, user_id) VALUES (?, ?) - ('jack@google.com', 5) + [...] ('jack@google.com', 5) INSERT INTO addresses (email_address, user_id) VALUES (?, ?) - ('j25@yahoo.com', 5) + [...] ('j25@yahoo.com', 5) COMMIT Querying for Jack, we get just Jack back. No SQL is yet issued for Jack's addresses: @@ -1292,7 +1292,7 @@ Querying for Jack, we get just Jack back. No SQL is yet issued for Jack's addre users.nickname AS users_nickname FROM users WHERE users.name = ? - ('jack',) + [...] ('jack',) {stop}>>> jack <User(name='jack', fullname='Jack Bean', nickname='gjffdd')> @@ -1308,7 +1308,7 @@ Let's look at the ``addresses`` collection. Watch the SQL: addresses.user_id AS addresses_user_id FROM addresses WHERE ? = addresses.user_id ORDER BY addresses.id - (5,) + [...] (5,) {stop}[<Address(email_address='jack@google.com')>, <Address(email_address='j25@yahoo.com')>] When we accessed the ``addresses`` collection, SQL was suddenly issued. This @@ -1349,7 +1349,7 @@ Below we load the ``User`` and ``Address`` entities at once using this method: FROM users, addresses WHERE users.id = addresses.user_id AND addresses.email_address = ? - ('jack@google.com',) + [...] ('jack@google.com',) {stop}<User(name='jack', fullname='Jack Bean', nickname='gjffdd')> <Address(email_address='jack@google.com')> @@ -1367,7 +1367,7 @@ using the :meth:`_query.Query.join` method: users.nickname AS users_nickname FROM users JOIN addresses ON users.id = addresses.user_id WHERE addresses.email_address = ? - ('jack@google.com',) + [...] ('jack@google.com',) {stop}[<User(name='jack', fullname='Jack Bean', nickname='gjffdd')>] :meth:`_query.Query.join` knows how to join between ``User`` @@ -1435,7 +1435,7 @@ distinct email addresses at the same time: ON users.id = addresses_2.user_id WHERE addresses_1.email_address = ? AND addresses_2.email_address = ? - ('jack@google.com', 'j25@yahoo.com') + [...] ('jack@google.com', 'j25@yahoo.com') {stop}jack jack@google.com j25@yahoo.com In addition to using the :meth:`_orm.PropComparator.of_type` method, it is @@ -1496,7 +1496,7 @@ accessible through an attribute called ``c``: FROM addresses GROUP BY addresses.user_id) AS anon_1 ON users.id = anon_1.user_id ORDER BY users.id - ('*',) + [...] ('*',) {stop}<User(name='ed', fullname='Ed Jones', nickname='eddie')> None <User(name='wendy', fullname='Wendy Williams', nickname='windy')> None <User(name='mary', fullname='Mary Contrary', nickname='mary')> None @@ -1534,7 +1534,7 @@ to associate an "alias" of a mapped class to a subquery: FROM addresses WHERE addresses.email_address != ?) AS anon_1 ON users.id = anon_1.user_id - ('j25@yahoo.com',) + [...] ('j25@yahoo.com',) {stop}<User(name='jack', fullname='Jack Bean', nickname='gjffdd')> <Address(email_address='jack@google.com')> @@ -1559,7 +1559,7 @@ There is an explicit EXISTS construct, which looks like this: WHERE EXISTS (SELECT * FROM addresses WHERE addresses.user_id = users.id) - () + [...] () {stop}jack The :class:`~sqlalchemy.orm.query.Query` features several operators which make @@ -1576,7 +1576,7 @@ usage of EXISTS automatically. Above, the statement can be expressed along the WHERE EXISTS (SELECT 1 FROM addresses WHERE users.id = addresses.user_id) - () + [...] () {stop}jack :meth:`~.RelationshipProperty.Comparator.any` takes criterion as well, to limit the rows matched: @@ -1591,7 +1591,7 @@ usage of EXISTS automatically. Above, the statement can be expressed along the WHERE EXISTS (SELECT 1 FROM addresses WHERE users.id = addresses.user_id AND addresses.email_address LIKE ?) - ('%google%',) + [...] ('%google%',) {stop}jack :meth:`~.RelationshipProperty.Comparator.has` is the same operator as @@ -1609,7 +1609,7 @@ usage of EXISTS automatically. Above, the statement can be expressed along the WHERE NOT (EXISTS (SELECT 1 FROM users WHERE users.id = addresses.user_id AND users.name = ?)) - ('jack',) + [...] ('jack',) {stop}[] Common Relationship Operators @@ -1685,14 +1685,14 @@ at once: users.nickname AS users_nickname FROM users WHERE users.name = ? - ('jack',) + [...] ('jack',) SELECT addresses.user_id AS addresses_user_id, addresses.id AS addresses_id, addresses.email_address AS addresses_email_address FROM addresses WHERE addresses.user_id IN (?) ORDER BY addresses.id - (5,) + [...] (5,) {stop}>>> jack <User(name='jack', fullname='Jack Bean', nickname='gjffdd')> @@ -1728,7 +1728,7 @@ will emit the extra join regardless: FROM users LEFT OUTER JOIN addresses AS addresses_1 ON users.id = addresses_1.user_id WHERE users.name = ? ORDER BY addresses_1.id - ('jack',) + [...] ('jack',) {stop}>>> jack <User(name='jack', fullname='Jack Bean', nickname='gjffdd')> @@ -1792,7 +1792,7 @@ attribute: addresses.user_id AS addresses_user_id FROM addresses JOIN users ON users.id = addresses.user_id WHERE users.name = ? - ('jack',) + [...] ('jack',) {stop}>>> jacks_addresses [<Address(email_address='jack@google.com')>, <Address(email_address='j25@yahoo.com')>] @@ -1814,9 +1814,9 @@ in the session, then we'll issue a ``count`` query to see that no rows remain: >>> session.delete(jack) {sql}>>> session.query(User).filter_by(name='jack').count() UPDATE addresses SET user_id=? WHERE addresses.id = ? - ((None, 1), (None, 2)) + [...] ((None, 1), (None, 2)) DELETE FROM users WHERE users.id = ? - (5,) + [...] (5,) SELECT count(*) AS count_1 FROM (SELECT users.id AS users_id, users.name AS users_name, @@ -1824,7 +1824,7 @@ in the session, then we'll issue a ``count`` query to see that no rows remain: users.nickname AS users_nickname FROM users WHERE users.name = ?) AS anon_1 - ('jack',) + [...] ('jack',) {stop}0 So far, so good. How about Jack's ``Address`` objects ? @@ -1840,7 +1840,7 @@ So far, so good. How about Jack's ``Address`` objects ? addresses.user_id AS addresses_user_id FROM addresses WHERE addresses.email_address IN (?, ?)) AS anon_1 - ('jack@google.com', 'j25@yahoo.com') + [...] ('jack@google.com', 'j25@yahoo.com') {stop}2 Uh oh, they're still there ! Analyzing the flush SQL, we can see that the @@ -1914,7 +1914,7 @@ being deleted: users.nickname AS users_nickname FROM users WHERE users.id = ? - (5,) + [...] (5,) {stop} # remove one Address (lazy load fires off) @@ -1924,7 +1924,7 @@ being deleted: addresses.user_id AS addresses_user_id FROM addresses WHERE ? = addresses.user_id - (5,) + [...] (5,) {stop} # only one address remains @@ -1932,14 +1932,14 @@ being deleted: ... Address.email_address.in_(['jack@google.com', 'j25@yahoo.com']) ... ).count() DELETE FROM addresses WHERE addresses.id = ? - (2,) + [...] (2,) SELECT count(*) AS count_1 FROM (SELECT addresses.id AS addresses_id, addresses.email_address AS addresses_email_address, addresses.user_id AS addresses_user_id FROM addresses WHERE addresses.email_address IN (?, ?)) AS anon_1 - ('jack@google.com', 'j25@yahoo.com') + [...] ('jack@google.com', 'j25@yahoo.com') {stop}1 Deleting Jack will delete both Jack and the remaining ``Address`` associated @@ -1951,9 +1951,9 @@ with the user: {sql}>>> session.query(User).filter_by(name='jack').count() DELETE FROM addresses WHERE addresses.id = ? - (1,) + [...] (1,) DELETE FROM users WHERE users.id = ? - (5,) + [...] (5,) SELECT count(*) AS count_1 FROM (SELECT users.id AS users_id, users.name AS users_name, @@ -1961,7 +1961,7 @@ with the user: users.nickname AS users_nickname FROM users WHERE users.name = ?) AS anon_1 - ('jack',) + [...] ('jack',) {stop}0 {sql}>>> session.query(Address).filter( @@ -1973,7 +1973,7 @@ with the user: addresses.user_id AS addresses_user_id FROM addresses WHERE addresses.email_address IN (?, ?)) AS anon_1 - ('jack@google.com', 'j25@yahoo.com') + [...] ('jack@google.com', 'j25@yahoo.com') {stop}0 .. topic:: More on Cascades @@ -2086,7 +2086,7 @@ Create new tables: PRIMARY KEY (id), UNIQUE (keyword) ) - () + [...] () COMMIT CREATE TABLE posts ( id INTEGER NOT NULL, @@ -2096,7 +2096,7 @@ Create new tables: PRIMARY KEY (id), FOREIGN KEY(user_id) REFERENCES users (id) ) - () + [...] () COMMIT CREATE TABLE post_keywords ( post_id INTEGER NOT NULL, @@ -2105,7 +2105,7 @@ Create new tables: FOREIGN KEY(post_id) REFERENCES posts (id), FOREIGN KEY(keyword_id) REFERENCES keywords (id) ) - () + [...] () COMMIT Usage is not too different from what we've been doing. Let's give Wendy some blog posts: @@ -2121,7 +2121,7 @@ Usage is not too different from what we've been doing. Let's give Wendy some bl users.nickname AS users_nickname FROM users WHERE users.name = ? - ('wendy',) + [...] ('wendy',) {stop} >>> post = BlogPost("Wendy's Blog Post", "This is a test", wendy) >>> session.add(post) @@ -2144,13 +2144,13 @@ keyword string 'firstpost'": ... filter(BlogPost.keywords.any(keyword='firstpost')).\ ... all() INSERT INTO keywords (keyword) VALUES (?) - ('wendy',) + [...] ('wendy',) INSERT INTO keywords (keyword) VALUES (?) - ('firstpost',) + [...] ('firstpost',) INSERT INTO posts (user_id, headline, body) VALUES (?, ?, ?) - (2, "Wendy's Blog Post", 'This is a test') + [...] (2, "Wendy's Blog Post", 'This is a test') INSERT INTO post_keywords (post_id, keyword_id) VALUES (?, ?) - (...) + [...] (...) SELECT posts.id AS posts_id, posts.user_id AS posts_user_id, posts.headline AS posts_headline, @@ -2161,7 +2161,7 @@ keyword string 'firstpost'": WHERE posts.id = post_keywords.post_id AND keywords.id = post_keywords.keyword_id AND keywords.keyword = ?) - ('firstpost',) + [...] ('firstpost',) {stop}[BlogPost("Wendy's Blog Post", 'This is a test', <User(name='wendy', fullname='Wendy Williams', nickname='windy')>)] If we want to look up posts owned by the user ``wendy``, we can tell @@ -2183,7 +2183,7 @@ the query to narrow down to that ``User`` object as a parent: WHERE posts.id = post_keywords.post_id AND keywords.id = post_keywords.keyword_id AND keywords.keyword = ?)) - (2, 'firstpost') + [...] (2, 'firstpost') {stop}[BlogPost("Wendy's Blog Post", 'This is a test', <User(name='wendy', fullname='Wendy Williams', nickname='windy')>)] Or we can use Wendy's own ``posts`` relationship, which is a "dynamic" @@ -2204,7 +2204,7 @@ relationship, to query straight from there: WHERE posts.id = post_keywords.post_id AND keywords.id = post_keywords.keyword_id AND keywords.keyword = ?)) - (2, 'firstpost') + [...] (2, 'firstpost') {stop}[BlogPost("Wendy's Blog Post", 'This is a test', <User(name='wendy', fullname='Wendy Williams', nickname='windy')>)] Further Reference |