Tutorialspoint.com-SQLAlchemy - Quick Guide
Tutorialspoint.com-SQLAlchemy - Quick Guide
tutorialspoint.com/sqlalchemy/sqlalchemy_quick_guide.htm
SQLAlchemy - Introduction
SQLAlchemy is a popular SQL toolkit and Object Relational Mapper. It is written in
Python and gives full power and flexibility of SQL to an application developer. It is an
open source and cross-platform software released under MIT license.
SQLAlchemy is famous for its object-relational mapper (ORM), using which, classes can
be mapped to the database, thereby allowing the object model and database schema to
develop in a cleanly decoupled way from the beginning.
As size and performance of SQL databases start to matter, they behave less like object
collections. On the other hand, as abstraction in object collections starts to matter, they
behave less like tables and rows. SQLAlchemy aims to accommodate both of these
principles.
For this reason, it has adopted the data mapper pattern (like Hibernate) rather
than the active record pattern used by a number of other ORMs. Databases
and SQL will be viewed in a different perspective using SQLAlchemy.
Michael Bayer is the original author of SQLAlchemy. Its initial version was released in
February 2006. Latest version is numbered as 1.2.7, released as recently as in April
2018.
What is ORM?
ORM (Object Relational Mapping) is a programming technique for converting data
between incompatible type systems in object-oriented programming languages. Usually,
the type system used in an Object Oriented (OO) language like Python contains non-
scalar types. These cannot be expressed as primitive types such as integers and strings.
Hence, the OO programmer has to convert objects in scalar data to interact with
backend database. However, data types in most of the database products such as Oracle,
MySQL, etc., are primary.
In an ORM system, each class maps to a table in the underlying database. Instead of
writing tedious database interfacing code yourself, an ORM takes care of these issues
for you while you can focus on programming the logics of the system.
Using the above command, we can download the latest released version of
SQLAlchemy from python.org and install it to your system.
Firebird
Microsoft SQL Server
MySQL
Oracle
PostgreSQL
SQLite
Sybase
To check if SQLAlchemy is properly installed and to know its version, enter the
following command in the Python prompt −
2/73
The SQL Expression Language presents a system of representing relational database
structures and expressions using Python constructs. It presents a system of
representing the primitive constructs of the relational database directly without
opinion, which is in contrast to ORM that presents a high level and abstracted pattern
of usage, which itself is an example of applied usage of the Expression Language.
Engine class connects a Pool and Dialect together to provide a source of database
connectivity and behavior. An object of Engine class is instantiated using the
create_engine() function.
The create_engine() function takes the database as one argument. The database is not
needed to be defined anywhere. The standard calling form has to send the URL as the
first positional argument, usually a string that indicates database dialect and connection
arguments. Using the code given below, we can create a database.
To specifically mention DB-API to be used for connection, the URL string takes the
form as follows −
dialect[+driver]://user:password@host/dbname
3/73
For example, if you are using PyMySQL driver with MySQL, use the following
command −
mysql+pymysql://<username>:<password>@<host>/<dbname>
The echo flag is a shortcut to set up SQLAlchemy logging, which is accomplished via
Python’s standard logging module. In the subsequent chapters, we will learn all the
generated SQLs. To hide the verbose output, set echo attribute to None. Other
arguments to create_engine() function may be dialect specific.
1 connect()
2 execute()
3 begin()
4 dispose()
5 driver()
6 table_names()
7 transaction()
The SQL Expression Language constructs its expressions against table columns.
SQLAlchemy Column object represents a column in a database table which is in turn
represented by a Tableobject. Metadata contains definitions of tables and associated
objects such as index, view, triggers, etc.
Constructor of MetaData class can have bind and schema parameters which are by
default None.
Next, we define our tables all within above metadata catalog, using the Table
construct, which resembles regular SQL CREATE TABLE statement.
SQLAlchemy matches Python data to the best possible generic column data types
defined in it. Some of the generic data types are −
BigInteger
Boolean
Date
DateTime
Float
Integer
Numeric
SmallInteger
String
Text
Time
5/73
To create a students table in college database, use the following snippet −
students = Table(
'students', meta,
Column('id', Integer, primary_key = True),
Column('name', String),
Column('lastname', String),
)
The create_all() function uses the engine object to create all the defined table objects
and stores the information in metadata.
meta.create_all(engine)
Complete code is given below which will create a SQLite database college.db with a
students table in it.
students = Table(
'students', meta,
Column('id', Integer, primary_key = True),
Column('name', String),
Column('lastname', String),
)
meta.create_all(engine)
Because echo attribute of create_engine() function is set to True, the console will
display the actual SQL query for table creation as follows −
The college.db will be created in current working directory. To check if the students
table is created, you can open the database using any SQLite GUI tool such as
SQLiteStudio.
The below image shows the students table that is created in the database −
6/73
SQLAlchemy Core - SQL Expressions
In this chapter, we will briefly focus on the SQL Expressions and their functions.
SQL expressions are constructed using corresponding methods relative to target table
object. For example, the INSERT statement is created by executing insert() method as
follows −
ins = students.insert()
The result of above method is an insert object that can be verified by using str()
function. The below code inserts details like student id, name, lastname.
'INSERT INTO students (id, name, lastname) VALUES (:id, :name, :lastname)'
It is possible to insert value in a specific field by values() method to insert object. The
code for the same is given below −
The SQL echoed on Python console doesn’t show the actual value (‘Karan’ in this case).
Instead, SQLALchemy generates a bind parameter which is visible in compiled form of
the statement.
ins.compile().params
{'name': 'Karan'}
Similarly, methods like update(), delete() and select() create UPDATE, DELETE
and SELECT expressions respectively. We shall learn about them in later chapters.
7/73
SQLAlchemy Core - Executing Expression
In the previous chapter, we have learnt SQL Expressions. In this chapter, we shall look
into the execution of these expressions.
conn = engine.connect()
Following is the entire snippet that shows the execution of INSERT query using
SQLAlchemy’s core technique −
students = Table(
'students', meta,
Column('id', Integer, primary_key = True),
Column('name', String),
Column('lastname', String),
)
ins = students.insert()
ins = students.insert().values(name = 'Ravi', lastname = 'Kapoor')
conn = engine.connect()
result = conn.execute(ins)
The result can be verified by opening the database using SQLite Studio as shown in the
below screenshot −
8/73
The result variable is known as a ResultProxy object. It is analogous to the DBAPI
cursor object. We can acquire information about the primary key values which were
generated from our statement using ResultProxy.inserted_primary_key as shown
below −
result.inserted_primary_key
[1]
To issue many inserts using DBAPI’s execute many() method, we can send in a list of
dictionaries each containing a distinct set of parameters to be inserted.
conn.execute(students.insert(), [
{'name':'Rajiv', 'lastname' : 'Khanna'},
{'name':'Komal','lastname' : 'Bhandari'},
{'name':'Abdul','lastname' : 'Sattar'},
{'name':'Priya','lastname' : 'Rajhans'},
])
This is reflected in the data view of the table as shown in the following figure −
9/73
SQLAlchemy Core - Selecting Rows
In this chapter, we will discuss about the concept of selecting rows in the table object.
s = students.select()
The select object translates to SELECT query by str(s) function as shown below −
We can use this select object as a parameter to execute() method of connection object as
shown in the code below −
result = conn.execute(s)
When the above statement is executed, Python shell echoes following equivalent SQL
expression −
The resultant variable is an equivalent of cursor in DBAPI. We can now fetch records
using fetchone() method.
row = result.fetchone()
All selected rows in the table can be printed by a for loop as given below −
10/73
The complete code to print all rows from students table is shown below −
students = Table(
'students', meta,
Column('id', Integer, primary_key = True),
Column('name', String),
Column('lastname', String),
)
s = students.select()
conn = engine.connect()
result = conn.execute(s)
The WHERE clause of SELECT query can be applied by using Select.where(). For
example, if we want to display rows with id >2
s = students.select().where(students.c.id>2)
result = conn.execute(s)
Here c attribute is an alias for column. Following output will be displayed on the
shell −
Here, we have to note that select object can also be obtained by select() function in
sqlalchemy.sql module. The select() function requires the table object as argument.
The text()function requires Bound parameters in the named colon format. They are
consistent regardless of database backend. To send values in for the parameters, we pass
them into the execute() method as additional arguments.
The values of x = ’A’ and y = ’L’ are passed as parameters. Result is a list of rows with
names between ‘A’ and ‘L’ −
12/73
stmt = text("SELECT * FROM students WHERE students.name BETWEEN :x AND :y")
stmt = stmt.bindparams(
bindparam("x", type_= String),
bindparam("y", type_= String)
)
The text() function also be produces fragments of SQL within a select() object that
accepts text() objects as an arguments. The “geometry” of the statement is provided by
select() construct , and the textual content by text() construct. We can build a statement
without the need to refer to any pre-established Table metadata.
You can also use and_() function to combine multiple conditions in WHERE clause
created with the help of text() function.
Above code fetches rows with names between “A” and “L” with id greater than 2. The
output of the code is given below −
In case of a table, this allows the same table to be named in the FROM clause multiple
times. It provides a parent name for the columns represented by the statement, allowing
them to be referenced relative to this name.
In SQLAlchemy, any Table, select() construct, or other selectable object can be turned
into an alias using the From Clause.alias() method, which produces an Alias
construct. The alias() function in sqlalchemy.sql module represents an alias, as typically
13/73
applied to any table or sub-select within a SQL statement using the AS keyword.
This alias can now be used in select() construct to refer to students table −
s = select([st]).where(st.c.id>2)
We can now execute this SQL query with the execute() method of connection object. The
complete code is as follows −
table.update().where(conditions).values(SET expressions)
The values() method on the resultant update object is used to specify the SET
conditions of the UPDATE. If left as None, the SET conditions are determined from
those parameters passed to the statement during the execution and/or compilation of
the statement.
The where clause is an Optional expression describing the WHERE condition of the
UPDATE statement.
Following code snippet changes value of ‘lastname’ column from ‘Khanna’ to ‘Kapoor’ in
students table −
students = Table(
'students',
meta,
Column('id', Integer, primary_key = True),
Column('name', String),
Column('lastname', String),
)
conn = engine.connect()
stmt=students.update().where(students.c.lastname=='Khanna').values(lastname='Kapoor')
conn.execute(stmt)
s = students.select()
conn.execute(s).fetchall()
The above code displays following output with second row showing effect of update
operation as in the screenshot given −
[
(1, 'Ravi', 'Kapoor'),
(2, 'Rajiv', 'Kapoor'),
(3, 'Komal', 'Bhandari'),
(4, 'Abdul', 'Sattar'),
(5, 'Priya', 'Rajhans')
]
15/73
Note that similar functionality can also be achieved by using update() function in
sqlalchemy.sql.expression module as shown below −
The delete operation can be achieved by running delete() method on target table object
as given in the following statement −
stmt = students.delete()
In case of students table, the above line of code constructs a SQL expression as
following −
However, this will delete all rows in students table. Usually DELETE query is associated
with a logical expression specified by WHERE clause. The following statement shows
where parameter −
The resultant SQL expression will have a bound parameter which will be substituted at
runtime when the statement is executed.
Following code example will delete those rows from students table having lastname as
‘Khanna’ −
16/73
from sqlalchemy.sql.expression import update
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String
engine = create_engine('sqlite:///college.db', echo = True)
meta = MetaData()
students = Table(
'students', meta,
Column('id', Integer, primary_key = True),
Column('name', String),
Column('lastname', String),
)
conn = engine.connect()
stmt = students.delete().where(students.c.lastname == 'Khanna')
conn.execute(stmt)
s = students.select()
conn.execute(s).fetchall()
To verify the result, refresh the data view of students table in SQLiteStudio.
For this purpose, two tables are created in our SQLite database (college.db). The
students table has the same structure as given in the previous section; whereas the
addresses table has st_id column which is mapped to id column in students table
using foreign key constraint.
17/73
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, ForeignKey
engine = create_engine('sqlite:///college.db', echo=True)
meta = MetaData()
students = Table(
'students', meta,
Column('id', Integer, primary_key = True),
Column('name', String),
Column('lastname', String),
)
addresses = Table(
'addresses', meta,
Column('id', Integer, primary_key = True),
Column('st_id', Integer, ForeignKey('students.id')),
Column('postal_add', String),
Column('email_add', String))
meta.create_all(engine)
Above code will translate to CREATE TABLE queries for students and addresses table
as below −
18/73
These tables are populated with data by executing insert() method of table objects.
To insert 5 rows in students table, you can use the code given below −
19/73
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String
engine = create_engine('sqlite:///college.db', echo = True)
meta = MetaData()
conn = engine.connect()
students = Table(
'students', meta,
Column('id', Integer, primary_key = True),
Column('name', String),
Column('lastname', String),
)
conn.execute(students.insert(), [
{'name':'Ravi', 'lastname':'Kapoor'},
{'name':'Rajiv', 'lastname' : 'Khanna'},
{'name':'Komal','lastname' : 'Bhandari'},
{'name':'Abdul','lastname' : 'Sattar'},
{'name':'Priya','lastname' : 'Rajhans'},
])
Rows are added in addresses table with the help of the following code −
addresses = Table(
'addresses', meta,
Column('id', Integer, primary_key = True),
Column('st_id', Integer),
Column('postal_add', String),
Column('email_add', String)
)
conn.execute(addresses.insert(), [
{'st_id':1, 'postal_add':'Shivajinagar Pune', 'email_add':'ravi@gmail.com'},
{'st_id':1, 'postal_add':'ChurchGate Mumbai', 'email_add':'kapoor@gmail.com'},
{'st_id':3, 'postal_add':'Jubilee Hills Hyderabad', 'email_add':'komal@gmail.com'},
{'st_id':5, 'postal_add':'MG Road Bangaluru', 'email_add':'as@yahoo.com'},
{'st_id':2, 'postal_add':'Cannought Place new Delhi', 'email_add':'admin@khanna.com'},
])
Note that the st_id column in addresses table refers to id column in students table. We
can now use this relation to fetch data from both the tables. We want to fetch name
and lastname from students table corresponding to st_id in the addresses table.
20/73
The select objects will effectively translate into following SQL expression joining two
tables on common relation −
SELECT students.id,
students.name,
students.lastname,
addresses.id,
addresses.st_id,
addresses.postal_add,
addresses.email_add
FROM students, addresses
WHERE students.id = addresses.st_id
This will produce output extracting corresponding data from both tables as follows −
Using SQLAlchemy’s table object, more than one table can be specified in WHERE
clause of update() method. The PostgreSQL and Microsoft SQL Server support
UPDATE statements that refer to multiple tables. This implements “UPDATE FROM”
syntax, which updates one table at a time. However, additional tables can be referenced
in an additional “FROM” clause in the WHERE clause directly. The following lines of
codes explain the concept of multiple table updates clearly.
stmt = students.update().\
values({
students.c.name:'xyz',
addresses.c.email_add:'abc@xyz.com'
}).\
where(students.c.id == addresses.c.id)
UPDATE students
SET email_add = :addresses_email_add, name = :name
FROM addresses
WHERE students.id = addresses.id
As far as MySQL dialect is concerned, multiple tables can be embedded into a single
UPDATE statement separated by a comma as given below −
21/73
stmt = students.update().\
values(name = 'xyz').\
where(students.c.id == addresses.c.id)
SQLite dialect however doesn’t support multiple-table criteria within UPDATE and
shows following error −
NotImplementedError: This backend does not support multiple-table criteria within UPDATE
Parameter-Ordered Updates
The UPDATE query of raw SQL has SET clause. It is rendered by the update() construct
using the column ordering given in the originating Table object. Therefore, a particular
UPDATE statement with particular columns will be rendered the same each time. Since
the parameters themselves are passed to the Update.values() method as Python
dictionary keys, there is no other fixed ordering available.
In some cases, the order of parameters rendered in the SET clause are significant. In
MySQL, providing updates to column values is based on that of other column values.
SET clause in MySQL is evaluated on a per-value basis and not on per-row basis. For
this purpose, the preserve_parameter_order is used. Python list of 2-tuples is
given as argument to the Update.values() method −
The List object is similar to dictionary except that it is ordered. This ensures that the “y”
column’s SET clause will render first, then the “x” column’s SET clause.
22/73
More than one table can be referred in WHERE clause of DELETE statement in many
DBMS dialects. For PG and MySQL, “DELETE USING” syntax is used; and for SQL
Server, using “DELETE FROM” expression refers to more than one table. The
SQLAlchemy delete() construct supports both of these modes implicitly, by specifying
multiple tables in the WHERE clause as follows −
stmt = users.delete().\
where(users.c.id == addresses.c.id).\
where(addresses.c.email_address.startswith('xyz%'))
conn.execute(stmt)
On a PostgreSQL backend, the resulting SQL from the above statement would render as
−
If this method is used with a database that doesn’t support this behaviour, the compiler
will raise NotImplementedError.
Effect of joining is achieved by just placing two tables in either the columns clause or
the where clause of the select() construct. Now we use the join() and outerjoin()
methods.
The join() method returns a join object from one table object to another.
The functions of the parameters mentioned in the above code are as follows −
right − the right side of the join; this is any Table object
full − if True, renders a FULL OUTER JOIN, instead of LEFT OUTER JOIN
For example, following use of join() method will automatically result in join based on
the foreign key.
>>> print(students.join(addresses))
stmt = select([students]).select_from(j)
If this statement is executed using the connection representing engine, data belonging to
selected columns will be displayed. The complete code is as follows −
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, ForeignKey
engine = create_engine('sqlite:///college.db', echo = True)
meta = MetaData()
conn = engine.connect()
students = Table(
'students', meta,
Column('id', Integer, primary_key = True),
Column('name', String),
Column('lastname', String),
)
addresses = Table(
'addresses', meta,
Column('id', Integer, primary_key = True),
Column('st_id', Integer,ForeignKey('students.id')),
Column('postal_add', String),
Column('email_add', String)
)
[
(1, 'Ravi', 'Kapoor'),
(1, 'Ravi', 'Kapoor'),
(3, 'Komal', 'Bhandari'),
(5, 'Priya', 'Rajhans'),
(2, 'Rajiv', 'Khanna')
]
24/73
SQLAlchemy Core - Using Conjunctions
Conjunctions are functions in SQLAlchemy module that implement relational operators
used in WHERE clause of SQL expressions. The operators AND, OR, NOT, etc., are
used to form a compound expression combining two individual logical expressions. A
simple example of using AND in SELECT statement is as follows −
SQLAlchemy functions and_(), or_() and not_() respectively implement AND, OR and
NOT operators.
and_() function
It produces a conjunction of expressions joined by AND. An example is given below for
better understanding −
print(
and_(
students.c.name == 'Ravi',
students.c.id <3
)
)
This translates to −
To use and_() in a select() construct on a students table, use the following line of code −
SELECT students.id,
students.name,
students.lastname
FROM students
WHERE students.name = :name_1 AND students.id < :id_1
The complete code that displays output of the above SELECT query is as follows −
25/73
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, ForeignKey,
select
engine = create_engine('sqlite:///college.db', echo = True)
meta = MetaData()
conn = engine.connect()
students = Table(
'students', meta,
Column('id', Integer, primary_key = True),
Column('name', String),
Column('lastname', String),
)
Following row will be selected assuming that students table is populated with data used
in previous example −
or_() function
It produces conjunction of expressions joined by OR. We shall replace the stmt object in
the above example with the following one using or_()
SELECT students.id,
students.name,
students.lastname
FROM students
WHERE students.name = :name_1
OR students.id < :id_1
Once you make the substitution and run the above code, the result will be two rows
falling in the OR condition −
asc() function
It produces an ascending ORDER BY clause. The function takes the column to apply the
function as a parameter.
26/73
The statement implements following SQL expression −
SELECT students.id,
students.name,
students.lastname
FROM students
ORDER BY students.name ASC
Following code lists out all records in students table in ascending order of name column
−
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, ForeignKey,
select
engine = create_engine('sqlite:///college.db', echo = True)
meta = MetaData()
conn = engine.connect()
students = Table(
'students', meta,
Column('id', Integer, primary_key = True),
Column('name', String),
Column('lastname', String),
)
desc() function
Similarly desc() function produces descending ORDER BY clause as follows −
SELECT students.id,
students.name,
students.lastname
FROM students
ORDER BY students.lastname DESC
27/73
And the output for the above lines of code is −
between() function
It produces a BETWEEN predicate clause. This is generally used to validate if value of a
certain column falls between a range. For example, following code selects rows for
which id column is between 2 and 4 −
SELECT students.id,
students.name,
students.lastname
FROM students
WHERE students.id
BETWEEN :id_1 AND :id_2
Standard SQL has recommended many functions which are implemented by most
dialects. They return a single value based on the arguments passed to it. Some SQL
functions take columns as arguments whereas some are generic. Thefunc keyword in
SQLAlchemy API is used to generate these functions.
In SQL, now() is a generic function. Following statements renders the now() function
using func −
From the above code, count of number of rows in students table will be fetched.
Some built-in SQL functions are demonstrated using Employee table with following
data −
ID Name Marks
1 Kamal 56
2 Fernandez 85
3 Sunil 62
4 Bhaskar 76
The max() function is implemented by following usage of func from SQLAlchemy which
will result in 85, the total maximum marks obtained −
Similarly, min() function that will return 56, minimum marks, will be rendered by
following code −
So, the AVG() function can also be implemented by using the below code −
result = conn.execute(select([func.max(students.c.lastname).label('Name')]))
print (result.fetchone())
Set operations such as UNION and INTERSECT are supported by standard SQL and
most of its dialect. SQLAlchemy implements them with the help of following functions
−
union()
While combining results of two or more SELECT statements, UNION eliminates
duplicates from the resultset. The number of columns and datatype must be same in
both the tables.
The union() function returns a CompoundSelect object from multiple tables. Following
example demonstrates its use −
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, union
engine = create_engine('sqlite:///college.db', echo = True)
meta = MetaData()
conn = engine.connect()
addresses = Table(
'addresses', meta,
Column('id', Integer, primary_key = True),
Column('st_id', Integer),
Column('postal_add', String),
Column('email_add', String)
)
u = union(addresses.select().where(addresses.c.email_add.like('%@gmail.com
addresses.select().where(addresses.c.email_add.like('%@yahoo.com'))))
result = conn.execute(u)
result.fetchall()
SELECT addresses.id,
addresses.st_id,
addresses.postal_add,
addresses.email_add
FROM addresses
WHERE addresses.email_add LIKE ? UNION SELECT addresses.id,
addresses.st_id,
addresses.postal_add,
addresses.email_add
FROM addresses
WHERE addresses.email_add LIKE ?
From our addresses table, following rows represent the union operation −
30/73
[
(1, 1, 'Shivajinagar Pune', 'ravi@gmail.com'),
(2, 1, 'ChurchGate Mumbai', 'kapoor@gmail.com'),
(3, 3, 'Jubilee Hills Hyderabad', 'komal@gmail.com'),
(4, 5, 'MG Road Bangaluru', 'as@yahoo.com')
]
union_all()
UNION ALL operation cannot remove the duplicates and cannot sort the data in the
resultset. For example, in above query, UNION is replaced by UNION ALL to see the
effect.
u = union_all(addresses.select().where(addresses.c.email_add.like('%@gmail.com')),
addresses.select().where(addresses.c.email_add.like('%@yahoo.com')))
SELECT addresses.id,
addresses.st_id,
addresses.postal_add,
addresses.email_add
FROM addresses
WHERE addresses.email_add LIKE ? UNION ALL SELECT addresses.id,
addresses.st_id,
addresses.postal_add,
addresses.email_add
FROM addresses
WHERE addresses.email_add LIKE ?
except_()
The SQL EXCEPT clause/operator is used to combine two SELECT statements and
return rows from the first SELECT statement that are not returned by the second
SELECT statement. The except_() function generates a SELECT expression with
EXCEPT clause.
In the following example, the except_() function returns only those records from
addresses table that have ‘gmail.com’ in email_add field but excludes those which have
‘Pune’ as part of postal_add field.
u = except_(addresses.select().where(addresses.c.email_add.like('%@gmail.com')),
addresses.select().where(addresses.c.postal_add.like('%Pune')))
31/73
SELECT addresses.id,
addresses.st_id,
addresses.postal_add,
addresses.email_add
FROM addresses
WHERE addresses.email_add LIKE ? EXCEPT SELECT addresses.id,
addresses.st_id,
addresses.postal_add,
addresses.email_add
FROM addresses
WHERE addresses.postal_add LIKE ?
Assuming that addresses table contains data used in earlier examples, it will display
following output −
intersect()
Using INTERSECT operator, SQL displays common rows from both the SELECT
statements. The intersect() function implements this behaviour.
u = intersect(addresses.select().where(addresses.c.email_add.like('%@gmail.com')),
addresses.select().where(addresses.c.postal_add.like('%Pune')))
SELECT addresses.id,
addresses.st_id,
addresses.postal_add,
addresses.email_add
FROM addresses
WHERE addresses.email_add LIKE ? INTERSECT SELECT addresses.id,
addresses.st_id,
addresses.postal_add,
addresses.email_add
FROM addresses
WHERE addresses.postal_add LIKE ?
The two bound parameters ‘%gmail.com’ and ‘%Pune’ generate a single row from
original data in addresses table as shown below −
The ORM is constructed on top of the SQL Expression Language. It is a high level and
abstracted pattern of usage. In fact, ORM is an applied usage of the Expression
Language.
Declare Mapping
First of all, create_engine() function is called to set up an engine object which is
subsequently used to perform SQL operations. The function has two arguments, one is
the name of database and other is an echo parameter when set to True will generate the
activity log. If it doesn’t exist, the database will be created. In the following example, a
SQLite database is created.
The Engine establishes a real DBAPI connection to the database when a method like
Engine.execute() or Engine.connect() is called. It is then used to emit the SQLORM
which does not use the Engine directly; instead, it is used behind the scenes by the
ORM.
In case of ORM, the configurational process starts by describing the database tables and
then by defining classes which will be mapped to those tables. In SQLAlchemy, these
two tasks are performed together. This is done by using Declarative system; the classes
created include directives to describe the actual database table they are mapped to.
A base class stores a catlog of classes and mapped tables in the Declarative system. This
is called as the declarative base class. There will be usually just one instance of this base
in a commonly imported module. The declarative_base() function is used to create base
class. This function is defined in sqlalchemy.ext.declarative module.
Once base classis declared, any number of mapped classes can be defined in terms of it.
Following code defines a Customer’s class. It contains the table to be mapped to, and
names and datatypes of columns in it.
33/73
class Customers(Base):
__tablename__ = 'customers'
This mapped class like a normal Python class has attributes and methods as per the
requirement.
Each Table object is a member of larger collection known as MetaData and this object is
available using the .metadata attribute of declarative base class. The
MetaData.create_all() method is, passing in our Engine as a source of database
connectivity. For all tables that haven’t been created yet, it issues CREATE TABLE
statements to the database.
Base.metadata.create_all(engine)
The complete script to create a database and a table, and to map Python class is given
below −
class Customers(Base):
__tablename__ = 'customers'
id = Column(Integer, primary_key=True)
name = Column(String)
address = Column(String)
email = Column(String)
Base.metadata.create_all(engine)
When executed, Python console will echo following SQL expression being executed −
34/73
CREATE TABLE customers (
id INTEGER NOT NULL,
name VARCHAR,
address VARCHAR,
email VARCHAR,
PRIMARY KEY (id)
)
If we open the Sales.db using SQLiteStudio graphic tool, it shows customers table inside
it with above mentioned structure.
The session object is then set up using its default constructor as follows −
session = Session()
Some of the frequently required methods of session class are listed below −
1 begin()
35/73
2 add()
places an object in the session. Its state is persisted in the database on next
flush operation
3 add_all()
4 commit()
5 delete()
6 execute()
7 expire()
8 flush()
9 invalidate()
10 rollback()
11 close()
Closes current session by clearing all items and ending any transaction in
progress
36/73
In the previous chapters of SQLAlchemy ORM, we have learnt how to declare mapping
and create sessions. In this chapter, we will learn how to add objects to the table.
We have declared Customer class that has been mapped to customers table. We have to
declare an object of this class and persistently add it to the table by add() method of
session object.
Note that this transaction is pending until the same is flushed using commit() method.
session.commit()
class Customers(Base):
__tablename__ = 'customers'
id = Column(Integer, primary_key=True)
name = Column(String)
address = Column(String)
email = Column(String)
session.add(c1)
session.commit()
To add multiple records, we can use add_all() method of the session class.
session.add_all([
Customers(name = 'Komal Pande', address = 'Koti, Hyderabad', email =
'komal@gmail.com'),
Customers(name = 'Rajender Nath', address = 'Sector 40, Gurgaon', email =
'nath@gmail.com'),
Customers(name = 'S.M.Krishna', address = 'Budhwar Peth, Pune', email =
'smk@gmail.com')]
)
session.commit()
37/73
Table view of SQLiteStudio shows that the records are persistently added in customers
table. The following image shows the result −
Query objects are initially generated using the query() method of the Session as follows
−
q = session.query(mapped class)
q = Query(mappedClass, session)
The query object has all() method which returns a resultset in the form of list of objects.
If we execute it on our customers table −
result = session.query(Customers).all()
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
38/73
The result object can be traversed using For loop as below to obtain all records in
underlying customers table. Here is the complete code to display all records in
Customers table −
class Customers(Base):
__tablename__ = 'customers'
id = Column(Integer, primary_key = True)
name = Column(String)
address = Column(String)
email = Column(String)
1 add_columns()
2 add_entity()
3 count()
39/73
4 delete()
It performs a bulk delete query. Deletes rows matched by this query from
the database.
5 distinct()
It applies a DISTINCT clause to the query and return the newly resulting
Query.
6 filter()
It applies the given filtering criterion to a copy of this Query, using SQL
expressions.
7 first()
It returns the first result of this Query or None if the result doesn’t contain
any row.
8 get()
9 group_by()
It applies one or more GROUP BY criterion to the query and return the
newly resulting Query
10 join()
It creates a SQL JOIN against this Query object’s criterion and apply
generatively, returning the newly resulting Query.
11 one()
12 order_by()
It applies one or more ORDER BY criterion to the query and returns the
newly resulting Query.
40/73
13 update()
It performs a bulk update query and updates rows matched by this query in
the database.
To modify data of a certain attribute of any object, we have to assign new value to it and
commit the changes to make the change persistent.
Let us fetch an object from the table whose primary key identifier, in our Customers
table with ID=2. We can use get() method of session as follows −
x = session.query(Customers).get(2)
We can display contents of the selected object with the below given code −
Now we need to update the Address field by assigning new value as given below −
The change will be persistently reflected in the database. Now we fetch object
corresponding to first row in the table by using first() method as follows −
x = session.query(Customers).first()
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
LIMIT ? OFFSET ?
The bound parameters will be LIMIT = 1 and OFFSET = 0 respectively which means
first row will be selected.
Now, the output for the above code displaying the first row is as follows −
41/73
Name: Ravi Kumar Address: Station Road Nanded Email: ravi@gmail.com
Now change name attribute and display the contents using the below code −
Even though the change is displayed, it is not committed. You can retain the earlier
persistent position by using rollback() method with the code below.
session.rollback()
For bulk updates, we shall use update() method of the Query object. Let us try and give
a prefix, ‘Mr.’ to name in each row (except ID = 2). The corresponding update()
statement is as follows −
session.query(Customers).filter(Customers.id! = 2).
update({Customers.name:"Mr."+Customers.name}, synchronize_session = False)
A dictionary of key-values with key being the attribute to be updated, and value
being the new contents of attribute.
Three out of 4 rows in the table will have name prefixed with ‘Mr.’ However, the
changes are not committed and hence will not be reflected in the table view of
SQLiteStudio. It will be refreshed only when we commit the session.
session.query(class).filter(criteria)
42/73
In the following example, resultset obtained by SELECT query on Customers table is
filtered by a condition, (ID>2) −
result = session.query(Customers).filter(Customers.id>2)
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.id > ?
Since the bound parameter (?) is given as 2, only those rows with ID column>2 will be
displayed. The complete code is given below −
class Customers(Base):
__tablename__ = 'customers'
address = Column(String)
email = Column(String)
ID: 3 Name: Rajender Nath Address: Sector 40, Gurgaon Email: nath@gmail.com
ID: 4 Name: S.M.Krishna Address: Budhwar Peth, Pune Email: smk@gmail.com
Equals
The usual operator used is == and it applies the criteria to check equality.
43/73
result = session.query(Customers).filter(Customers.id == 2)
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.id = ?
ID: 2 Name: Komal Pande Address: Banjara Hills Secunderabad Email: komal@gmail.com
Not Equals
The operator used for not equals is != and it provides not equals criteria.
result = session.query(Customers).filter(Customers.id! = 2)
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.id != ?
ID: 1 Name: Ravi Kumar Address: Station Road Nanded Email: ravi@gmail.com
ID: 3 Name: Rajender Nath Address: Sector 40, Gurgaon Email: nath@gmail.com
ID: 4 Name: S.M.Krishna Address: Budhwar Peth, Pune Email: smk@gmail.com
Like
like() method itself produces the LIKE criteria for WHERE clause in the SELECT
expression.
result = session.query(Customers).filter(Customers.name.like('Ra%'))
for row in result:
print ("ID:", row.id, "Name: ",row.name, "Address:",row.address, "Email:",row.email)
44/73
Above SQLAlchemy code is equivalent to following SQL expression −
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.name LIKE ?
ID: 1 Name: Ravi Kumar Address: Station Road Nanded Email: ravi@gmail.com
ID: 3 Name: Rajender Nath Address: Sector 40, Gurgaon Email: nath@gmail.com
IN
This operator checks whether the column value belongs to a collection of items in a list.
It is provided by in_() method.
result = session.query(Customers).filter(Customers.id.in_([1,3]))
for row in result:
print ("ID:", row.id, "Name: ",row.name, "Address:",row.address, "Email:",row.email)
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.id IN (?, ?)
ID: 1 Name: Ravi Kumar Address: Station Road Nanded Email: ravi@gmail.com
ID: 3 Name: Rajender Nath Address: Sector 40, Gurgaon Email: nath@gmail.com
AND
This conjunction is generated by either putting multiple commas separated
criteria in the filter or using and_() method as given below −
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.id > ? AND customers.name LIKE ?
ID: 3 Name: Rajender Nath Address: Sector 40, Gurgaon Email: nath@gmail.com
OR
This conjunction is implemented by or_() method.
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.id > ? OR customers.name LIKE ?
ID: 1 Name: Ravi Kumar Address: Station Road Nanded Email: ravi@gmail.com
ID: 3 Name: Rajender Nath Address: Sector 40, Gurgaon Email: nath@gmail.com
ID: 4 Name: S.M.Krishna Address: Budhwar Peth, Pune Email: smk@gmail.com
all()
It returns a list. Given below is the line of code for all() function.
session.query(Customers).all()
46/73
Python console displays following SQL expression emitted −
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
first()
It applies a limit of one and returns the first result as a scalar.
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
LIMIT ? OFFSET ?
one()
This command fully fetches all rows, and if there is not exactly one object identity or
composite row present in the result, it raises an error.
session.query(Customers).one()
The one() method is useful for systems that expect to handle “no items found” versus
“multiple items found” differently.
scalar()
It invokes the one() method, and upon success returns the first column of the row as
follows −
session.query(Customers).filter(Customers.id == 3).scalar()
47/73
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.id = ?
Literal strings can be used flexibly with Query object by specifying their use with the
text() construct. Most applicable methods accept it. For example, filter() and
order_by().
In the example given below, the filter() method translates the string “id<3” to the
WHERE id<3
The raw SQL expression generated shows conversion of filter to WHERE clause with the
code illustrated below −
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE id<3
From our sample data in Customers table, two rows will be selected and name column
will be printed as follows −
Ravi Kumar
Komal Pande
To specify bind parameters with string-based SQL, use a colon,and to specify the values,
use the params() method.
48/73
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE id = ?
The result of above code will be a basic SELECT statement as given below −
The text() construct allows us to link its textual SQL to Core or ORM-mapped column
expressions positionally. We can achieve this by passing column expressions as
positional arguments to the TextClause.columns() method.
The id and name columns of all rows will be selected even though the SQLite engine
executes following expression generated by above code shows all columns in text()
method −
Using declarative, we define this table along with its mapped class, Invoices as given
below −
49/73
from sqlalchemy import create_engine, ForeignKey, Column, Integer, String
engine = create_engine('sqlite:///sales.db', echo = True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from sqlalchemy.orm import relationship
class Customer(Base):
__tablename__ = 'customers'
class Invoice(Base):
__tablename__ = 'invoices'
We can check that new table is created in sales.db with the help of SQLiteStudio tool.
50/73
Invoices class applies ForeignKey construct on custid attribute. This directive indicates
that values in this column should be constrained to be values present in id column in
customers table. This is a core feature of relational databases, and is the “glue” that
transforms unconnected collection of tables to have rich overlapping relationships.
A second directive, known as relationship(), tells the ORM that the Invoice class should
be linked to the Customer class using the attribute Invoice.customer. The relationship()
uses the foreign key relationships between the two tables to determine the nature of this
linkage, determining that it is many to one.
An additional relationship() directive is placed on the Customer mapped class under the
attribute Customer.invoices. The parameter relationship.back_populates is assigned to
refer to the complementary attribute names, so that each relationship() can make
intelligent decision about the same relationship as expressed in reverse. On one side,
Invoices.customer refers to Invoices instance, and on the other side, Customer.invoices
refers to a list of Customers instances.
One To Many
A One to Many relationship refers to parent with the help of a foreign key on the child
table. relationship() is then specified on the parent, as referencing a collection of items
represented by the child. The relationship.back_populates parameter is used to
51/73
establish a bidirectional relationship in one-to-many, where the “reverse” side is a many
to one.
Many To One
On the other hand, Many to One relationship places a foreign key in the parent table to
refer to the child. relationship() is declared on the parent, where a new scalar-holding
attribute will be created. Here again the relationship.back_populates parameter is used
for Bidirectionalbehaviour.
One To One
One To One relationship is essentially a bidirectional relationship in nature. The 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 type of relation, set
uselist parameter to false.
Many To Many
Many to Many relationship is established by adding an association table related to two
classes by defining attributes with their foreign keys. It is indicated by the secondary
argument to relationship(). Usually, the Table uses the MetaData object associated with
the declarative base class, so that the ForeignKey directives can locate the remote tables
with which to link. The relationship.back_populates parameter for each relationship()
establishes a bidirectional relationship. Both sides of the relationship contain a
collection.
Now when we create a Customer object, a blank invoice collection will be present in the
form of Python List.
The invoices attribute of c1.invoices will be an empty list. We can assign items in the list
as −
Let us commit this object to the database using Session object as follows −
52/73
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind = engine)
session = Session()
session.add(c1)
session.commit()
This will automatically generate INSERT queries for customers and invoices tables −
Let us now look at contents of customers table and invoices table in the table view of
SQLiteStudio −
53/73
You can construct Customer object by providing mapped attribute of invoices in the
constructor itself by using the below command −
c2 = [
Customer(
name = "Govind Pant",
address = "Gulmandi Aurangabad",
email = "gpant@gmail.com",
invoices = [Invoice(invno = 3, amount = 10000),
Invoice(invno = 4, amount = 5000)]
)
]
54/73
rows = [
Customer(
name = "Govind Kala",
address = "Gulmandi Aurangabad",
email = "kala@gmail.com",
invoices = [Invoice(invno = 7, amount = 12000), Invoice(invno = 8, amount = 18500)]),
Customer(
name = "Abdul Rahman",
address = "Rohtak",
email = "abdulr@gmail.com",
invoices = [Invoice(invno = 9, amount = 15000),
Invoice(invno = 11, amount = 6000)
])
]
session.add_all(rows)
session.commit()
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email, invoices.id
AS invoices_id, invoices.custid
AS invoices_custid, invoices.invno
AS invoices_invno, invoices.amount
AS invoices_amount
FROM customers, invoices
WHERE customers.id = invoices.custid
55/73
ID: 2 Name: Gopal Krishna Invoice No: 10 Amount: 15000
ID: 2 Name: Gopal Krishna Invoice No: 14 Amount: 3850
ID: 3 Name: Govind Pant Invoice No: 3 Amount: 10000
ID: 3 Name: Govind Pant Invoice No: 4 Amount: 5000
ID: 4 Name: Govind Kala Invoice No: 7 Amount: 12000
ID: 4 Name: Govind Kala Invoice No: 8 Amount: 8500
ID: 5 Name: Abdul Rahman Invoice No: 9 Amount: 15000
ID: 5 Name: Abdul Rahman Invoice No: 11 Amount: 6000
The actual SQL JOIN syntax is easily achieved using the Query.join() method as follows
−
session.query(Customer).join(Invoice).filter(Invoice.amount == 8500).all()
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers JOIN invoices ON customers.id = invoices.custid
WHERE invoices.amount = ?
Query.join() knows how to join between these tables because there’s only one foreign
key between them. If there were no foreign keys, or more foreign keys, Query.join()
works better when one of the following forms are used −
query.outerjoin(Customer.invoices)
56/73
The subquery() method produces a SQL expression representing SELECT statement
embedded within an alias.
stmt = session.query(
Invoice.custid, func.count('*').label('invoice_count')
).group_by(Invoice.custid).subquery()
Once we have our statement, it behaves like a Table construct. The columns on the
statement are accessible through an attribute called c as shown in the below code −
__eq__()
The above operator is a many-to-one “equals” comparison. The line of code for this
operator is as shown below −
s = session.query(Customer).filter(Invoice.invno.__eq__(12))
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers, invoices
WHERE invoices.invno = ?
__ne__()
57/73
This operator is a many-to-one “not equals” comparison. The line of code for this
operator is as shown below −
s = session.query(Customer).filter(Invoice.custid.__ne__(2))
The equivalent SQL query for the above line of code is given below −
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers, invoices
WHERE invoices.custid != ?
contains()
This operator is used for one-to-many collections and given below is the code for
contains() −
s = session.query(Invoice).filter(Invoice.invno.contains([3,4,5]))
SELECT invoices.id
AS invoices_id, invoices.custid
AS invoices_custid, invoices.invno
AS invoices_invno, invoices.amount
AS invoices_amount
FROM invoices
WHERE (invoices.invno LIKE '%' + ? || '%')
any()
any() operator is used for collections as shown below −
s = session.query(Customer).filter(Customer.invoices.any(Invoice.invno==11))
The equivalent SQL query for the above line of code is shown below −
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE EXISTS (
SELECT 1
FROM invoices
WHERE customers.id = invoices.custid
AND invoices.invno = ?)
58/73
has()
This operator is used for scalar references as follows −
SELECT invoices.id
AS invoices_id, invoices.custid
AS invoices_custid, invoices.invno
AS invoices_invno, invoices.amount
AS invoices_amount
FROM invoices
WHERE EXISTS (
SELECT 1
FROM customers
WHERE customers.id = invoices.custid
AND customers.name = ?)
Subquery Load
We want that Customer.invoices should load eagerly. The orm.subqueryload() option
gives a second SELECT statement that fully loads the collections associated with the
results just loaded. The name “subquery” causes the SELECT statement to be
constructed directly via the Query re-used and embedded as a subquery into a SELECT
against the related table.
59/73
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.name = ?
('Govind Pant',)
SELECT invoices.id
AS invoices_id, invoices.custid
AS invoices_custid, invoices.invno
AS invoices_invno, invoices.amount
AS invoices_amount, anon_1.customers_id
AS anon_1_customers_id
FROM (
SELECT customers.id
AS customers_id
FROM customers
WHERE customers.name = ?)
AS anon_1
JOIN invoices
ON anon_1.customers_id = invoices.custid
ORDER BY anon_1.customers_id, invoices.id 2018-06-25 18:24:47,479
INFO sqlalchemy.engine.base.Engine ('Govind Pant',)
To access the data from two tables, we can use the below program −
for x in c1.invoices:
print ("Invoice no : {}, Amount : {}".format(x.invno, x.amount))
Joined Load
The other function is called orm.joinedload(). This emits a LEFT OUTER JOIN. Lead
object as well as the related object or collection is loaded in one step.
60/73
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email, invoices_1.id
AS invoices_1_id, invoices_1.custid
AS invoices_1_custid, invoices_1.invno
AS invoices_1_invno, invoices_1.amount
AS invoices_1_amount
FROM customers
LEFT OUTER JOIN invoices
AS invoices_1
ON customers.id = invoices_1.custid
The OUTER JOIN resulted in two rows, but it gives one instance of Customer back. This
is because Query applies a “uniquing” strategy, based on object identity, to the returned
entities. Joined eager loading can be applied without affecting the query results.
In our sales.db database, Customer and Invoice classes are mapped to customer and
invoice table with one to many type of relationship. We will try to delete Customer
object and see the result.
As a quick reference, below are the definitions of Customer and Invoice classes −
61/73
from sqlalchemy import create_engine, ForeignKey, Column, Integer, String
engine = create_engine('sqlite:///sales.db', echo = True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from sqlalchemy.orm import relationship
class Customer(Base):
__tablename__ = 'customers'
class Invoice(Base):
__tablename__ = 'invoices'
We setup a session and obtain a Customer object by querying it with primary ID using
the below program −
In our sample table, x.name happens to be 'Gopal Krishna'. Let us delete this x from the
session and count the occurrence of this name.
session.delete(x)
session.query(Customer).filter_by(name = 'Gopal Krishna').count()
SELECT count(*)
AS count_1
FROM (
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.name = ?)
AS anon_1('Gopal Krishna',) 0
62/73
However, the related Invoice objects of x are still there. It can be verified by the
following code −
session.query(Invoice).filter(Invoice.invno.in_([10,14])).count()
Here, 10 and 14 are invoice numbers belonging to customer Gopal Krishna. Result of
the above query is 2, which means the related objects have not been deleted.
SELECT count(*)
AS count_1
FROM (
SELECT invoices.id
AS invoices_id, invoices.custid
AS invoices_custid, invoices.invno
AS invoices_invno, invoices.amount
AS invoices_amount
FROM invoices
WHERE invoices.invno IN (?, ?))
AS anon_1(10, 14) 2
This is because SQLAlchemy doesn’t assume the deletion of cascade; we have to give a
command to delete it.
save-update
merge
expunge
delete
delete-orphan
refresh-expire
Often used option is "all, delete-orphan" to indicate that related objects should follow
along with the parent object in all cases, and be deleted when de-associated.
63/73
class Customer(Base):
__tablename__ = 'customers'
Let us delete the Customer with Gopal Krishna name using the below program and see
the count of its related Invoice objects −
64/73
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.id = ?
(2,)
SELECT invoices.id
AS invoices_id, invoices.custid
AS invoices_custid, invoices.invno
AS invoices_invno, invoices.amount
AS invoices_amount
FROM invoices
WHERE ? = invoices.custid
ORDER BY invoices.id (2,)
DELETE FROM invoices
WHERE invoices.id = ? ((1,), (2,))
DELETE FROM customers
WHERE customers.id = ? (2,)
SELECT count(*)
AS count_1
FROM (
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.name = ?)
AS anon_1('Gopal Krishna',)
SELECT count(*)
AS count_1
FROM (
SELECT invoices.id
AS invoices_id, invoices.custid
AS invoices_custid, invoices.invno
AS invoices_invno, invoices.amount
AS invoices_amount
FROM invoices
WHERE invoices.invno IN (?, ?))
AS anon_1(10, 14)
0
65/73
For this purpose, we shall create a SQLite database (mycollege.db) with two tables -
department and employee. Here, we assume that an employee is a part of more than
one department, and a department has more than one employee. This constitutes
many-to-many relationship.
class Department(Base):
__tablename__ = 'department'
id = Column(Integer, primary_key = True)
name = Column(String)
employees = relationship('Employee', secondary = 'link')
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key = True)
name = Column(String)
departments = relationship(Department,secondary='link')
We now define a Link class. It is linked to link table and contains department_id and
employee_id attributes respectively referencing to primary keys of department and
employee table.
class Link(Base):
__tablename__ = 'link'
department_id = Column(
Integer,
ForeignKey('department.id'),
primary_key = True)
employee_id = Column(
Integer,
ForeignKey('employee.id'),
primary_key = True)
Here, we have to make a note that Department class has employees attribute related to
Employee class. The relationship function’s secondary attribute is assigned a link as its
value.
Similarly, Employee class has departments attribute related to Department class. The
relationship function’s secondary attribute is assigned a link as its value.
All these three tables are created when the following statement is executed −
Base.metadata.create_all(engine)
66/73
The Python console emits following CREATE TABLE queries −
67/73
Next we create three objects of Department class and three objects of Employee class as
shown below −
d1 = Department(name = "Accounts")
d2 = Department(name = "Sales")
d3 = Department(name = "Marketing")
e1 = Employee(name = "John")
e2 = Employee(name = "Tony")
e3 = Employee(name = "Graham")
68/73
Each table has a collection attribute having append() method. We can add Employee
objects to Employees collection of Department object. Similarly, we can add
Department objects to departments collection attribute of Employee objects.
e1.departments.append(d1)
e2.departments.append(d3)
d1.employees.append(e3)
d2.employees.append(e2)
d3.employees.append(e1)
e3.departments.append(d2)
All we have to do now is to set up a session object, add all objects to it and commit the
changes as shown below −
To check the effect of above operations, use SQLiteStudio and view data in department,
employee and link tables −
69/73
70/73
To display the data, run the following query statement −
As per the data populated in our example, output will be displayed as below −
SQLAlchemy - Dialects
SQLAlchemy uses system of dialects to communicate with various types of databases.
Each database has a corresponding DBAPI wrapper. All dialects require that an
appropriate DBAPI driver is installed.
Firebird
Microsoft SQL Server
MySQL
Oracle
71/73
PostgreSQL
SQL
Sybase
dialect+driver://username:password@host:port/database
PostgreSQL
The PostgreSQL dialect uses psycopg2 as the default DBAPI. pg8000 is also available
as a pure-Python substitute as shown below:
# default
engine = create_engine('postgresql://scott:tiger@localhost/mydatabase')
# psycopg2
engine = create_engine('postgresql+psycopg2://scott:tiger@localhost/mydatabase')
# pg8000
engine = create_engine('postgresql+pg8000://scott:tiger@localhost/mydatabase')
MySQL
The MySQL dialect uses mysql-python as the default DBAPI. There are many MySQL
DBAPIs available, such as MySQL-connector-python as follows −
# default
engine = create_engine('mysql://scott:tiger@localhost/foo')
# mysql-python
engine = create_engine('mysql+mysqldb://scott:tiger@localhost/foo')
# MySQL-connector-python
engine = create_engine('mysql+mysqlconnector://scott:tiger@localhost/foo')
Oracle
The Oracle dialect uses cx_oracle as the default DBAPI as follows −
engine = create_engine('oracle://scott:tiger@127.0.0.1:1521/sidname')
engine = create_engine('oracle+cx_oracle://scott:tiger@tnsname')
# pymssql
engine = create_engine('mssql+pymssql://scott:tiger@hostname:port/dbname')
SQLite
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 as shown below −
engine = create_engine('sqlite:///foo.db')
And for an absolute file path, the three slashes are followed by the absolute path as given
below −
engine = create_engine('sqlite:///C:\\path\\to\\foo.db')
engine = create_engine('sqlite://')
Conclusion
In the first part of this tutorial, we have learnt how to use the Expression Language to
execute SQL statements. Expression language embeds SQL constructs in Python code.
In the second part, we have discussed object relation mapping capability of
SQLAlchemy. The ORM API maps the SQL tables with Python classes.
73/73