mirror of
				https://github.com/django/django.git
				synced 2025-10-26 07:06:08 +00:00 
			
		
		
		
	time.monotonic() available from Python 3.3: - Nicely communicates a narrow intent of "get a local system monotonic clock time" instead of possible "get a not necessarily accurate Unix time stamp because it needs to be communicated to outside of this process/machine" when time.time() is used. - Its result isn't affected by the system clock updates. There are two classes of time.time() uses changed to time.monotonic() by this change: - measuring time taken to run some code. - setting and checking a "close_at" threshold for for persistent db connections (django/db/backends/base/base.py).
		
			
				
	
	
		
			113 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			113 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| ========================
 | |
| Database instrumentation
 | |
| ========================
 | |
| 
 | |
| To help you understand and control the queries issued by your code, Django
 | |
| provides a hook for installing wrapper functions around the execution of
 | |
| database queries. For example, wrappers can count queries, measure query
 | |
| duration, log queries, or even prevent query execution (e.g. to make sure that
 | |
| no queries are issued while rendering a template with prefetched data).
 | |
| 
 | |
| The wrappers are modeled after :doc:`middleware </topics/http/middleware>` --
 | |
| they are callables which take another callable as one of their arguments. They
 | |
| call that callable to invoke the (possibly wrapped) database query, and they
 | |
| can do what they want around that call. They are, however, created and
 | |
| installed by user code, and so don't need a separate factory like middleware do.
 | |
| 
 | |
| Installing a wrapper is done in a context manager -- so the wrappers are
 | |
| temporary and specific to some flow in your code.
 | |
| 
 | |
| As mentioned above, an example of a wrapper is a query execution blocker. It
 | |
| could look like this::
 | |
| 
 | |
|     def blocker(*args):
 | |
|         raise Exception('No database access allowed here.')
 | |
| 
 | |
| And it would be used in a view to block queries from the template like so::
 | |
| 
 | |
|     from django.db import connection
 | |
|     from django.shortcuts import render
 | |
| 
 | |
|     def my_view(request):
 | |
|         context = {...}  # Code to generate context with all data.
 | |
|         template_name = ...
 | |
|         with connection.execute_wrapper(blocker):
 | |
|             return render(request, template_name, context)
 | |
| 
 | |
| The parameters sent to the wrappers are:
 | |
| 
 | |
| * ``execute`` -- a callable, which should be invoked with the rest of the
 | |
|   parameters in order to execute the query.
 | |
| 
 | |
| * ``sql`` -- a ``str``, the SQL query to be sent to the database.
 | |
| 
 | |
| * ``params`` -- a list/tuple of parameter values for the SQL command, or a
 | |
|   list/tuple of lists/tuples if the wrapped call is ``executemany()``.
 | |
| 
 | |
| * ``many`` -- a ``bool`` indicating whether the ultimately invoked call is
 | |
|   ``execute()`` or ``executemany()`` (and whether ``params`` is expected to be
 | |
|   a sequence of values, or a sequence of sequences of values).
 | |
| 
 | |
| * ``context`` -- a dictionary with further data about the context of
 | |
|   invocation. This includes the connection and cursor.
 | |
| 
 | |
| Using the parameters, a slightly more complex version of the blocker could
 | |
| include the connection name in the error message::
 | |
| 
 | |
|     def blocker(execute, sql, params, many, context):
 | |
|         alias = context['connection'].alias
 | |
|         raise Exception("Access to database '{}' blocked here".format(alias))
 | |
| 
 | |
| For a more complete example, a query logger could look like this::
 | |
| 
 | |
|     import time
 | |
| 
 | |
|     class QueryLogger:
 | |
| 
 | |
|         def __init__(self):
 | |
|             self.queries = []
 | |
| 
 | |
|         def __call__(self, execute, sql, params, many, context):
 | |
|             current_query = {'sql': sql, 'params': params, 'many': many}
 | |
|             start = time.monotonic()
 | |
|             try:
 | |
|                 result = execute(sql, params, many, context)
 | |
|             except Exception as e:
 | |
|                 current_query['status'] = 'error'
 | |
|                 current_query['exception'] = e
 | |
|                 raise
 | |
|             else:
 | |
|                 current_query['status'] = 'ok'
 | |
|                 return result
 | |
|             finally:
 | |
|                 duration = time.monotonic() - start
 | |
|                 current_query['duration'] = duration
 | |
|                 self.queries.append(current_query)
 | |
| 
 | |
| To use this, you would create a logger object and install it as a wrapper::
 | |
| 
 | |
|     from django.db import connection
 | |
| 
 | |
|     ql = QueryLogger()
 | |
|     with connection.execute_wrapper(ql):
 | |
|         do_queries()
 | |
|     # Now we can print the log.
 | |
|     print(ql.queries)
 | |
| 
 | |
| .. currentmodule:: django.db.backends.base.DatabaseWrapper
 | |
| 
 | |
| ``connection.execute_wrapper()``
 | |
| --------------------------------
 | |
| 
 | |
| .. method:: execute_wrapper(wrapper)
 | |
| 
 | |
| Returns a context manager which, when entered, installs a wrapper around
 | |
| database query executions, and when exited, removes the wrapper. The wrapper is
 | |
| installed on the thread-local connection object.
 | |
| 
 | |
| ``wrapper`` is a callable taking five arguments.  It is called for every query
 | |
| execution in the scope of the context manager, with arguments ``execute``,
 | |
| ``sql``, ``params``, ``many``, and ``context`` as described above. It's
 | |
| expected to call ``execute(sql, params, many, context)`` and return the return
 | |
| value of that call.
 |