In Django, how can I return the time it took to load a page (not the date) in every page of the site, without having to write in every views.py a code similar to the following one?
start = time.time() #model operations loadingpagetime = time.time() - start
If using a TEMPLATE_CONTEXT_PROCESSOR
is the best option.
How would I get the whole page loading time from there, instead of just getting the template loading time?
UPDATE:
As the initial question doesn't seem to be clear enough, here is an approach of what would be the Python version of what I want to do.
#!/usr/bin/env python import cgitb; cgitb.enable() import time print 'Content-type: text/html\n\n' start = time.time() print '<html>' print '<head>' print '</head>' print '<body>' print '<div>HEADER</div>' print '<div>' print '<p>Welcome to my Django Webpage!</p>' print '<p>Welcome to my Django Webpage!</p>' print '<p>Welcome to my Django Webpage!</p>' print '</div>' time.sleep(3) loadingtime = time.time() - start print '<div>It took ',loadingtime,' seconds to load the page</div>' print '</body>' print '</html>'
You can create a custom middleware to log this. Here is how I create a middleware to achieve this purpose base on http://djangosnippets.org/snippets/358/ (I modified the code a bit).
Firstly, assuming your project has a name: test_project
, create a file name middlewares.py
, I place it in the same folder as settings.py
:
from django.db import connection from time import time from operator import add import re class StatsMiddleware(object): def process_view(self, request, view_func, view_args, view_kwargs): ''' In your base template, put this: <div id="stats"> <!-- STATS: Total: %(total_time).2fs Python: %(python_time).2fs DB: %(db_time).2fs Queries: %(db_queries)d ENDSTATS --> </div> ''' # Uncomment the following if you want to get stats on DEBUG=True only #if not settings.DEBUG: # return None # get number of db queries before we do anything n = len(connection.queries) # time the view start = time() response = view_func(request, *view_args, **view_kwargs) total_time = time() - start # compute the db time for the queries just run db_queries = len(connection.queries) - n if db_queries: db_time = reduce(add, [float(q['time']) for q in connection.queries[n:]]) else: db_time = 0.0 # and backout python time python_time = total_time - db_time stats = { 'total_time': total_time, 'python_time': python_time, 'db_time': db_time, 'db_queries': db_queries, } # replace the comment if found if response and response.content: s = response.content regexp = re.compile(r'(?P<cmt><!--\s*STATS:(?P<fmt>.*?)ENDSTATS\s*-->)') match = regexp.search(s) if match: s = (s[:match.start('cmt')] + match.group('fmt') % stats + s[match.end('cmt'):]) response.content = s return response
Secondly, modify settings.py
to add your middleware:
MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', # ... your existing middlewares ... # your custom middleware here 'test_project.middlewares.StatsMiddleware', )
Note: you have to add the full path to your middleware class like above, the format is:
<project_name>.<middleware_file_name>.<middleware_class_name>
A second note is I added this middleware to the end of the list because I just want to log the template load time alone. If you want to log the load time of templates + all middlewares, please put it in the beginning of MIDDLEWARE_CLASSES
list (credits to @Symmitchry).
Back to the main topic, the next step is to modify your base.html
or whatever pages you want to log load time, add this:
<div id="stats"> <!-- STATS: Total: %(total_time).2fs Python: %(python_time).2fs DB: %(db_time).2fs Queries: %(db_queries)d ENDSTATS --> </div>
Note: you can name the <div id="stats">
and use CSS for that div however you want, but DON'T change the comment <!-- STATS: .... -->
. If you want to change it, be sure that you test it against the regex pattern in the created middlewares.py
.
Voila, enjoy the statistics.
EDIT:
For those who use CBVs (Class Based Views) a lot, you might have encountered the error ContentNotRenderedError
with above solution. Have no fear, here is the fix in middlewares.py
:
# replace the comment if found if response: try: # detects TemplateResponse which are not yet rendered if response.is_rendered: rendered_content = response.content else: rendered_content = response.rendered_content except AttributeError: # django < 1.5 rendered_content = response.content if rendered_content: s = rendered_content regexp = re.compile( r'(?P<cmt><!--\s*STATS:(?P<fmt>.*?)ENDSTATS\s*-->)' ) match = regexp.search(s) if match: s = (s[:match.start('cmt')] + match.group('fmt') % stats + s[match.end('cmt'):]) response.content = s return response
I got it working with Django 1.6.x, if you have problem with other version of Django, please ping me in comment section.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With