Python DB-API is not a specific library, but a set of rules that govern individual modules that implement work with specific databases. The individual implementation nuances for different databases may differ, but the general principles allow using the same approach when working with different databases.
The article discusses the basic methods of DB-API, allowing you to fully work with the database. The full list can be found on the links at the end of the article.
Required level of preparation : a basic understanding of the syntax of SQL and Python.
Preparing inventory for further comfortable work
- Python has built-in support for SQLite database, for this you don’t need to install anything further, it’s enough to specify the import of the standard library in the script
import sqlite3
- Download the test database with which we will work. This article will use the open (MIT license) test database “Chinook”. You can download it at the following links:
')
chinookdatabase.codeplex.com
github.com/lerocha/chinook-database
We need only the binary file “Chinook_Sqlite.sqlite” to work.
- For the convenience of working with the database (viewing, editing) we need a database browser program that supports SQLite. The article does not deal with the browser, but it will help you to see visually what is happening with the base during our experiments.
Note : when making changes to the base, do not forget to apply them, as the base with unapplied changes remains locked.
You can use (the last two options are cross-platform and free):
Python DB-API modules depending on the database
Database | DB-API module |
---|
Sqlite | sqlite3 |
PostgreSQL | psycopg2 |
Mysql | mysql.connector |
Odbc | pyodbc |
Connection to the base, getting the cursor
To begin, consider the most basic DB-API template, which we will use in all further examples:
When working with other databases, additional connection parameters are used, for example, for PostrgeSQL:
conn = psycopg2.connect( host=hostname, user=username, password=password, dbname=database)
Reading from base
Please note: After getting the result from the cursor, the second time without repeating the query itself you cannot get it - the empty result will return!
Record to base
Note : If several connections are made to the database and one of them performs a modification of the base, the SQLite database is locked before completion (connection method.
Commit ()) or cancellation (connection method.
Rollback ()) of the transaction.
We split the query into multiple lines in triple quotes
Long queries can be split into several lines in random order, if they are enclosed in triple quotes - single ('' '...' '') or double ("" "..." "")
cursor.execute(""" SELECT name FROM Artist ORDER BY Name LIMIT 3 """)
Of course, in such a simple example the breakdown does not make sense, but on complex long queries it can dramatically increase the readability of the code.
Combining database queries into one method call
The cursor method .execute () allows you to do only one query at a time, when you try to make multiple semicolons, there will be an error.
For those who do not believe in the word: cursor.execute(""" insert into Artist values (Null, 'A Aagrh!'); insert into Artist values (Null, 'A Aagrh-2!'); """)
To solve this problem, you can either call the cursor method several times.
execute ()
cursor.execute("""insert into Artist values (Null, 'A Aagrh!');""") cursor.execute("""insert into Artist values (Null, 'A Aagrh-2!');""")
Or use the cursor method.
executescript ()
cursor.executescript(""" insert into Artist values (Null, 'A Aagrh!'); insert into Artist values (Null, 'A Aagrh-2!'); """)
This method is also convenient when our requests are stored in a separate variable or even in a file and we need to apply it to the database.
Make the substitution of the value in the query
It is important ! Never, under any circumstances, use string concatenation (+) or parameter interpolation in a string (%) to transfer variables to a SQL query. Such a query formation, with the possibility of user data entering into it, is a gateway for SQL injections!
The correct way is to use the second argument of the .execute () method.
Two options are possible:
Note 1 : In PostgreSQL (UPD: and in MySQL) instead of the '?' used for substitution:% s
Note 2 : In this way, it will not be possible to replace the names of the tables, one of the possible solutions in this case is considered here:
stackoverflow.com/questions/3247183/variable-table-name-in-sqlite/3247553#3247553UPD: Note 3 : Thanks to
Igelko for mentioning the
paramstyle parameter - it determines which style is used for variable substitution in this module.
Here is a link
with a useful technique for working with different styles of substitutions .
We do multiple insertion of rows passing through the collection using the cursor method. executemany ()
We get the results one by one using the cursor method. fetchone ()
He always returns a tuple or None. if the request is empty.
cursor.execute("SELECT Name FROM Artist ORDER BY Name LIMIT 3") print(cursor.fetchone())
It is important ! The standard cursor retrieves all data from the server immediately, regardless of whether we use .fetchall () or .fetchone ()
Cursor as an iterator
UPD: Increase code stability
I thank
paratagas for the valuable addition:
To make the program more stable (especially during write operations), you can wrap the instructions for accessing the database in “try-except-else” blocks and use the native error object built into sqlite3, for example, like this:
try: cursor.execute(sql_statement) result = cursor.fetchall() except sqlite3.DatabaseError as err: print("Error: ", err) else: conn.commit()
UPD: Using with psycopg2
Thanks to
KurtRotzke for the valuable addition:
Recent versions of psycopg2 allow you to do this:
with psycopg2.connect("dbname='habr'") as conn: with conn.cursor() as cur:
Some objects in Python have __enter__ and __exit__ methods, which allows you to "cleanly" interact with them, as in the example above.
UPD: row_factory usage
Thanks to
remzalp for a valuable addition:
Using
row_factory allows
you to take metadata from a query and refer to the result, for example, by the column name.
Essentially - a callback to process data when returning a string. Yes, and a useful cursor.description, where there is everything you need.
Example from documentation:
import sqlite3 def dict_factory(cursor, row): d = {} for idx, col in enumerate(cursor.description): d[col[0]] = row[idx] return d con = sqlite3.connect(":memory:") con.row_factory = dict_factory cur = con.cursor() cur.execute("select 1 as a") print(cur.fetchone()["a"])
Additional materials (in English)
The second part of the article is under development, where working with the database in Python using SQLAlchemy will be considered.
I invite you to discuss:
- If I somewhere made an inaccuracy or did not take into account something important - write in the comments, important comments will later be added to the article indicating your authorship.
- If some points are not clear and clarification is required - write your questions in the comments - or I or other readers will give an answer, and efficient questions with answers will be later added to the article.