Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Django's request/response cycle - Django Under The Hood 2015

Django's request/response cycle - Django Under The Hood 2015

Jacob Kaplan-Moss

November 06, 2015
Tweet

More Decks by Jacob Kaplan-Moss

Other Decks in Technology

Transcript

  1. What’s a view? “A view function… is simply a Python

    function that takes a Web request and returns a Web response.” — https://docs.djangoproject.com/en/1.9/topics/http/views/ def my_view(request): return HttpResponse(“it worked!”)
  2. How does Django know to call my view? “A URLconf…

    is a simple mapping between URL patterns (simple regular expressions) to Python functions (your views).” — https://docs.djangoproject.com/en/1.9/topics/http/urls/ patterns = [ url(‘^view/$’, my_view), ... ]
  3. The Internet Your View But what the heck happens in

    here? GET /view/ HttpRequest HttpResponse 200 OK
  4. Extension point “W”: WSGI middleware Use cases: • wrap the

    entire site in some behavior
 (much code in Django happens “unprotected” by Django middleware) • middleware that’s not Django-specific 
 (most doesn’t need to be…) Examples: • https://github.com/dahlia/wsgi-oauth2 • https://github.com/evansd/whitenoise from django.core.wsgi import get_wsgi_application from wsgioauth2 import github application = get_wsgi_application() client = github.make_client(client_id='...', client_secret='...') application = client.wsgi_middleware(application, secret='...')
  5. Extension point “M”: Django middleware Use cases: do something on

    every request — with more Django- specific context than WSGI middleware. Careful: Middleware are classic Django foot-gun! Examples: • https://github.com/carljm/django-secure • https://github.com/django-debug-toolbar/django-debug-toolbar
  6. Django middleware example (django-secure) class SecurityMiddleware(object): def __init__(self): self.xss_filter =

    conf.SECURE_BROWSER_XSS_FILTER ... def process_request(self, request): ... path = request.path.lstrip("/") if (self.redirect and not request.is_secure() and not any(pattern.search(path) for pattern in self.redirect_exempt)): host = self.redirect_host or request.get_host() return HttpResponsePermanentRedirect( "https://%s%s" % (host, request.get_full_path())) def process_response(self, request, response): ... if self.xss_filter and not 'x-xss-protection' in response: response["x-xss-protection"] = "1; mode=block" return response ★ ★ ★
  7. WSGI middleware vs Django middleware + Works with other Python

    frameworks, not just Django. + Limited access to other parts of the stack (e.g. databases). - More confusing API. - Hard to interoperate with Django-specific parts of 
 your app. + Simple API. + Easy integration with the rest of your app. - Django-specific. - Easy to mess up and introduce performance problems. - Misleading name.
  8. Extension point “S”: signals Use case: I’m… not sure, exactly.

    Timing data? import time from django.core import signal from django.dispatch import receiver @receiver(signals.request_started): def started(sender, **kwargs): print("at=request_started time={0} path={1}".format( time.time(), sender.environ['PATH_INFO'])) @receiver(signals.request_finished) def ended(sender, **kwargs): print("at=request_finished time={0} path={1}".format( time.time(), sender.environ['PATH_INFO'])) Yeah, silly example. But seriously, I don’t really know why you’d use signals — either middleware form works better.
  9. Extension point “U”: request-specific urls Use cases: •Modifying URLs based

    on request details: multi- tenancy, internationalization, … •Probably under-used! class MTMiddleware(object): def process_request(self, request): request.tenant = lookup_tenant(request) request.urlconf = ‘mt.urls.%s’ % request.tenant.slug ★
  10. Extension point “L”: lazy responses Use cases: • Defer response

    rendering until “later” in the response cycle. • Better support more complex composed views. • Allow composed views or middleware to inspect/modify 
 template/context details. Note: lazy responses are duck-typed! def non_lazy(request): ... content = templ.render(context) return HttpResponse(content) def lazy(request): ... return TemplateResponse(request, tmpl, context)
  11. It’s a very elegant architecture you’ve got there. Be a

    shame if something were to happen to it… https://github.com/andrewgodwin/channels
  12. Channels The Internet Your View GET /view/ HttpRequest HttpResponse 200

    OK Django, today Django, tomorrow The Internet Your View Websocket Channels
  13. Generating diagrams with seqdiag (http://blockdiag.com/en/seqdiag/index.html) seqdiag { client -> server

    [label = "GET /resource"]; server => database [label = "SELECT * FROM table;"]; client <-- server [label = "{ ... json ... }"]; }
  14. {% extends "parts/base.jinja" %} {% block content %} === initial

    server load === {% include "parts/initial-server-load.jinja" %} === request begins === server -> app [label = "__call__(env,\nstart_response)"]; {% include "parts/first-request.jinja" %} {% include "parts/request-phase.jinja" %} ... {% endblock %} %.diag: %.jinja $(jinja2) $< data.json > $@ png/%.png: %.diag $(seqdiag) -T png --antialias --no-transparency $< -o $@