Package: templatequery
Template QueryProvides an easy and readable way to create formattable template query strings with placeholders for psycopg2. Instead of wrapping arguments withpsycopg2.sqlclasses (e.g.Literal,Identifier) the expected class can be written inside the query.Quick Example:>>>TemplateQuery('SELECT * FROM {table@Q} WHERE {@I} {@S} {value@L}').format(...'column_name','>=',table='public.my_table',value=100...).as_string(conn)'SELECT * FROM "public"."my_table" WHERE "column_name" >= 100'InstallationThis package requirespsycopg2and can be installed usingpipto download it fromPyPI:$pipinstalltemplatequeryor usingsetup.pyif you have downloaded the source package locally:$pythonsetup.pybuild
$sudopythonsetup.pyinstallUsageIn psycopg2, variables can be inserted into queries using%splaceholders and supplying arguments tocursor.executebut this does not allow for identifier arguments such as table or columns names. The alternative is to usepsycopg2.sql.SQL.formatbut this requires arguments to be converted intoComposableobjects such asLiteralorIdentifier.TheTemplateQueryclass allows this conversion to be specified inside the query and applied to the formatting arguments automatically.Normally placeholders are written as{}for positional arguments and{key_name}for keyword arguments.TemplateQueryallows for additional placeholders of the form{key_name@X}, wherekey_nameis optional andXis one of the following formats which applies to the relevant argument a class frompsycopg2.sql:S(wraps withSQL) raw query snippet with no escaping!! beware of SQL injection !!I(wraps withIdentifier) identifier representing names of database objectsP(wraps withPlaceholder) %s style placeholder whose value can be added laterAn additional formQcan be used to separate qualified names that are dot-separated, such as"schema.table", into aComposedof individualIdentifierobjects joined bySQL('.'). Supplying a tuple of identifiers and using theIform will achieve the same result when usingpsycopg2 >= 2.8Example Scriptfrompsycopg2importconnectfrompsycopg2.extrasimportexecute_valuesfromtemplatequeryimportTemplateQueryfromrandomimportrandint# example database configurationconnection_details=dict(host='localhost',dbname='test',user='postgres',password='password')# example table containing itemsparams=dict(table='public.item',category='brand',value='price_cents',)# queriesquery_create=TemplateQuery("DROP TABLE IF EXISTS {table@Q}; ""CREATE TABLE {table@Q} (""id bigserial, ""{category@I} varchar, ""{value@I} bigint);")query_insert=TemplateQuery("INSERT INTO {table@Q} ({category@I}, {value@I}) ""VALUES%s")query_analyze=TemplateQuery("SELECT ""{category@I}, AVG({value@I}) {avg_value@I}""FROM {table@Q}""GROUP BY {category@I}""ORDER BY {avg_value@I}")# connect to postgreSQL using a psycopg2 connectionwithconnect(**connection_details)asconn:cursor=conn.cursor()# create tablecursor.execute(query_create.format(**params))# insert data# generate test data for columns (brand, price)# where a higher value gives a character closer to Adata=[]for_inrange(1000):score=randint(0,5)data.append(('FEDCBA'[score],(randint(1,10000)*(score+1))))execute_values(cursor,query_insert.format(**params),data)conn.commit()# analyze average prices per category (brand)cursor.execute(query_analyze.format(**params,avg_value='avg_'+params['value']))result=cursor.fetchall()>>>result[('F',Decimal('4975.8218390804597701')),('E',Decimal('10353.853658536585')),('D',Decimal('15447.445714285714')),('C',Decimal('21370.236024844720')),('B',Decimal('25997.774566473988')),('A',Decimal('31847.215686274510'))]