├── .gitignore ├── LICENSE ├── README.rst ├── django_print_sql ├── __init__.py └── print_sql.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | build/ 3 | django_print_sql.egg-info/ 4 | __pycache__/ 5 | *.pyc 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Aaron Zhang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | django-print-sql 2 | ================ 3 | 4 | django-print-sql is an easy-to-use SQL debug tool for Django developers to print SQL statements 5 | 6 | 7 | What's new 8 | ---------- 9 | 10 | **2018.3.6** 11 | 12 | * Added a decorator 13 | * It now prints how long it takes each query to execute, as well as the total time elapsed 14 | 15 | Requirements 16 | ------------ 17 | 18 | You need to have django installed (obviously). 19 | 20 | I've tried it on Django 1.11.11 and 2.0.3. 21 | 22 | If sqlparse is installed, the SQL statement wil be formatted. 23 | 24 | Install 25 | ------- 26 | 27 | From pip, run:: 28 | 29 | $ pip install --upgrade django-print-sql 30 | 31 | Consider using the ``--user`` option_. 32 | 33 | .. _option: https://pip.pypa.io/en/latest/user_guide/#user-installs 34 | 35 | From the repository, run:: 36 | 37 | python setup.py install 38 | 39 | to install django-print-sql on your system. 40 | 41 | django-print-sql is compatible with Python 2.7 and Python 3 (>= 3.3) (hopefully :D). 42 | 43 | Install sqlparse to pretty print the statements:: 44 | 45 | $ pip install --upgrade sqlparse 46 | 47 | Usage 48 | ----- 49 | Example 1. Use as context manager:: 50 | 51 | from django_print_sql import print_sql 52 | 53 | # set `count_only` to `True` will print the number of executed SQL statements only 54 | with print_sql(count_only=False): 55 | 56 | # write the code you want to analyze in here, 57 | # e.g. some complex foreign key lookup, 58 | # or analyzing a DRF serializer's performance 59 | 60 | for user in User.objects.all()[:10]: 61 | user.groups.first() 62 | 63 | Example 2. Use as decorator:: 64 | 65 | from django_print_sql import print_sql_decorator 66 | 67 | 68 | @print_sql_decorator(count_only=False) # this works on class-based views as well 69 | def get(request): 70 | # your view code here 71 | 72 | Links 73 | ----- 74 | 75 | Project Page 76 | https://github.com/rabbit-aaron/django-print-sql 77 | 78 | django-print-sql is licensed under the MIT license. 79 | 80 | Parts of the readme are based on sqlparse's readme file. 81 | sqlparse: https://github.com/andialbrecht/sqlparse 82 | -------------------------------------------------------------------------------- /django_print_sql/__init__.py: -------------------------------------------------------------------------------- 1 | from .print_sql import print_sql, print_sql_decorator 2 | 3 | __all__ = ['print_sql', 'print_sql_decorator'] -------------------------------------------------------------------------------- /django_print_sql/print_sql.py: -------------------------------------------------------------------------------- 1 | import time 2 | from django.db.models.sql.compiler import SQLCompiler 3 | from functools import wraps 4 | from contextlib import contextmanager 5 | 6 | try: 7 | import sqlparse 8 | except ImportError: 9 | import warnings 10 | warnings.warn('`pip install sqlparse` to use the pretty print feature') 11 | 12 | class sqlparse(object): 13 | 14 | @staticmethod 15 | def format(statement, *args, **kwargs): 16 | return statement 17 | 18 | 19 | def pprint_sql(query): 20 | statement = query[0] % query[1] 21 | print(sqlparse.format(statement, reindent=True, keyword_case='upper')) 22 | 23 | 24 | original_execute_sql = SQLCompiler.execute_sql 25 | 26 | 27 | @contextmanager 28 | def print_sql(count_only=False): 29 | 30 | shared_var = {'count': 0, 'total_time': 0} 31 | 32 | @wraps(original_execute_sql) 33 | def execute_sql(self, *args, **kwargs): 34 | shared_var['count'] += 1 35 | time_begin = time.time() 36 | ret = original_execute_sql(self, *args, **kwargs) 37 | time_elapsed = time.time() - time_begin 38 | shared_var['total_time'] += time_elapsed 39 | if not count_only: 40 | pprint_sql(self.as_sql()) 41 | print('[Time elapsed: {time:.2f}ms]\n'.format(time=time_elapsed * 1000)) 42 | return ret 43 | 44 | # monkey patching the SQLCompiler 45 | SQLCompiler.execute_sql = execute_sql 46 | 47 | yield # execute code in the `with` statement 48 | 49 | # restore original execute_sql 50 | SQLCompiler.execute_sql = original_execute_sql 51 | 52 | print('[{num_of_queries} {query_word} executed, total time elapsed {time:.2f}ms]\n'.format( 53 | num_of_queries=shared_var['count'], 54 | query_word='query' if shared_var['count'] == 1 else 'queries', 55 | time=shared_var['total_time'] * 1000 56 | )) 57 | 58 | 59 | def print_sql_decorator(*args, **kwargs): 60 | def wrapper(func): 61 | @wraps(func) 62 | def wrapped(*fargs, **fkwargs): 63 | with print_sql(*args, **kwargs): 64 | return func(*fargs, **fkwargs) 65 | return wrapped 66 | return wrapper 67 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Always prefer setuptools over distutils 2 | from setuptools import setup, find_packages 3 | # To use a consistent encoding 4 | from codecs import open 5 | from os import path 6 | 7 | here = path.abspath(path.dirname(__file__)) 8 | 9 | # Get the long description from the README file 10 | with open(path.join(here, 'README.rst'), encoding='utf-8') as f: 11 | long_description = f.read() 12 | 13 | # Arguments marked as "Required" below must be included for upload to PyPI. 14 | # Fields marked as "Optional" may be commented out. 15 | 16 | setup( 17 | name='django-print-sql', # Required 18 | version='2018.3.6', # Required 19 | description='django_print_sql is an easy-to-use SQL debug tool for Django developers to print SQL statements', # Required 20 | long_description=long_description, # Optional 21 | url='https://github.com/rabbit-aaron/django-print-sql', # Optional 22 | author='Aaron Zhang', # Optional 23 | author_email='rabbit.aaron@gmail.com', 24 | classifiers=[ # Optional 25 | 'Development Status :: 4 - Beta', 26 | 'Intended Audience :: Developers', 27 | 'License :: OSI Approved :: MIT License', 28 | 'Programming Language :: Python :: 2', 29 | 'Programming Language :: Python :: 2.7', 30 | 'Programming Language :: Python :: 3', 31 | 'Programming Language :: Python :: 3.4', 32 | 'Programming Language :: Python :: 3.5', 33 | 'Programming Language :: Python :: 3.6', 34 | ], 35 | keywords='django sql debug', # Optional 36 | packages=find_packages(), # Required 37 | ) 38 | --------------------------------------------------------------------------------