{"id":13578263,"url":"https://github.com/sripathikrishnan/jinjasql","last_synced_at":"2025-12-12T01:01:49.914Z","repository":{"id":40394466,"uuid":"71768806","full_name":"sripathikrishnan/jinjasql","owner":"sripathikrishnan","description":"Template Language for SQL with Automatic Bind Parameter Extraction","archived":false,"fork":false,"pushed_at":"2024-04-10T05:05:19.000Z","size":76,"stargazers_count":834,"open_issues_count":20,"forks_count":88,"subscribers_count":23,"default_branch":"master","last_synced_at":"2025-05-23T18:14:42.024Z","etag":null,"topics":["python","sql","template-language"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sripathikrishnan.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-10-24T08:36:50.000Z","updated_at":"2025-05-13T07:24:42.000Z","dependencies_parsed_at":"2024-06-18T14:01:26.298Z","dependency_job_id":"33b4b06f-8e0f-4cf5-b4ab-f5053bdec626","html_url":"https://github.com/sripathikrishnan/jinjasql","commit_stats":{"total_commits":82,"total_committers":11,"mean_commits":7.454545454545454,"dds":"0.41463414634146345","last_synced_commit":"d1e0d0fcc4564f621c1c6ba8bf3c6d2949425ea0"},"previous_names":["hashedin/jinjasql"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/sripathikrishnan/jinjasql","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sripathikrishnan%2Fjinjasql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sripathikrishnan%2Fjinjasql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sripathikrishnan%2Fjinjasql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sripathikrishnan%2Fjinjasql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sripathikrishnan","download_url":"https://codeload.github.com/sripathikrishnan/jinjasql/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sripathikrishnan%2Fjinjasql/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27673718,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-11T02:00:11.302Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["python","sql","template-language"],"created_at":"2024-08-01T15:01:28.886Z","updated_at":"2025-12-12T01:01:49.867Z","avatar_url":"https://github.com/sripathikrishnan.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# Generate SQL Queries using a Jinja Template, without worrying about SQL Injection #\n\n[![Github Actions Build Status](https://github.com/sripathikrishnan/jinjasql/workflows/Tests/badge.svg)](https://github.com/sripathikrishnan/jinjasql/actions)\n\nJinjaSQL is a template language for SQL statements and scripts. \nSince it's based in [Jinja2](http://jinja.pocoo.org/), \nyou have all the power it offers - conditional statements, macros,\nlooping constructs, blocks, inheritance, and many more.\n\nJinjaSQL automatically binds parameters that are inserted into the template.\nAfter JinjaSQL evaluates the template, you get:\n\n1. A Query with %s placeholders for the parameters\n2. A List of values corresponding to the placeholders that need to be bound to the query\n\nJinjaSQL doesn't actually execute the query - it only prepares the \nquery and the bind parameters. You can execute the query using any \ndatabase engine / driver you are working with.\n\nFor example, if you have a template like this -\n\n```sql    \nselect username, sum(spend)\nfrom transactions\nwhere start_date \u003e {{request.start_date}}\nand end_date \u003c {{request.end_date}}\n{% if request.organization %}\nand organization = {{request.organization}}\n{% endif %}\n```\n\nthen, depending on the parameters you provide, you get a query\n\n```sql\nselect username, sum(spend)\nfrom transaction\nwhere start_date \u003e %s\nand end_date \u003c %s\nand organization = %s\n```\nwith bind parameters = ['2016-10-10', '2016-10-20', 1321]\n\nIf `request.organization` was empty/falsy, the corresponding and clause\nwould be absent from the query, and the list of bind parameters\nwould not have the organization id.\n\n## When to use JinjaSQL ##\n\nJinjaSQL is *not* meant to replace your ORM. ORMs like those provided\nby SQLAlchemy or Django are great for a variety of use cases, and should\nbe the default in most cases. But there are a few use cases where \nyou really need the power of SQL.\n\nUse JinjaSQL for - \n\n1. Reporting, business intelligence or dashboard like use cases\n1. When you need aggregation/group by\n1. Use cases that require data from multiple tables\n1. Migration scripts \u0026 bulk updates that would benefit from macros\n\nIn all other use cases, you should reach to your ORM \ninstead of writing SQL/JinjaSQL.\n\nWhile JinjaSQL can handle insert/update statements, you are better off\nusing your ORM to handle such statements. JinjaSQL is mostly meant \nfor dynamic select statements that an ORM cannot handle as well.\n\n## Basic Usage ##\n\nFirst, import the `JinjaSql` class and create an object. `JinjaSql` is thread-safe, so you can safely create one object at startup and use it everywhere.\n\n```python\nfrom jinjasql import JinjaSql\nj = JinjaSql()\n```\n\nNext, create your template query. You can use the full power of Jinja templates over here - macros, includes, imports, if/else conditions, loops, filters and so on. You can load the template from a file or from database or wherever else Jinja supports.\n\n```python\ntemplate = \"\"\"\n    SELECT project, timesheet, hours\n    FROM timesheet\n    WHERE user_id = {{ user_id }}\n    {% if project_id %}\n    AND project_id = {{ project_id }}\n    {% endif %}\n\"\"\"\n```\n\nCreate a context object. This object is a regular dictionary, and can contain nested dictionaries, lists or objects. The template query is evaluated against this context object.\n\n```python\ndata = {\n    \"project_id\": 123,\n    \"user_id\": u\"sripathi\"\n}\n```\n\nFinally, call the `prepare_query` method with the template and the context. You get back two things:\n\n1. `query` is the generated SQL query. Variables are replaced by %s\n1. `bind_params` is an array of parameters corresponding to the %s\n\n```python\nquery, bind_params = j.prepare_query(template, data)\n```\n\nThis is the query that is generated:\n```python\nexpected_query = \"\"\"\n    SELECT project, timesheet, hours\n    FROM timesheet\n    WHERE user_id = %s\n    \n    AND project_id = %s\n\"\"\"\n```\n\nAnd these are the bind parameters:\n```python\nself.assertEquals(bind_params, [u'sripathi', 123])\nself.assertEquals(query.strip(), expected_query.strip())\n```\n\nYou can now use the query and bind parameters to execute the query. For example, in django, you would do something like this:\n\n```python\nfrom django.db import connection\nwith connection.cursor() as cursor:\n    cursor.execute(query, bind_params)\n    for row in cursor.fetchall():\n        # do something with the results\n        pass\n```\n\n## Multiple Param Styles ##\nPer [PEP-249](https://www.python.org/dev/peps/pep-0249/), bind parameters can be specified in multiple ways. \nYou can pass the optional constructor argument `param_style` to control\nthe style of query parameter.\n\n1. **format** : `... where name = %s`. This is the default\n1. **qmark** :  `where name = ?`\n1. **numeric** : `where name = :1 and last_name = :2`\n1. **named** : `where name = :name and last_name = :last_name`\n1. **pyformat** : `where name = %(name)s and last_name = %(last_name)s`\n1. **asyncpg** : `where name = $1 and last_name = $2`. This is not part of PEP-249 standard, but is used by [asyncpg library for postgres](https://magicstack.github.io/asyncpg/current/usage.html)\n\nHere's how it works - \n\n```python\nj = JinjaSql(param_style='named')\nquery, bind_params = j.prepare_query(template, data)\n```\n\nIf param_style is `named` or `pyformat`, `bind_parameters` will be a python dictionary. For all other param styles, it will be a list.\n\nIn case of `named` and `pyformat`, remember the following:\n\n1. `prepare_query` returns a dictionary instead of a list\n1. The returned dictionary is flat, and only contains keys that are actually used in the query\n1. The keys in the dictionary and in the query are guaranteed to have unique names. Even if you bind the same parameter twice, the key will be renamed\n\n\n## Handling In Clauses ##\nIf you bind a list or tuple in query, JinjaSQL will raise \na `MissingInClauseException`. JinjaSQL needs manual intervention - you have to apply the `|inclause` filter.\n\n```sql\nselect 'x' from dual\nwhere project_id in {{ project_ids | inclause }}\n```\nNotice that you don't need to enclose in parantheses.\n\nJinjaSQL will automatically create the appropriate number of bind expressions.\n\n## SQL Safe Strings ##\nSometimes, you want to insert dynamic table names/column names. By default, JinjaSQL will convert them to bind parameters. This won't work, because table and column names are usually not allowed in bind \nparameters.\n\nIn such cases, you can use the `|sqlsafe` filter. \n\n```sql\nselect {{column_names | sqlsafe}} from dual\n```\n\nIf you use `sqlsafe`, it is your responsibility to ensure there is no sql injection.\n\n## Installing jinjasql ##\n\nPre-Requisites : \n\n1. python 2.7.x, 3.4.x or 3.5.x\n2. jinja2 \u003e= version 2.5\n\nTo install from PyPI (recommended) :\n\n    pip install jinjasql\n    \nTo install from source : \n\n    git clone https://github.com/sripathikrishnan/jinjasql\n    cd jinjasql\n    sudo python setup.py install\n\n## How does JinjaSQL work? ##\n\n### The bind filter ###\n\nAt it's core, JinjaSQL provides a filter called `bind`. This filter gobbles up whatever value is provided, and always emits the placeholder string %s. The actual value is then stored in a thread local list of bind parameters.\n\n```python\njinja.prepare_query(\"select * from user where id = {{userid | bind}}\", \n                    {userid: 143})\n```\n\nWhen this code is evaluated, the output query is `select * from user where id = %s`. \n\n### Pre-processing the Query Template ###\n\nManually applying the `bind` filter to every parameter is error-prone. Sooner than later, a developer will miss the filter, and it will lead to SQL Injection.\n\nJinjaSQL automatically applies the bind filter to ALL variables. The query template is transformed before it is evaluated.\n\n```sql\nselect * from user where id = {{userid}}\n```\n\nbecomes \n```sql\nselect * from user where id = {{userid | bind}}\n```\n\nJinja lets extensions to [rewrite the token stream](http://jinja.pocoo.org/docs/dev/extensions/#jinja2.ext.Extension.filter_stream). JinjaSQL looks for `variable_begin` and `variable_end` tokens in the stream, and rewrites the stream to include the `bind` filter as the last filter.\n\n### Autoescape and JinjaSQL ###\n\nJinja has an autoescape feature. If turned on, it automatically HTML escapes variables. It does this by wrapping strings using the `Markup` class.\n\nJinjaSQL builds on this functionality. JinjaSQL requires autoescape to be turned on. As a result, strings that are injected are wrapped using the Markup class. JinjaSQL uses this wrapper class as well to prevent double-binding of parameters.\n\n\n## License\n\njinjasql is licensed under the MIT License. See [LICENSE](https://github.com/sripathikrishnan/jinjasql/blob/master/LICENSE)\n\n\n## Developer Notes\n\nJinjaSQL runs tests against a variety of databases and database drivers. It uses the testcontainers project to launch databases in docker containers.\n\nTo setup your development environment and run the tests on an ubuntu machine:\n\n```bash\nsudo apt-get install gcc g++ python3-dev unixodbc unixodbc-dev \npip install -r requirements.txt\npython run_tests\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsripathikrishnan%2Fjinjasql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsripathikrishnan%2Fjinjasql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsripathikrishnan%2Fjinjasql/lists"}