Skip to content

add mysql support #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@
A light-weight module to generate usable SQL from a Django QuerySet.

## Backend Support
Currently, `django-sql-compiler` only supports connections made via the `django.db.backends.postgresql` backend.
Currently, `django-sql-compiler` only supports connections made via the `django.db.backends.postgresql` and `django.db.backends.mysql` backends.

## About Django SQL Compiler
The Django ORM is very useful for abstracting away SQL queries from the focus of the developer. This is very useful
for preventing SQL injection attacks and generating queries programmatically using applied logic in your Django app.

However, very complex queries (such as those used in reporting, analytics, or data science projects) can be difficult
or impossible to create with the Django ORM alone. The ORM provides the `.raw` query method and exposes the raw database
or impossible to create with the Django ORM alone. The ORM provides the `.raw` query method and exposes the raw database
`connection` objects which can be used to execute arbitrary SQL against the database. In doing so, we lose the benefit
of the ORM with respect to dynamically adding components to the query (such as filters in a `WHERE` clause) in a way that
of the ORM with respect to dynamically adding components to the query (such as filters in a `WHERE` clause) in a way that
prevents injection attacks.

The base Django `QuerySet` object has a `Query` object available at the `.query` property. Casting this `Query` object as a `str`
prints out what looks like a valid SQL query. However, this version of the query is not properly escaped or quoted, meaning
The base Django `QuerySet` object has a `Query` object available at the `.query` property. Casting this `Query` object as a `str`
prints out what looks like a valid SQL query. However, this version of the query is not properly escaped or quoted, meaning
it's not **actually** valid SQL unless there are no dynamic components (such as filters from user input) in the `QuerySet`.

`django-sql-compiler` aims to provide a way to generate clean, usable SQL from a given `QuerySet`, which can be used in
tandem with a raw SQL query to give SQL users more flexibility in querying their Django-connected database while still
`django-sql-compiler` aims to provide a way to generate clean, usable SQL from a given `QuerySet`, which can be used in
tandem with a raw SQL query to give SQL users more flexibility in querying their Django-connected database while still
retaining the Django ORM for security and dynamic query generation purposes.

## Usage
Expand Down Expand Up @@ -79,7 +79,7 @@ filtered_query_set = MyModel.objects.filter(
field_one__lte=request.data.get('field_one_filter', 0),
field_two__in=request.data.get('field_two_filter', [])
)


more_complex_query = """
select
Expand All @@ -94,4 +94,4 @@ more_complex_results = MyModel.objects.raw(more_complex_query)
```

Now, users who are more familiar with SQL rather than the Django ORM can use the ORM for security and conveniently
generating SQL queries and use SQL for the rest of their transformations.
generating SQL queries and use SQL for the rest of their transformations.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

setup(
name='django-sql-compiler',
version='0.1.1',
version='0.2.3',
description="A light-weight module to generate usable SQL from a Django QuerySet.",
long_description=open('README.md').read(),
long_description_content_type='text/markdown',
Expand Down
3 changes: 3 additions & 0 deletions sql_compiler/compilers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from .postgresql import PostgresCompiler, PostgresTenantsCompiler, Psycopg2Compiler
from .mysql import MySQLCompiler

__all__ = [
'MySQLCompiler',
'PostgresCompiler',
'PostgresTenantsCompiler',
'Psycopg2Compiler',
Expand All @@ -9,6 +11,7 @@
]

compilers = [
MySQLCompiler,
PostgresCompiler,
PostgresTenantsCompiler,
Psycopg2Compiler
Expand Down
26 changes: 25 additions & 1 deletion sql_compiler/compilers/mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,28 @@ class MySQLCompiler(BaseSQLCompiler):
backend_name = 'django.db.backends.mysql'

def compile_sql(self, connection, query, params):
pass
with connection.cursor() as cursor:
compiled_sql = self.mogrify(cursor, query, params)
return compiled_sql

def mogrify(self, cursor, query, args=None):
db = cursor.connection
if isinstance(query, str):
query = query.encode(db.encoding)

if args is not None:
if isinstance(args, dict):
nargs = {}
for key, item in args.items():
if isinstance(key, str):
key = key.encode(db.encoding)
nargs[key] = db.literal(item)
args = nargs
else:
args = tuple(map(db.literal, args))
try:
query = query % args
except TypeError as m:
raise ProgrammingError(str(m))

return query