- fixed bug which was preventing UNIONS from being cloneable,
[ticket:986]
+ - added "bind" keyword argument to insert(), update(), delete();
+ .bind property is settable on those as well as select().
+
- orm
- any(), has(), contains(), ~contains(), attribute level ==
and != now work properly with self-referential relations -
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 `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:
{python}
- >>> from sqlalchemy.databases.sqlite import SQLiteDialect
- >>> compiled = ins.compile(dialect=SQLiteDialect())
- >>> str(compiled)
+ >>> ins.bind = engine
+ >>> str(ins)
'INSERT INTO users (name, fullname) VALUES (?, ?)'
What about the `result` variable we got when we called `execute()` ? As the SQLAlchemy `Connection` object references a DBAPI connection, the result, known as a `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:
Where `||` is the string concatenation operator used on most databases. But not all of them. MySQL users, fear not:
{python}
- >>> from sqlalchemy.databases.mysql import MySQLDialect
- >>> print (users.c.name + users.c.fullname).compile(dialect=MySQLDialect())
+ >>> print (users.c.name + users.c.fullname).compile(bind=create_engine('mysql://'))
concat(users.name, users.fullname)
-The above illustrates the SQL that's generated for an `Engine` that's connected to a MySQL database (note that the `Dialect` is normally created behind the scenes; we created one above just to illustrate without using an engine).
+The above illustrates the SQL that's generated for an `Engine` that's connected to a MySQL database; the `||` operator now compiles as MySQL's `concat()` function.
If you have come across an operator which really isn't available, you can always use the `op()` method; this generates whatever operator you need:
yield t
def bind(self):
+ if self._bind:
+ return self._bind
for s in self.selects:
e = s.bind
if e:
return e
else:
return None
- bind = property(bind)
+ def _set_bind(self, bind):
+ self._bind = bind
+ bind = property(bind, _set_bind)
class Select(_SelectBaseMixin, FromClause):
"""Represents a ``SELECT`` statement.
self._bind = e
return e
return None
- bind = property(bind)
+ def _set_bind(self, bind):
+ self._bind = bind
+ bind = property(bind, _set_bind)
class _UpdateBase(ClauseElement):
"""Form the base for ``INSERT``, ``UPDATE``, and ``DELETE`` statements."""
return parameters
def bind(self):
- return self.table.bind
- bind = property(bind)
+ return self._bind or self.table.bind
+
+ def _set_bind(self, bind):
+ self._bind = bind
+ bind = property(bind, _set_bind)
class Insert(_UpdateBase):
- def __init__(self, table, values=None, inline=False, **kwargs):
+ def __init__(self, table, values=None, inline=False, bind=None, **kwargs):
+ self._bind = bind
self.table = table
self.select = None
self.inline=inline
return u
class Update(_UpdateBase):
- def __init__(self, table, whereclause, values=None, inline=False, **kwargs):
+ def __init__(self, table, whereclause, values=None, inline=False, bind=None, **kwargs):
+ self._bind = bind
self.table = table
if whereclause:
self._whereclause = _literal_as_text(whereclause)
return u
class Delete(_UpdateBase):
- def __init__(self, table, whereclause):
+ def __init__(self, table, whereclause, bind=None):
+ self._bind = bind
self.table = table
if whereclause:
self._whereclause = _literal_as_text(whereclause)