Package: databank
DatabankDatabank is an easy-to-use Python library for making raw SQL queries in a multi-threaded environment.No ORM, no frills. Only raw SQL queries and parameter binding. Thread-safe. Built on top ofSQLAlchemy.(The photo was taken byMatthew Ratzloffand is licensed under CC BY-NC-ND 2.0.)InstallationYou can install the latest stable version fromPyPI:$ pip install databankAdapters not included.Install e.g.psycopg2for PostgreSQL:$ pip install psycopg2UsageConnect to the database of your choice:>>>fromdatabankimportDatabase>>>db=Database("postgresql://user:password@localhost/db",pool_size=2)The keyword arguments are passed directly to SQLAlchemy'screate_engine()function. Depending on the database you connect to, you have options like the size of connection pools.If you are usingdatabankin a multi-threaded environment (e.g. in a web application), make sure the pool size is at least the number of worker threads.Let's create a simple table:>>>db.execute("CREATE TABLE beatles (id SERIAL PRIMARY KEY, member TEXT NOT NULL);")You can insert multiple rows at once:>>>params=[...{"id":0,"member":"John"},...{"id":1,"member":"Paul"},...{"id":2,"member":"George"},...{"id":3,"member":"Ringo"}...]>>>db.execute_many("INSERT INTO beatles (id, member) VALUES (:id, :member);",params)Fetch a single row:>>>db.fetch_one("SELECT * FROM beatles;"){'id':0,'member':'John'}But you can also fetchnrows:>>>db.fetch_many("SELECT * FROM beatles;",n=2)[{'id':0,'member':'John'},{'id':1,'member':'Paul'}]Or all rows:>>>db.fetch_all("SELECT * FROM beatles;")[{'id':0,'member':'John'},{'id':1,'member':'Paul'},{'id':2,'member':'George'},{'id':3,'member':'Ringo'}]If you are using PostgreSQL withjsonbcolumns, you can use a helper function to serialize the parameter values:>>>fromdatabank.utilsimportserialize_params>>>serialize_params({"member":"Ringo","song":["Don't Pass Me By","Octopus's Garden"]}){'member':'Ringo','song':'["Don\'t Pass Me By", "Octopus\'s Garden"]'}Executing Queries in the BackgroundFor bothexecute()andexecute_many()you can pass anin_backgroundkeyword argument (which is by defaultFalse). If set toTrue, the query will be executed in the background in another thread and the method will return immediately theThreadobject (i.e. non-blocking). You can calljoin()on that object to wait for the query to finish or just do nothing and go on:>>>db.execute("INSERT INTO beatles (id, member) VALUES (:id, :member);",{"id":4,"member":"Klaus"},in_background=True)<Thread(Thread-1(_execute),started140067398776512)>Beware that if you are usingin_background=True, you have to make sure that the connection pool size is large enough to handle the number of concurrent queries and that your program is running long enough if you are not explicitly waiting for the thread to finish. Also note that this might lead to a range of other issues like locking, reduced performance or even deadlocks. You also might want to set an explicit timeout for queries by passing e.g.{"options": "-c statement_timeout=60000"}for PostgreSQL when initializing theDatabaseobject to kill all queries taking longer than 60 seconds.Query CollectionYou can also organize SQL queries in an SQL file and load them into aQueryCollection:/* @name insert_data */INSERTINTObeatles(id,member)VALUES(:id,:member);/* @name select_all_data */SELECT*FROMbeatles;This idea is borrowed fromPgTypedA querymusthave a header comment with the name of the query. If a query name is not unique, the last query with the same name will be used. You can parse that file and load the queries into aQueryCollection:>>>fromdatabankimportQueryCollection>>>queries=QueryCollection.from_file("queries.sql")and access the queries like in a dictionary:>>>queries["insert_data"]'INSERT INTO beatles (id, member) VALUES (:id, :member);'>>>queries["select_all_data"]'SELECT * FROM beatles;'