SlideShare a Scribd company logo
1 of 133
Download to read offline
Behind the curtain
How Django handles a request
Daniel Hepper
About this tutorial
• not a top-down Django tutorial

• for people who want to get to the bottom of things
What we'll cover
• what is Django?

• dissecting the tiniest Django web app

• the same app, but more complicated
About me
Daniel Hepper

• degree in computer science

• freelancing for 7 years

• first contact with Django
around 0.95

• NOT a core developer

• varying amounts beard
What is Django
The web framework for
perfectionists with deadlines
Django makes it easier to build better Web apps
more quickly and with less code.
Web apps
• web app

• Django project

• django-admin.py startproject

• Django app

• module, part of an web app

• might be re-usable: re-usable app

• python manage.py startapp
Web Framework
Web Framework
• you take things from a library,

you put something in a framework

• "Don't call us, we call you"

• But you have to tell us your number

you have to provide an entrypoint
Settings
Django's entrypoint
Web Framework
Web Framework
• turn HTTP requests into HTTP response
Browser Cloud
HTTP Request
HTTP Response
HTTP Client
HTTP Request
HTTP Response
HTTP Server
HTTP-Request & Reponse
GET / HTTP/1.1
Host: 127.0.0.1:8000
User-Agent: curl/7.54.0
Accept: */*
HTTP/1.0 200 OK
Date: Sat, 26 May 2018 07:27:52 GMT
Server: WSGIServer/0.2 CPython/3.6.4
Content-Type: text/html; charset=utf-8
Hello world
HTTP Client
HTTP
HTTP Server Django
?
HTTP Client
HTTP
HTTP Server Django
WSGI
Browser
HTTP
Gunicorn Django
WSGI
Browser
HTTP
Nginx Django
WSGI
Gunicorn
HTTP
Browser
HTTP
Nginx Django
WSGI
uwsgi
uwsgi
WSGI
• Defined in PEP-333 / PEP-3333

• "A [...] standard interface between web servers and
Python web applications or frameworks, to promote web
application portability across a variety of web servers."

• "WSGI is a tool for framework and server developers"
WSGI
• servers

• applications

• middleware
WSGI Application
• Callable that accepts two arguments

• a function, method, class, instance with a __call__ method

• result = application(environ, start_response)
• environ: dictionary object, containing CGI-style environment variables

• start_response: callable, accepting two required positional
arguments, third optional

• def start_response(status, response_headers,
exc_info=None):
• When called by the server, the application object must return an iterable
yielding zero or more bytestrings
Hello WSGI
HELLO_WORLD = b"Hello world!n"
def simple_app(environ, start_response):
"""Simplest possible application object"""
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers)
return [HELLO_WORLD]
The web framework for
perfectionists with deadlines
Django makes it easier to build better Web apps
more quickly and with less code.
WSGI
Let's create a
Django project
(venv)$ pip install django==2.0.5
(venv)$ django-admin.py startproject default
(venv)$ tree default
default
├── manage.py
└── default
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
(venv)$ cat default/default/settings.py
"""
Django settings for mysite project...
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '-^oi_8%de()$d3qqa_4fgwds#&nlfn2(j-@_hu(j-m7-+-@q@p'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
...
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
...
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'default.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
"Django is too complicated!"
Web development is
complicated
Django offers reasonable defaults

to make your life easier
Let's simplify
All you need is...
• a view

• an URLConf

• settings

• a WSGI application object
https://github.com/consideratecode/django-behind-the-curtain/
views.py
from django.http import HttpResponse
def hello(request):
return HttpResponse('Hello world')
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views)
]
settings.py
SECRET_KEY = 'not-so-secret-123%^&'
ROOT_URLCONF = 'simple.urls'
wsgi.py
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault(
"DJANGO_SETTINGS_MODULE",
"simple.settings"
)
application = get_wsgi_application()
Running a
Django web application
• job of a WSGI server

• two phases

• import the application object

• call the application object for each request
A simple WSGI server
from wsgiref.simple_server import make_server
from simple.wsgi import application
httpd = make_server('127.0.0.1', 8000, application)
print("Serving HTTP on port 8000...")
httpd.handle_request()
(venv)$ tree simple
.
├── server.py
└── simple
├── __init__.py
├── __pycache__
├── settings.py
├── urls.py
├── views.py
└── wsgi.py
DEMO
Behind the curtain
Starting the
application
Starting the
application
import os
from django.core.wsgi import
get_wsgi_application
os.environ.setdefault(
"DJANGO_SETTINGS_MODULE",
"simple.settings"
)
application = get_wsgi_application()
wsgi.py
import django
from django.core.handlers.wsgi import WSGIHandler
def get_wsgi_application():
"""
The public interface to Django's WSGI support.
Return a WSGI callable.
Avoids making django.core.handlers.WSGIHandler a public
API, in case the internal WSGI implementation changes or
moves in the future.
"""
django.setup(set_prefix=False)
return WSGIHandler()
django/core/wsgi.py
from django.utils.version import get_version
VERSION = (2, 0, 0, 'final', 0)
__version__ = get_version(VERSION)
def setup(set_prefix=True):
"""
Configure the settings (this happens as a side effect of accessing the
first setting), configure logging and populate the app registry.
Set the thread-local urlresolvers script prefix if `set_prefix` is True.
"""
from django.apps import apps
from django.conf import settings
from django.urls import set_script_prefix
from django.utils.log import configure_logging
configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)
if set_prefix:
set_script_prefix(
'/'
if settings.FORCE_SCRIPT_NAME is None
else settings.FORCE_SCRIPT_NAME
)
apps.populate(settings.INSTALLED_APPS)
django/__init__.py
[...]
class LazySettings(LazyObject):
"""
A lazy proxy for either global Django settings or a custom settings
object. The user can manually configure settings prior to using
them. Otherwise,Django uses the settings module pointed to by
DJANGO_SETTINGS_MODULE.
"""
def __getattr__(self, name):
"""
Return the value of a setting and cache it in self.__dict__.
"""
if self._wrapped is empty:
self._setup(name)
val = getattr(self._wrapped, name)
self.__dict__[name] = val
return val
[...]
settings = LazySettings()
django/conf/__init__.py
[...]
class LazySettings(LazyObject):
"""
A lazy proxy for either global Django settings or a custom settings
object. The user can manually configure settings prior to using
them. Otherwise,Django uses the settings module pointed to by
DJANGO_SETTINGS_MODULE.
"""
def _setup(self, name=None):
"""
Load the settings module pointed to by the environment
variable. This is used the first time settings are needed, if
the user hasn't configured settings manually.
"""
settings_module = os.environ.get(ENVIRONMENT_VARIABLE)
if not settings_module:
desc = ("setting %s" % name) if name else "settings"
raise ImproperlyConfigured(...)
self._wrapped = Settings(settings_module)
[...]
settings = LazySettings()
django/conf/__init__.py
[...]
class Settings:
def __init__(self, settings_module):
for setting in dir(global_settings):
if setting.isupper():
setattr(self, setting,
getattr(global_settings, setting))
[...]
mod = importlib.import_module(self.SETTINGS_MODULE)
self._explicit_settings = set()
for setting in dir(mod):
if setting.isupper():
setting_value = getattr(mod, setting)
[...]
setattr(self, setting, setting_value)
[...]
[...]
settings = LazySettings()
django/conf/__init__.py
from django.utils.version import get_version
VERSION = (2, 0, 0, 'final', 0)
__version__ = get_version(VERSION)
def setup(set_prefix=True):
"""
Configure the settings (this happens as a side effect of accessing the
first setting), configure logging and populate the app registry.
Set the thread-local urlresolvers script prefix if `set_prefix` is True.
"""
from django.apps import apps
from django.conf import settings
from django.urls import set_script_prefix
from django.utils.log import configure_logging
configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)
if set_prefix:
set_script_prefix(
'/'
if settings.FORCE_SCRIPT_NAME is None
else settings.FORCE_SCRIPT_NAME
)
apps.populate(settings.INSTALLED_APPS)
django/__init__.py
import django
from django.core.handlers.wsgi import WSGIHandler
def get_wsgi_application():
"""
The public interface to Django's WSGI support.
Return a WSGI callable.
Avoids making django.core.handlers.WSGIHandler a public
API, in case the internal WSGI implementation changes or
moves in the future.
"""
django.setup(set_prefix=False)
return WSGIHandler()
django/core/wsgi.py
class WSGIHandler(base.BaseHandler):
request_class = WSGIRequest
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.load_middleware()
def __call__(self, environ, start_response):
[...]
django/core/handlers/wsgi.py
class BaseHandler:
[...]
def load_middleware(self):
"""
Populate middleware lists from settings.MIDDLEWARE.
"""
self._request_middleware = []
self._view_middleware = []
self._template_response_middleware = []
self._response_middleware = []
self._exception_middleware = []
handler = convert_exception_to_response(
self._get_response)
for middleware_path in reversed(settings.MIDDLEWARE):
[...]
self._middleware_chain = handler
django/core/handlers/base.py
class WSGIHandler(base.BaseHandler):
[...]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.load_middleware()
def __call__(self, environ, start_response):
[...]
django/core/handlers/wsgi.py
import django
from django.core.handlers.wsgi import WSGIHandler
def get_wsgi_application():
"""
The public interface to Django's WSGI support.
Return a WSGI callable.
Avoids making django.core.handlers.WSGIHandler a public
API, in case the internal WSGI implementation changes or
moves in the future.
"""
django.setup(set_prefix=False)
return WSGIHandler()
django/core/wsgi.py
import os
from django.core.wsgi import
get_wsgi_application
os.environ.setdefault(
"DJANGO_SETTINGS_MODULE",
"simple.settings"
)
application = get_wsgi_application()
wsgi.py
Handling a request
WSGI Application
• Callable that accepts two arguments

• a function, method, class, instance with a __call__ method

• result = application(environ, start_response)
• environ: dictionary object, containing CGI-style environment variables

• start_response: callable, accepting two required positional
arguments, third optional

• def start_response(status, response_headers,
exc_info=None):
• When called by the server, the application object must return an iterable
yielding zero or more bytestrings
class WSGIHandler(base.BaseHandler):
request_class = WSGIRequest
[...]
def __call__(self, environ, start_response):
set_script_prefix(get_script_name(environ))
signals.request_started.send(sender=self.__class__,
environ=environ)
request = self.request_class(environ)
response = self.get_response(request)
[...]
django/core/handlers/wsgi.py
class WSGIRequest(HttpRequest):
def __init__(self, environ):
[...]
django/core/handlers/wsgi.py
class WSGIRequest(HttpRequest):
def __init__(self, environ):
[...]
django/core/handlers/wsgi.py
class HttpRequest:
"""A basic HTTP request."""
# The encoding used in GET/POST dicts. None means use default
# setting.
_encoding = None
_upload_handlers = []
def __init__(self):
# WARNING: The `WSGIRequest` subclass doesn't call
# `super`.
# Any variable assignment made here should also happen in
# `WSGIRequest.__init__()`.
[...]
django/core/http/request.py
class WSGIRequest(HttpRequest):
def __init__(self, environ):
script_name = get_script_name(environ)
path_info = get_path_info(environ)
if not path_info:
# Sometimes PATH_INFO exists, but is empty (e.g.
# accessing the SCRIPT_NAME URL without a
# slash). We really need to operate as if they'd
# requested '/'. Not amazingly nice to force
# the path like this, but should be harmless.
path_info = '/'
[...]
django/core/handlers/wsgi.py
path vs.
script_name & path_info
• http://mydjangoproject.com/

• http://mydjangoproject.com/polls/

• http://mydjangoprojects.com/project1/

• http://mydjangoprojects.com/project2/polls/

• http://mydjangoprojects.com/project3
class WSGIRequest(HttpRequest):
def __init__(self, environ):
script_name = get_script_name(environ)
path_info = get_path_info(environ)
if not path_info:
# Sometimes PATH_INFO exists, but is empty (e.g.
# accessing the SCRIPT_NAME URL without a
# slash). We really need to operate as if they'd
# requested '/'. Not amazingly nice to force
# the path like this, but should be harmless.
path_info = '/'
[...]
def get_script_name(environ):
"""
Return the equivalent of the HTTP request's SCRIPT_NAME
environment variable. If Apache mod_rewrite is used, return
what would have been the script name prior to any rewriting
(so it's the script name as seen from the client's
perspective), unless the FORCE_SCRIPT_NAME setting is
set (to anything).
"""
[...]
django/core/handlers/wsgi.py
class WSGIRequest(HttpRequest):
def __init__(self, environ):
script_name = get_script_name(environ)
path_info = get_path_info(environ)
if not path_info:
# Sometimes PATH_INFO exists, but is empty (e.g.
# accessing the SCRIPT_NAME URL without a
# slash). We really need to operate as if they'd
# requested '/'. Not amazingly nice to force
# the path like this, but should be harmless.
path_info = '/'
[...]
def get_path_info(environ):
"""Return the HTTP request's PATH_INFO as a string."""
path_info = get_bytes_from_wsgi(environ, 'PATH_INFO', '/')
return repercent_broken_unicode(path_info).decode()
django/core/handlers/wsgi.py
class WSGIRequest(HttpRequest):
def __init__(self, environ):
[...]
self.environ = environ
self.path_info = path_info
# be careful to only replace the first slash in the path
# because of http://test/something and
# http://test//something being different as
# stated in http://www.ietf.org/rfc/rfc2396.txt
self.path = '%s/%s' % (script_name.rstrip('/'),
path_info.replace('/', '', 1))
[...]
django/core/handlers/wsgi.py
class WSGIRequest(HttpRequest):
def __init__(self, environ):
[...]
self.META = environ
self.META['PATH_INFO'] = path_info
self.META['SCRIPT_NAME'] = script_name
self.method = environ['REQUEST_METHOD'].upper()
[...]
django/core/handlers/wsgi.py
class WSGIRequest(HttpRequest):
def __init__(self, environ):
[...]
self.content_type, self.content_params = 
cgi.parse_header(environ.get('CONTENT_TYPE', ''))
if 'charset' in self.content_params:
try:
codecs.lookup(self.content_params['charset'])
except LookupError:
pass
else:
self.encoding = self.content_params['charset']
[...]
django/core/handlers/wsgi.py
class WSGIRequest(HttpRequest):
def __init__(self, environ):
[...]
self._post_parse_error = False
try:
content_length = int(environ.get('CONTENT_LENGTH'))
except (ValueError, TypeError):
content_length = 0
self._stream = LimitedStream(self.environ['wsgi.input'],
content_length)
self._read_started = False
self.resolver_match = None
django/core/handlers/wsgi.py
class WSGIRequest(HttpRequest):
def __init__(self, environ):
[...]
self._post_parse_error = False
try:
content_length = int(environ.get('CONTENT_LENGTH'))
except (ValueError, TypeError):
content_length = 0
self._stream = LimitedStream(self.environ['wsgi.input'],
content_length)
self._read_started = False
self.resolver_match = None
class LimitedStream:
"""
Wrap another stream to disallow reading it past a number of
bytes.
"""
[...]
django/core/handlers/wsgi.py
class WSGIHandler(base.BaseHandler):
request_class = WSGIRequest
[...]
def __call__(self, environ, start_response):
set_script_prefix(get_script_name(environ))
signals.request_started.send(sender=self.__class__,
environ=environ)
request = self.request_class(environ)
response = self.get_response(request)
[...]
django/core/handlers/wsgi.py
class BaseHandler:
[...]
def get_response(self, request):
"""
Return an HttpResponse object for the given
HttpRequest.
"""
# Setup default url resolver for this thread
set_urlconf(settings.ROOT_URLCONF)
response = self._middleware_chain(request)
django/core/handlers/base.py
# Overridden URLconfs for each thread are stored here.
_urlconfs = local()
[...]
def set_urlconf(urlconf_name):
"""
Set the URLconf for the current thread (overriding the
default one in settings). If urlconf_name is None, revert
back to the default.
"""
if urlconf_name:
_urlconfs.value = urlconf_name
else:
if hasattr(_urlconfs, "value"):
del _urlconfs.value
django/urls/base.py
class BaseHandler:
[...]
def get_response(self, request):
"""
Return an HttpResponse object for the given
HttpRequest.
"""
# Setup default url resolver for this thread
set_urlconf(settings.ROOT_URLCONF)
response = self._middleware_chain(request)
django/core/handlers/base.py
class BaseHandler:
def load_middleware(self):
"""
Populate middleware lists from settings.MIDDLEWARE.
"""
handler = convert_exception_to_response(
self._get_response
)
[...]
self._middleware_chain = handler
def get_response(self, request):
"""
Return an HttpResponse object for the given
HttpRequest.
"""
# Setup default url resolver for this thread
set_urlconf(settings.ROOT_URLCONF)
response = self._middleware_chain(request)
django/core/handlers/base.py
class BaseHandler:
[...]
def _get_response(self, request):
"""
Resolve and call the view,
then apply view, exception, and template_response
middleware.
This method is everything that happens
inside the request/response middleware.
"""
[...]
django/core/handlers/base.py
class BaseHandler:
[...]
def _get_response(self, request):
"""
Resolve and call the view,
then apply view, exception, and template_response
middleware.
This method is everything that happens
inside the request/response middleware.
"""
response = None
if hasattr(request, 'urlconf'):
urlconf = request.urlconf
set_urlconf(urlconf)
resolver = get_resolver(urlconf)
else:
resolver = get_resolver()
resolver_match = resolver.resolve(request.path_info)
callback, callback_args, callback_kwargs = resolver_match
request.resolver_match = resolver_match
django/core/handlers/base.py
class BaseHandler:
[...]
def _get_response(self, request):
[...]
callback, callback_args, callback_kwargs = resolver_match
[...]
# Apply view middleware
for middleware_method in self._view_middleware:
response = middleware_method(request, callback, ...)
if response:
break
[...]
django/core/handlers/base.py
class BaseHandler:
[...]
def _get_response(self, request):
[...]
callback, callback_args, callback_kwargs = resolver_match
[...]
# Apply view middleware
[...]
if response is None:
wrapped_callback = self.make_view_atomic(callback)
try:
response = wrapped_callback(
request, *callback_args, **callback_kwargs
)
except Exception as e:
response = self.process_exception_by_middleware(
e, request
)
django/core/handlers/base.py
class BaseHandler:
[...]
def _get_response(self, request):
[...]
callback, callback_args, callback_kwargs = resolver_match
[...]
# Apply view middleware
[...]
response = wrapped_callback(
request, *callback_args, **callback_kwargs
)
# Complain if the view returned None (a common error).
[...]
# If the response supports deferred rendering, apply
# template response middleware and then render the response
[...]
return response
django/core/handlers/base.py
class BaseHandler:
def load_middleware(self):
"""
Populate middleware lists from settings.MIDDLEWARE.
"""
handler = convert_exception_to_response(
self._get_response
)
[...]
self._middleware_chain = handler
def get_response(self, request):
"""
Return an HttpResponse object for the given
HttpRequest.
"""
# Setup default url resolver for this thread
set_urlconf(settings.ROOT_URLCONF)
response = self._middleware_chain(request)
django/core/handlers/base.py
class BaseHandler:
[...]
def get_response(self, request):
[...]
response = self._middleware_chain(request)
response._closable_objects.append(request)
[...]
return response
django/core/handlers/base.py
class HttpResponseBase:
[...]
# The WSGI server must call this method upon completion of
# the request.
# See http://blog.dscpl.com.au/2012/10/obligations-for-calling-close
def close(self):
for closable in self._closable_objects:
try:
closable.close()
except Exception:
pass
self.closed = True
signals.request_finished.send(sender=self._handler_class)
django/http/response.py
class BaseHandler:
[...]
def get_response(self, request):
[...]
response = self._middleware_chain(request)
response._closable_objects.append(request)
# If the exception handler returns a TemplateResponse
# that has not been rendered, force it to be rendered.
if not getattr(response, 'is_rendered', True) and
callable(getattr(response, 'render', None)):
response = response.render()
[...]
return response
django/core/handlers/base.py
class BaseHandler:
[...]
def get_response(self, request):
[...]
response = self._middleware_chain(request)
response._closable_objects.append(request)
# If the exception handler returns a TemplateResponse
# that has not been rendered, force it to be rendered.
if not getattr(response, 'is_rendered', True) and
callable(getattr(response, 'render', None)):
response = response.render()
if response.status_code == 404:
logger.warning(
'Not Found: %s', request.path,
extra={'status_code': 404, 'request': request})
return response
django/core/handlers/base.py
class WSGIHandler(base.BaseHandler):
request_class = WSGIRequest
[...]
def __call__(self, environ, start_response):
set_script_prefix(get_script_name(environ))
signals.request_started.send(sender=self.__class__,
environ=environ)
request = self.request_class(environ)
response = self.get_response(request)
[...]
django/core/handlers/wsgi.py
class WSGIHandler(base.BaseHandler):
request_class = WSGIRequest
[...]
def __call__(self, environ, start_response):
[...]
response = self.get_response(request)
response._handler_class = self.__class__
status = '%d %s' % (response.status_code, response.reason_phrase
response_headers = list(response.items())
for c in response.cookies.values():
response_headers.append(('Set-Cookie', c.output(header='')))
start_response(status, response_headers)
if (getattr(response, 'file_to_stream', None) is not None
and environ.get('wsgi.file_wrapper')):
response = environ['wsgi.file_wrapper'](response.file_to_str
return response
django/core/handlers/wsgi.py
Middleware
Middleware
power
Django
Default
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Function based middleware
def simple_middleware(get_response):
# One-time configuration and initialization.
def middleware(request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
return middleware
Class based middleware
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
Additional hooks
• process_view(request, view_func, view_args, view_kwargs)
• process_exception(request, exception)
• process_template_response(request, response)
Pre-Django 1.10-style
middleware
• process_request()
• process_response()
• django.utils.deprecation.MiddlewareMixin
Using middleware
class Middleware1:
def __init__(self, get_response):
print('> Middleware1.__init__')
self.get_response = get_response
def __call__(self, request):
print('> Middleware1.__call__')
response = self.get_response(request)
print('< Middleware1.__call__')
return response
def process_view(self, request, view_func, view_args,
view_kwargs):
print('> Middleware1.process_view')
return None
def process_exception(self, request, exception):
print('> Middleware1.process_exception')
return None
def process_template_response(self, request, response):
print('> Middleware1.process_template_response')
return response
middleware/middleware.py
SECRET_KEY = 'not-so-secret-123%^&'
ROOT_URLCONF = 'middleware.urls'
MIDDLEWARE = [
'middleware.middleware.Middleware1',
'middleware.middleware.Middleware2',
'middleware.middleware.Middleware3',
]
middleware/settings.py
Starting the
application
$ python server.py
> Middleware3.__init__
> Middleware2.__init__
> Middleware1.__init__
Serving HTTP on port 8000...
class BaseHandler:
[...]
def load_middleware(self):
"""
Populate middleware lists from settings.MIDDLEWARE.
"""
self._request_middleware = []
self._view_middleware = []
self._template_response_middleware = []
self._response_middleware = []
self._exception_middleware = []
handler = convert_exception_to_response(self._get_response)
for middleware_path in reversed(settings.MIDDLEWARE):
[...]
self._middleware_chain = handler
django/core/handlers/base.py
class BaseHandler:
[...]
def load_middleware(self):
"""
Populate middleware lists from settings.MIDDLEWARE.
"""
[...]
handler = convert_exception_to_response(self._get_response)
for middleware_path in reversed(settings.MIDDLEWARE):
middleware = import_string(middleware_path)
try:
mw_instance = middleware(handler)
except MiddlewareNotUsed as exc:
if settings.DEBUG:
[...]
continue
[...]
handler = convert_exception_to_response(mw_instance)
self._middleware_chain = handler
django/core/handlers/base.py
settings.MIDDLEWARE = [
'middleware.middleware.Middleware1',
'middleware.middleware.Middleware2',
'middleware.middleware.Middleware3',
]
handler = convert_exception_to_response(self._get_response)
for middleware_path in reversed(settings.MIDDLEWARE):
middleware = import_string(middleware_path)
mw_instance = middleware(handler)
handler = convert_exception_to_response(mw_instance)
handler = convert_exception_to_response(self._get_response)
handler = convert_exception_to_response(Middleware3(handler))
handler = convert_exception_to_response(Middleware2(handler))
handler = convert_exception_to_response(Middleware1(handler))
settings.MIDDLEWARE = [
'middleware1',
'middleware2',
'middleware3',
]
handler = convert_exception_to_response(self._get_response)
for middleware_path in reversed(settings.MIDDLEWARE):
middleware = import_string(middleware_path)
mw_instance = middleware(handler)
handler = convert_exception_to_response(mw_instance)
handler = middleware1(middleware2(middleware3(self._get_response)))))))
class BaseHandler:
[...]
def load_middleware(self):
"""
Populate middleware lists from settings.MIDDLEWARE.
"""
[...]
handler = convert_exception_to_response(self._get_response)
for middleware_path in reversed(settings.MIDDLEWARE):
middleware = import_string(middleware_path)
try:
mw_instance = middleware(handler)
except MiddlewareNotUsed as exc:
if settings.DEBUG:
[...]
continue
[...]
handler = convert_exception_to_response(mw_instance)
self._middleware_chain = handler
django/core/handlers/base.py
class BaseHandler:
[...]
def load_middleware(self):
[...]
for middleware_path in reversed(settings.MIDDLEWARE):
[...]
if hasattr(mw_instance, 'process_view'):
self._view_middleware.insert(
0, mw_instance.process_view)
if hasattr(mw_instance, 'process_template_response'):
self._template_response_middleware.append(
mw_instance.process_template_response)
if hasattr(mw_instance, 'process_exception'):
self._exception_middleware.append(
mw_instance.process_exception)
handler = convert_exception_to_response(mw_instance)
self._middleware_chain = handler
django/core/handlers/base.py
settings.MIDDLEWARE = [
'middleware1',
'middleware2',
'middleware3',
]
self._view_middleware = [
middleware1_instance.process_view,
middleware2_instance.process_view,
middleware3_instance.process_view,
]
self._template_response_middleware = [
middleware3_instance.process_template_response,
middleware2_instance.process_template_response,
middleware1_instance.process_template_response,
]
self._exception_middleware = [
middleware3_instance.process_exception,
middleware2_instance.process_exception,
middleware1_instance.process_exception,
]
• DIAGRAM
Handling a request
$ python server.py
> Middleware3.__init__
> Middleware2.__init__
> Middleware1.__init__
Serving HTTP on port 8000...
> Middleware1.__call__
> Middleware2.__call__
> Middleware3.__call__
> Middleware1.process_view
> Middleware2.process_view
> Middleware3.process_view
< Middleware3.__call__
< Middleware2.__call__
< Middleware1.__call__
class BaseHandler:
def load_middleware(self):
"""
Populate middleware lists from settings.MIDDLEWARE.
"""
[...]
def get_response(self, request):
"""
Return an HttpResponse object for the given
HttpRequest.
"""
# Setup default url resolver for this thread
set_urlconf(settings.ROOT_URLCONF)
response = self._middleware_chain(request)
middleware1(middleware2(middleware3(self._get_response)))))))
django/core/handlers/base.py
Middleware1
Middleware2
__call__
Middleware3
__call__
BaseHandler
_get_response
class BaseHandler:
[...]
def _get_response(self, request):
"""
Resolve and call the view, then apply view, exception, and
template_response middleware. This method is everything
that happen inside the request/response middleware.
"""
response = None
[...]
return response
django/core/handlers/base.py
class BaseHandler:
[...]
def _get_response(self, request):
"""
Resolve and call the view, then apply view, exception, and
template_response middleware. This method is everything
that happen inside the request/response middleware.
"""
response = None
if hasattr(request, 'urlconf'):
urlconf = request.urlconf
set_urlconf(urlconf)
resolver = get_resolver(urlconf)
else:
resolver = get_resolver()
[...]
return response
django/core/handlers/base.py
[...]
@functools.lru_cache(maxsize=None)
def get_resolver(urlconf=None):
if urlconf is None:
from django.conf import settings
urlconf = settings.ROOT_URLCONF
return URLResolver(RegexPattern(r'^/'), urlconf)
[...]
django/urls/resolvers/base.py
class BaseHandler:
[...]
def _get_response(self, request):
"""
Resolve and call the view, then apply view, exception, and
template_response middleware. This method is everything
that happen inside the request/response middleware.
"""
[...]
resolver_match = resolver.resolve(request.path_info)
callback, callback_args, callback_kwargs = resolver_match
request.resolver_match = resolver_match
[...]
return response
django/core/handlers/base.py
class BaseHandler:
[...]
def _get_response(self, request):
"""
Resolve and call the view, then apply view, exception, and
template_response middleware. This method is everything
that happen inside the request/response middleware.
"""
[...]
callback, callback_args, callback_kwargs = resolver_match
[...]
# Apply view middleware
for middleware_method in self._view_middleware:
response = middleware_method(request,
callback,
callback_args,
callback_kwargs)
if response:
break
[...]
return response
django/core/handlers/base.py
Middleware1
Middleware2
__call__
Middleware3
__call__
BaseHandler
_get_response
process_view
process_view
process_view
class BaseHandler:
[...]
def _get_response(self, request):
"""
Resolve and call the view, then apply view, exception, and
template_response middleware. This method is everything
that happen inside the request/response middleware.
"""
[...]
if response is None:
wrapped_callback = self.make_view_atomic(callback)
try:
response = wrapped_callback(request,
*callback_args,
**callback_kwargs)
except Exception as e:
response = self.process_exception_by_middleware(
e, request)
[...]
return response
django/core/handlers/base.py
class BaseHandler:
[...]
def _get_response(self, request):
"""
Resolve and call the view, then apply view, exception, and
template_response middleware. This method is everything
that happen inside the request/response middleware.
"""
[...]
# Complain if the view returned None (a common error).
if response is None:
if isinstance(callback, types.FunctionType): # FBV
view_name = callback.__name__
else: # CBV
view_name = callback.__class__.__name__ + '.__call__'
raise ValueError(
"The view %s.%s didn't return an HttpResponse object. It
"returned None instead." % (callback.__module__, view_na
)
[...]
return response
django/core/handlers/base.py
class BaseHandler:
[...]
def _get_response(self, request):
"""
Resolve and call the view, then apply view, exception, and
template_response middleware. This method is everything
that happen inside the request/response middleware.
"""
[...]
# If the response supports deferred rendering, apply template
# response middleware and then render the response
elif hasattr(response, 'render') and callable(response.render):
for middleware_method in self._template_response_middleware:
response = middleware_method(request, response)
# Complain if the template response middleware
# returned None (a common error).
if response is None:
raise ValueError(...)
[...]
return response
django/core/handlers/base.py
class BaseHandler:
[...]
def _get_response(self, request):
"""
Resolve and call the view, then apply view, exception, and
template_response middleware. This method is everything
that happen inside the request/response middleware.
"""
[...]
# If the response supports deferred rendering, apply template
# response middleware and then render the response
elif hasattr(response, 'render') and callable(response.render):
[...]
try:
response = response.render()
except Exception as e:
response = self.process_exception_by_middleware(e, reque
return response
django/core/handlers/base.py
class BaseHandler:
[...]
def process_exception_by_middleware(self, exception, request):
"""
Pass the exception to the exception middleware.
If no middleware return a response for this exception,
raise it.
"""
for middleware_method in self._exception_middleware:
response = middleware_method(request, exception)
if response:
return response
raise
django/core/handlers/base.py
Middleware1
Middleware2
__call__
Middleware3
__call__
BaseHandler
_get_response
process_exception
process_exception
process_exception
class BaseHandler:
[...]
def _get_response(self, request):
"""
Resolve and call the view, then apply view, exception, and
template_response middleware. This method is everything
that happen inside the request/response middleware.
"""
[...]
# If the response supports deferred rendering, apply template
# response middleware and then render the response
elif hasattr(response, 'render') and callable(response.render):
[...]
try:
response = response.render()
except Exception as e:
response = self.process_exception_by_middleware(e, reque
return response
django/core/handlers/base.py
class BaseHandler:
[...]
def get_response(self, request):
"""
Return an HttpResponse object for the given
HttpRequest.
"""
# Setup default url resolver for this thread
set_urlconf(settings.ROOT_URLCONF)
response = self._middleware_chain(request)
[...]
return response
django/core/handlers/base.py
class BaseHandler:
[...]
def get_response(self, request):
[...]
response = self._middleware_chain(request)
[...]
return response
django/core/handlers/base.py
class BaseHandler:
[...]
def get_response(self, request):
[...]
response = self._middleware_chain(request)
response._closable_objects.append(request)
# If the exception handler returns a TemplateResponse
# that has not been rendered, force it to be rendered.
if not getattr(response, 'is_rendered', True) and
callable(getattr(response, 'render', None)):
response = response.render()
if response.status_code == 404:
logger.warning(
'Not Found: %s', request.path,
extra={'status_code': 404, 'request': request})
return response
django/core/handlers/base.py
class WSGIHandler(base.BaseHandler):
request_class = WSGIRequest
[...]
def __call__(self, environ, start_response):
[...]
response = self.get_response(request)
response._handler_class = self.__class__
status = '%d %s' % (response.status_code, response.reason_phrase
response_headers = list(response.items())
for c in response.cookies.values():
response_headers.append(('Set-Cookie', c.output(header='')))
start_response(status, response_headers)
if (getattr(response, 'file_to_stream', None) is not None
and environ.get('wsgi.file_wrapper')):
response = environ['wsgi.file_wrapper'](response.file_to_str
return response
django/core/handlers/wsgi.py
TIL
• Django is made by humans, not wizards

• Just code, no magic
Room for contribution
• Unused attributes _response_middleware and
_request_middleware in BaseHandler

• Wrong or misleading comments

• ExceptionMiddleware does not exist
Thanks
Daniel Hepper

@danielhepper

daniel@consideratecode.com

consideratecode.com
Image credits

• https://pixabay.com/en/interior-house-home-
curtain-2570933/

• https://unsplash.com/photos/MEW1f-yu2KI

• https://pixabay.com/en/rocket-launch-smoke-rocket-take-
off-67723/

• https://pixabay.com/en/gears-cogs-machine-
machinery-1236578/

• https://pixabay.com/en/balance-background-harmony-
stacked-3356546/

More Related Content

What's hot

An introduction to Python for absolute beginners
An introduction to Python for absolute beginnersAn introduction to Python for absolute beginners
An introduction to Python for absolute beginnersKálmán "KAMI" Szalai
 
Testing with Express, Mocha & Chai
Testing with Express, Mocha & ChaiTesting with Express, Mocha & Chai
Testing with Express, Mocha & ChaiJoerg Henning
 
Python Interview Questions And Answers
Python Interview Questions And AnswersPython Interview Questions And Answers
Python Interview Questions And AnswersH2Kinfosys
 
Introduction to Python Programming language.pptx
Introduction to Python Programming language.pptxIntroduction to Python Programming language.pptx
Introduction to Python Programming language.pptxBharathYusha1
 
How to add an optimization for C# to RyuJIT
How to add an optimization for C# to RyuJITHow to add an optimization for C# to RyuJIT
How to add an optimization for C# to RyuJITEgor Bogatov
 
Python programming
Python programmingPython programming
Python programmingMegha V
 
Python course syllabus
Python course syllabusPython course syllabus
Python course syllabusSugantha T
 
Introduction to Kotlin Language and its application to Android platform
Introduction to Kotlin Language and its application to Android platformIntroduction to Kotlin Language and its application to Android platform
Introduction to Kotlin Language and its application to Android platformEastBanc Tachnologies
 
Pf: the OpenBSD packet filter
Pf: the OpenBSD packet filterPf: the OpenBSD packet filter
Pf: the OpenBSD packet filterGiovanni Bechis
 
Introduction to Django
Introduction to DjangoIntroduction to Django
Introduction to DjangoKnoldus Inc.
 
Socket Programming In Python
Socket Programming In PythonSocket Programming In Python
Socket Programming In Pythondidip
 
Kotlin presentation
Kotlin presentation Kotlin presentation
Kotlin presentation MobileAcademy
 
Presentation on python
Presentation on pythonPresentation on python
Presentation on pythonwilliam john
 

What's hot (20)

Serving ML easily with FastAPI
Serving ML easily with FastAPIServing ML easily with FastAPI
Serving ML easily with FastAPI
 
An introduction to Python for absolute beginners
An introduction to Python for absolute beginnersAn introduction to Python for absolute beginners
An introduction to Python for absolute beginners
 
Testing with Express, Mocha & Chai
Testing with Express, Mocha & ChaiTesting with Express, Mocha & Chai
Testing with Express, Mocha & Chai
 
Design patterns in PHP
Design patterns in PHPDesign patterns in PHP
Design patterns in PHP
 
Python Interview Questions And Answers
Python Interview Questions And AnswersPython Interview Questions And Answers
Python Interview Questions And Answers
 
Final keyword
Final keywordFinal keyword
Final keyword
 
Introduction to Python Programming language.pptx
Introduction to Python Programming language.pptxIntroduction to Python Programming language.pptx
Introduction to Python Programming language.pptx
 
How to add an optimization for C# to RyuJIT
How to add an optimization for C# to RyuJITHow to add an optimization for C# to RyuJIT
How to add an optimization for C# to RyuJIT
 
Python programming
Python programmingPython programming
Python programming
 
Python course syllabus
Python course syllabusPython course syllabus
Python course syllabus
 
Introduction to Kotlin Language and its application to Android platform
Introduction to Kotlin Language and its application to Android platformIntroduction to Kotlin Language and its application to Android platform
Introduction to Kotlin Language and its application to Android platform
 
Pf: the OpenBSD packet filter
Pf: the OpenBSD packet filterPf: the OpenBSD packet filter
Pf: the OpenBSD packet filter
 
The Java memory model made easy
The Java memory model made easyThe Java memory model made easy
The Java memory model made easy
 
Introduction to Django
Introduction to DjangoIntroduction to Django
Introduction to Django
 
Socket Programming In Python
Socket Programming In PythonSocket Programming In Python
Socket Programming In Python
 
Final keyword in java
Final keyword in javaFinal keyword in java
Final keyword in java
 
Java- Nested Classes
Java- Nested ClassesJava- Nested Classes
Java- Nested Classes
 
Kotlin presentation
Kotlin presentation Kotlin presentation
Kotlin presentation
 
Presentation on python
Presentation on pythonPresentation on python
Presentation on python
 
NGINX Plus on AWS
NGINX Plus on AWSNGINX Plus on AWS
NGINX Plus on AWS
 

Similar to Behind the curtain - How Django handles a request

Django for mobile applications
Django for mobile applicationsDjango for mobile applications
Django for mobile applicationsHassan Abid
 
Testing in AngularJS
Testing in AngularJSTesting in AngularJS
Testing in AngularJSPeter Drinnan
 
gDayX 2013 - Advanced AngularJS - Nicolas Embleton
gDayX 2013 - Advanced AngularJS - Nicolas EmbletongDayX 2013 - Advanced AngularJS - Nicolas Embleton
gDayX 2013 - Advanced AngularJS - Nicolas EmbletonGeorge Nguyen
 
GDG Addis - An Introduction to Django and App Engine
GDG Addis - An Introduction to Django and App EngineGDG Addis - An Introduction to Django and App Engine
GDG Addis - An Introduction to Django and App EngineYared Ayalew
 
gDayX - Advanced angularjs
gDayX - Advanced angularjsgDayX - Advanced angularjs
gDayX - Advanced angularjsgdgvietnam
 
Kogito: cloud native business automation
Kogito: cloud native business automationKogito: cloud native business automation
Kogito: cloud native business automationMario Fusco
 
Тестирование и Django
Тестирование и DjangoТестирование и Django
Тестирование и DjangoMoscowDjango
 
Writing automation tests with python selenium behave pageobjects
Writing automation tests with python selenium behave pageobjectsWriting automation tests with python selenium behave pageobjects
Writing automation tests with python selenium behave pageobjectsLeticia Rss
 
Introduction To Django (Strange Loop 2011)
Introduction To Django (Strange Loop 2011)Introduction To Django (Strange Loop 2011)
Introduction To Django (Strange Loop 2011)Jacob Kaplan-Moss
 
Refresh Austin - Intro to Dexy
Refresh Austin - Intro to DexyRefresh Austin - Intro to Dexy
Refresh Austin - Intro to Dexyananelson
 
Google app-engine-with-python
Google app-engine-with-pythonGoogle app-engine-with-python
Google app-engine-with-pythonDeepak Garg
 
Introducing the Seneca MVP framework for Node.js
Introducing the Seneca MVP framework for Node.jsIntroducing the Seneca MVP framework for Node.js
Introducing the Seneca MVP framework for Node.jsRichard Rodger
 
國民雲端架構 Django + GAE
國民雲端架構 Django + GAE國民雲端架構 Django + GAE
國民雲端架構 Django + GAEWinston Chen
 
Angular Intermediate
Angular IntermediateAngular Intermediate
Angular IntermediateLinkMe Srl
 

Similar to Behind the curtain - How Django handles a request (20)

Django for mobile applications
Django for mobile applicationsDjango for mobile applications
Django for mobile applications
 
Testing in AngularJS
Testing in AngularJSTesting in AngularJS
Testing in AngularJS
 
Django tricks (2)
Django tricks (2)Django tricks (2)
Django tricks (2)
 
gDayX 2013 - Advanced AngularJS - Nicolas Embleton
gDayX 2013 - Advanced AngularJS - Nicolas EmbletongDayX 2013 - Advanced AngularJS - Nicolas Embleton
gDayX 2013 - Advanced AngularJS - Nicolas Embleton
 
React django
React djangoReact django
React django
 
GDG Addis - An Introduction to Django and App Engine
GDG Addis - An Introduction to Django and App EngineGDG Addis - An Introduction to Django and App Engine
GDG Addis - An Introduction to Django and App Engine
 
gDayX - Advanced angularjs
gDayX - Advanced angularjsgDayX - Advanced angularjs
gDayX - Advanced angularjs
 
Kogito: cloud native business automation
Kogito: cloud native business automationKogito: cloud native business automation
Kogito: cloud native business automation
 
Тестирование и Django
Тестирование и DjangoТестирование и Django
Тестирование и Django
 
Writing automation tests with python selenium behave pageobjects
Writing automation tests with python selenium behave pageobjectsWriting automation tests with python selenium behave pageobjects
Writing automation tests with python selenium behave pageobjects
 
Introduction To Django (Strange Loop 2011)
Introduction To Django (Strange Loop 2011)Introduction To Django (Strange Loop 2011)
Introduction To Django (Strange Loop 2011)
 
Refresh Austin - Intro to Dexy
Refresh Austin - Intro to DexyRefresh Austin - Intro to Dexy
Refresh Austin - Intro to Dexy
 
Google app-engine-with-python
Google app-engine-with-pythonGoogle app-engine-with-python
Google app-engine-with-python
 
Introducing the Seneca MVP framework for Node.js
Introducing the Seneca MVP framework for Node.jsIntroducing the Seneca MVP framework for Node.js
Introducing the Seneca MVP framework for Node.js
 
20120816 nodejsdublin
20120816 nodejsdublin20120816 nodejsdublin
20120816 nodejsdublin
 
國民雲端架構 Django + GAE
國民雲端架構 Django + GAE國民雲端架構 Django + GAE
國民雲端架構 Django + GAE
 
Angular Intermediate
Angular IntermediateAngular Intermediate
Angular Intermediate
 
Angularjs
AngularjsAngularjs
Angularjs
 
Introduction to Django
Introduction to DjangoIntroduction to Django
Introduction to Django
 
Django at the Disco
Django at the DiscoDjango at the Disco
Django at the Disco
 

Recently uploaded

Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension AidPhilip Schwarz
 
WSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburgmasabamasaba
 
WSO2Con2024 - From Blueprint to Brilliance: WSO2's Guide to API-First Enginee...
WSO2Con2024 - From Blueprint to Brilliance: WSO2's Guide to API-First Enginee...WSO2Con2024 - From Blueprint to Brilliance: WSO2's Guide to API-First Enginee...
WSO2Con2024 - From Blueprint to Brilliance: WSO2's Guide to API-First Enginee...WSO2
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrainmasabamasaba
 
WSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security ProgramWSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security ProgramWSO2
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisamasabamasaba
 
WSO2Con2024 - GitOps in Action: Navigating Application Deployment in the Plat...
WSO2Con2024 - GitOps in Action: Navigating Application Deployment in the Plat...WSO2Con2024 - GitOps in Action: Navigating Application Deployment in the Plat...
WSO2Con2024 - GitOps in Action: Navigating Application Deployment in the Plat...WSO2
 
What Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the SituationWhat Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the SituationJuha-Pekka Tolvanen
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park masabamasaba
 
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open SourceWSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open SourceWSO2
 
tonesoftg
tonesoftgtonesoftg
tonesoftglanshi9
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrandmasabamasaba
 
WSO2CON 2024 Slides - Unlocking Value with AI
WSO2CON 2024 Slides - Unlocking Value with AIWSO2CON 2024 Slides - Unlocking Value with AI
WSO2CON 2024 Slides - Unlocking Value with AIWSO2
 
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2
 
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2
 
%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in sowetomasabamasaba
 

Recently uploaded (20)

Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
WSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaS
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
 
WSO2Con2024 - From Blueprint to Brilliance: WSO2's Guide to API-First Enginee...
WSO2Con2024 - From Blueprint to Brilliance: WSO2's Guide to API-First Enginee...WSO2Con2024 - From Blueprint to Brilliance: WSO2's Guide to API-First Enginee...
WSO2Con2024 - From Blueprint to Brilliance: WSO2's Guide to API-First Enginee...
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
WSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security ProgramWSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security Program
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
WSO2Con2024 - GitOps in Action: Navigating Application Deployment in the Plat...
WSO2Con2024 - GitOps in Action: Navigating Application Deployment in the Plat...WSO2Con2024 - GitOps in Action: Navigating Application Deployment in the Plat...
WSO2Con2024 - GitOps in Action: Navigating Application Deployment in the Plat...
 
What Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the SituationWhat Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the Situation
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go Platformless
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open SourceWSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
WSO2CON 2024 - Freedom First—Unleashing Developer Potential with Open Source
 
tonesoftg
tonesoftgtonesoftg
tonesoftg
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
WSO2CON 2024 Slides - Unlocking Value with AI
WSO2CON 2024 Slides - Unlocking Value with AIWSO2CON 2024 Slides - Unlocking Value with AI
WSO2CON 2024 Slides - Unlocking Value with AI
 
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
WSO2CON 2024 - WSO2's Digital Transformation Journey with Choreo: A Platforml...
 
%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto
 

Behind the curtain - How Django handles a request

  • 1. Behind the curtain How Django handles a request Daniel Hepper
  • 2. About this tutorial • not a top-down Django tutorial • for people who want to get to the bottom of things
  • 3. What we'll cover • what is Django? • dissecting the tiniest Django web app • the same app, but more complicated
  • 4. About me Daniel Hepper • degree in computer science • freelancing for 7 years • first contact with Django around 0.95 • NOT a core developer • varying amounts beard
  • 6. The web framework for perfectionists with deadlines Django makes it easier to build better Web apps more quickly and with less code.
  • 7. Web apps • web app • Django project • django-admin.py startproject • Django app • module, part of an web app • might be re-usable: re-usable app • python manage.py startapp
  • 9. Web Framework • you take things from a library,
 you put something in a framework • "Don't call us, we call you" • But you have to tell us your number
 you have to provide an entrypoint
  • 12. Web Framework • turn HTTP requests into HTTP response
  • 14. HTTP Client HTTP Request HTTP Response HTTP Server
  • 15. HTTP-Request & Reponse GET / HTTP/1.1 Host: 127.0.0.1:8000 User-Agent: curl/7.54.0 Accept: */* HTTP/1.0 200 OK Date: Sat, 26 May 2018 07:27:52 GMT Server: WSGIServer/0.2 CPython/3.6.4 Content-Type: text/html; charset=utf-8 Hello world
  • 21. WSGI • Defined in PEP-333 / PEP-3333 • "A [...] standard interface between web servers and Python web applications or frameworks, to promote web application portability across a variety of web servers." • "WSGI is a tool for framework and server developers"
  • 23. WSGI Application • Callable that accepts two arguments • a function, method, class, instance with a __call__ method • result = application(environ, start_response) • environ: dictionary object, containing CGI-style environment variables • start_response: callable, accepting two required positional arguments, third optional • def start_response(status, response_headers, exc_info=None): • When called by the server, the application object must return an iterable yielding zero or more bytestrings
  • 24. Hello WSGI HELLO_WORLD = b"Hello world!n" def simple_app(environ, start_response): """Simplest possible application object""" status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return [HELLO_WORLD]
  • 25. The web framework for perfectionists with deadlines Django makes it easier to build better Web apps more quickly and with less code. WSGI
  • 27. (venv)$ pip install django==2.0.5 (venv)$ django-admin.py startproject default (venv)$ tree default default ├── manage.py └── default ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py (venv)$ cat default/default/settings.py
  • 28. """ Django settings for mysite project... """ import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = '-^oi_8%de()$d3qqa_4fgwds#&nlfn2(j-@_hu(j-m7-+-@q@p' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', ... 'django.contrib.staticfiles', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', ... 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'default.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', "Django is too complicated!"
  • 29. Web development is complicated Django offers reasonable defaults to make your life easier
  • 31. All you need is... • a view • an URLConf • settings • a WSGI application object
  • 33. views.py from django.http import HttpResponse def hello(request): return HttpResponse('Hello world')
  • 34. urls.py from django.urls import path from . import views urlpatterns = [ path('', views) ]
  • 36. wsgi.py import os from django.core.wsgi import get_wsgi_application os.environ.setdefault( "DJANGO_SETTINGS_MODULE", "simple.settings" ) application = get_wsgi_application()
  • 37. Running a Django web application • job of a WSGI server • two phases • import the application object • call the application object for each request
  • 38. A simple WSGI server from wsgiref.simple_server import make_server from simple.wsgi import application httpd = make_server('127.0.0.1', 8000, application) print("Serving HTTP on port 8000...") httpd.handle_request()
  • 39. (venv)$ tree simple . ├── server.py └── simple ├── __init__.py ├── __pycache__ ├── settings.py ├── urls.py ├── views.py └── wsgi.py
  • 40. DEMO
  • 44. import os from django.core.wsgi import get_wsgi_application os.environ.setdefault( "DJANGO_SETTINGS_MODULE", "simple.settings" ) application = get_wsgi_application() wsgi.py
  • 45. import django from django.core.handlers.wsgi import WSGIHandler def get_wsgi_application(): """ The public interface to Django's WSGI support. Return a WSGI callable. Avoids making django.core.handlers.WSGIHandler a public API, in case the internal WSGI implementation changes or moves in the future. """ django.setup(set_prefix=False) return WSGIHandler() django/core/wsgi.py
  • 46. from django.utils.version import get_version VERSION = (2, 0, 0, 'final', 0) __version__ = get_version(VERSION) def setup(set_prefix=True): """ Configure the settings (this happens as a side effect of accessing the first setting), configure logging and populate the app registry. Set the thread-local urlresolvers script prefix if `set_prefix` is True. """ from django.apps import apps from django.conf import settings from django.urls import set_script_prefix from django.utils.log import configure_logging configure_logging(settings.LOGGING_CONFIG, settings.LOGGING) if set_prefix: set_script_prefix( '/' if settings.FORCE_SCRIPT_NAME is None else settings.FORCE_SCRIPT_NAME ) apps.populate(settings.INSTALLED_APPS) django/__init__.py
  • 47. [...] class LazySettings(LazyObject): """ A lazy proxy for either global Django settings or a custom settings object. The user can manually configure settings prior to using them. Otherwise,Django uses the settings module pointed to by DJANGO_SETTINGS_MODULE. """ def __getattr__(self, name): """ Return the value of a setting and cache it in self.__dict__. """ if self._wrapped is empty: self._setup(name) val = getattr(self._wrapped, name) self.__dict__[name] = val return val [...] settings = LazySettings() django/conf/__init__.py
  • 48. [...] class LazySettings(LazyObject): """ A lazy proxy for either global Django settings or a custom settings object. The user can manually configure settings prior to using them. Otherwise,Django uses the settings module pointed to by DJANGO_SETTINGS_MODULE. """ def _setup(self, name=None): """ Load the settings module pointed to by the environment variable. This is used the first time settings are needed, if the user hasn't configured settings manually. """ settings_module = os.environ.get(ENVIRONMENT_VARIABLE) if not settings_module: desc = ("setting %s" % name) if name else "settings" raise ImproperlyConfigured(...) self._wrapped = Settings(settings_module) [...] settings = LazySettings() django/conf/__init__.py
  • 49. [...] class Settings: def __init__(self, settings_module): for setting in dir(global_settings): if setting.isupper(): setattr(self, setting, getattr(global_settings, setting)) [...] mod = importlib.import_module(self.SETTINGS_MODULE) self._explicit_settings = set() for setting in dir(mod): if setting.isupper(): setting_value = getattr(mod, setting) [...] setattr(self, setting, setting_value) [...] [...] settings = LazySettings() django/conf/__init__.py
  • 50. from django.utils.version import get_version VERSION = (2, 0, 0, 'final', 0) __version__ = get_version(VERSION) def setup(set_prefix=True): """ Configure the settings (this happens as a side effect of accessing the first setting), configure logging and populate the app registry. Set the thread-local urlresolvers script prefix if `set_prefix` is True. """ from django.apps import apps from django.conf import settings from django.urls import set_script_prefix from django.utils.log import configure_logging configure_logging(settings.LOGGING_CONFIG, settings.LOGGING) if set_prefix: set_script_prefix( '/' if settings.FORCE_SCRIPT_NAME is None else settings.FORCE_SCRIPT_NAME ) apps.populate(settings.INSTALLED_APPS) django/__init__.py
  • 51. import django from django.core.handlers.wsgi import WSGIHandler def get_wsgi_application(): """ The public interface to Django's WSGI support. Return a WSGI callable. Avoids making django.core.handlers.WSGIHandler a public API, in case the internal WSGI implementation changes or moves in the future. """ django.setup(set_prefix=False) return WSGIHandler() django/core/wsgi.py
  • 52. class WSGIHandler(base.BaseHandler): request_class = WSGIRequest def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.load_middleware() def __call__(self, environ, start_response): [...] django/core/handlers/wsgi.py
  • 53. class BaseHandler: [...] def load_middleware(self): """ Populate middleware lists from settings.MIDDLEWARE. """ self._request_middleware = [] self._view_middleware = [] self._template_response_middleware = [] self._response_middleware = [] self._exception_middleware = [] handler = convert_exception_to_response( self._get_response) for middleware_path in reversed(settings.MIDDLEWARE): [...] self._middleware_chain = handler django/core/handlers/base.py
  • 54. class WSGIHandler(base.BaseHandler): [...] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.load_middleware() def __call__(self, environ, start_response): [...] django/core/handlers/wsgi.py
  • 55. import django from django.core.handlers.wsgi import WSGIHandler def get_wsgi_application(): """ The public interface to Django's WSGI support. Return a WSGI callable. Avoids making django.core.handlers.WSGIHandler a public API, in case the internal WSGI implementation changes or moves in the future. """ django.setup(set_prefix=False) return WSGIHandler() django/core/wsgi.py
  • 56. import os from django.core.wsgi import get_wsgi_application os.environ.setdefault( "DJANGO_SETTINGS_MODULE", "simple.settings" ) application = get_wsgi_application() wsgi.py
  • 58. WSGI Application • Callable that accepts two arguments • a function, method, class, instance with a __call__ method • result = application(environ, start_response) • environ: dictionary object, containing CGI-style environment variables • start_response: callable, accepting two required positional arguments, third optional • def start_response(status, response_headers, exc_info=None): • When called by the server, the application object must return an iterable yielding zero or more bytestrings
  • 59. class WSGIHandler(base.BaseHandler): request_class = WSGIRequest [...] def __call__(self, environ, start_response): set_script_prefix(get_script_name(environ)) signals.request_started.send(sender=self.__class__, environ=environ) request = self.request_class(environ) response = self.get_response(request) [...] django/core/handlers/wsgi.py
  • 60. class WSGIRequest(HttpRequest): def __init__(self, environ): [...] django/core/handlers/wsgi.py
  • 61. class WSGIRequest(HttpRequest): def __init__(self, environ): [...] django/core/handlers/wsgi.py
  • 62. class HttpRequest: """A basic HTTP request.""" # The encoding used in GET/POST dicts. None means use default # setting. _encoding = None _upload_handlers = [] def __init__(self): # WARNING: The `WSGIRequest` subclass doesn't call # `super`. # Any variable assignment made here should also happen in # `WSGIRequest.__init__()`. [...] django/core/http/request.py
  • 63. class WSGIRequest(HttpRequest): def __init__(self, environ): script_name = get_script_name(environ) path_info = get_path_info(environ) if not path_info: # Sometimes PATH_INFO exists, but is empty (e.g. # accessing the SCRIPT_NAME URL without a # slash). We really need to operate as if they'd # requested '/'. Not amazingly nice to force # the path like this, but should be harmless. path_info = '/' [...] django/core/handlers/wsgi.py
  • 64. path vs. script_name & path_info • http://mydjangoproject.com/ • http://mydjangoproject.com/polls/ • http://mydjangoprojects.com/project1/ • http://mydjangoprojects.com/project2/polls/ • http://mydjangoprojects.com/project3
  • 65. class WSGIRequest(HttpRequest): def __init__(self, environ): script_name = get_script_name(environ) path_info = get_path_info(environ) if not path_info: # Sometimes PATH_INFO exists, but is empty (e.g. # accessing the SCRIPT_NAME URL without a # slash). We really need to operate as if they'd # requested '/'. Not amazingly nice to force # the path like this, but should be harmless. path_info = '/' [...] def get_script_name(environ): """ Return the equivalent of the HTTP request's SCRIPT_NAME environment variable. If Apache mod_rewrite is used, return what would have been the script name prior to any rewriting (so it's the script name as seen from the client's perspective), unless the FORCE_SCRIPT_NAME setting is set (to anything). """ [...] django/core/handlers/wsgi.py
  • 66. class WSGIRequest(HttpRequest): def __init__(self, environ): script_name = get_script_name(environ) path_info = get_path_info(environ) if not path_info: # Sometimes PATH_INFO exists, but is empty (e.g. # accessing the SCRIPT_NAME URL without a # slash). We really need to operate as if they'd # requested '/'. Not amazingly nice to force # the path like this, but should be harmless. path_info = '/' [...] def get_path_info(environ): """Return the HTTP request's PATH_INFO as a string.""" path_info = get_bytes_from_wsgi(environ, 'PATH_INFO', '/') return repercent_broken_unicode(path_info).decode() django/core/handlers/wsgi.py
  • 67. class WSGIRequest(HttpRequest): def __init__(self, environ): [...] self.environ = environ self.path_info = path_info # be careful to only replace the first slash in the path # because of http://test/something and # http://test//something being different as # stated in http://www.ietf.org/rfc/rfc2396.txt self.path = '%s/%s' % (script_name.rstrip('/'), path_info.replace('/', '', 1)) [...] django/core/handlers/wsgi.py
  • 68. class WSGIRequest(HttpRequest): def __init__(self, environ): [...] self.META = environ self.META['PATH_INFO'] = path_info self.META['SCRIPT_NAME'] = script_name self.method = environ['REQUEST_METHOD'].upper() [...] django/core/handlers/wsgi.py
  • 69. class WSGIRequest(HttpRequest): def __init__(self, environ): [...] self.content_type, self.content_params = cgi.parse_header(environ.get('CONTENT_TYPE', '')) if 'charset' in self.content_params: try: codecs.lookup(self.content_params['charset']) except LookupError: pass else: self.encoding = self.content_params['charset'] [...] django/core/handlers/wsgi.py
  • 70. class WSGIRequest(HttpRequest): def __init__(self, environ): [...] self._post_parse_error = False try: content_length = int(environ.get('CONTENT_LENGTH')) except (ValueError, TypeError): content_length = 0 self._stream = LimitedStream(self.environ['wsgi.input'], content_length) self._read_started = False self.resolver_match = None django/core/handlers/wsgi.py
  • 71. class WSGIRequest(HttpRequest): def __init__(self, environ): [...] self._post_parse_error = False try: content_length = int(environ.get('CONTENT_LENGTH')) except (ValueError, TypeError): content_length = 0 self._stream = LimitedStream(self.environ['wsgi.input'], content_length) self._read_started = False self.resolver_match = None class LimitedStream: """ Wrap another stream to disallow reading it past a number of bytes. """ [...] django/core/handlers/wsgi.py
  • 72. class WSGIHandler(base.BaseHandler): request_class = WSGIRequest [...] def __call__(self, environ, start_response): set_script_prefix(get_script_name(environ)) signals.request_started.send(sender=self.__class__, environ=environ) request = self.request_class(environ) response = self.get_response(request) [...] django/core/handlers/wsgi.py
  • 73. class BaseHandler: [...] def get_response(self, request): """ Return an HttpResponse object for the given HttpRequest. """ # Setup default url resolver for this thread set_urlconf(settings.ROOT_URLCONF) response = self._middleware_chain(request) django/core/handlers/base.py
  • 74. # Overridden URLconfs for each thread are stored here. _urlconfs = local() [...] def set_urlconf(urlconf_name): """ Set the URLconf for the current thread (overriding the default one in settings). If urlconf_name is None, revert back to the default. """ if urlconf_name: _urlconfs.value = urlconf_name else: if hasattr(_urlconfs, "value"): del _urlconfs.value django/urls/base.py
  • 75. class BaseHandler: [...] def get_response(self, request): """ Return an HttpResponse object for the given HttpRequest. """ # Setup default url resolver for this thread set_urlconf(settings.ROOT_URLCONF) response = self._middleware_chain(request) django/core/handlers/base.py
  • 76. class BaseHandler: def load_middleware(self): """ Populate middleware lists from settings.MIDDLEWARE. """ handler = convert_exception_to_response( self._get_response ) [...] self._middleware_chain = handler def get_response(self, request): """ Return an HttpResponse object for the given HttpRequest. """ # Setup default url resolver for this thread set_urlconf(settings.ROOT_URLCONF) response = self._middleware_chain(request) django/core/handlers/base.py
  • 77. class BaseHandler: [...] def _get_response(self, request): """ Resolve and call the view, then apply view, exception, and template_response middleware. This method is everything that happens inside the request/response middleware. """ [...] django/core/handlers/base.py
  • 78. class BaseHandler: [...] def _get_response(self, request): """ Resolve and call the view, then apply view, exception, and template_response middleware. This method is everything that happens inside the request/response middleware. """ response = None if hasattr(request, 'urlconf'): urlconf = request.urlconf set_urlconf(urlconf) resolver = get_resolver(urlconf) else: resolver = get_resolver() resolver_match = resolver.resolve(request.path_info) callback, callback_args, callback_kwargs = resolver_match request.resolver_match = resolver_match django/core/handlers/base.py
  • 79. class BaseHandler: [...] def _get_response(self, request): [...] callback, callback_args, callback_kwargs = resolver_match [...] # Apply view middleware for middleware_method in self._view_middleware: response = middleware_method(request, callback, ...) if response: break [...] django/core/handlers/base.py
  • 80. class BaseHandler: [...] def _get_response(self, request): [...] callback, callback_args, callback_kwargs = resolver_match [...] # Apply view middleware [...] if response is None: wrapped_callback = self.make_view_atomic(callback) try: response = wrapped_callback( request, *callback_args, **callback_kwargs ) except Exception as e: response = self.process_exception_by_middleware( e, request ) django/core/handlers/base.py
  • 81. class BaseHandler: [...] def _get_response(self, request): [...] callback, callback_args, callback_kwargs = resolver_match [...] # Apply view middleware [...] response = wrapped_callback( request, *callback_args, **callback_kwargs ) # Complain if the view returned None (a common error). [...] # If the response supports deferred rendering, apply # template response middleware and then render the response [...] return response django/core/handlers/base.py
  • 82. class BaseHandler: def load_middleware(self): """ Populate middleware lists from settings.MIDDLEWARE. """ handler = convert_exception_to_response( self._get_response ) [...] self._middleware_chain = handler def get_response(self, request): """ Return an HttpResponse object for the given HttpRequest. """ # Setup default url resolver for this thread set_urlconf(settings.ROOT_URLCONF) response = self._middleware_chain(request) django/core/handlers/base.py
  • 83. class BaseHandler: [...] def get_response(self, request): [...] response = self._middleware_chain(request) response._closable_objects.append(request) [...] return response django/core/handlers/base.py
  • 84. class HttpResponseBase: [...] # The WSGI server must call this method upon completion of # the request. # See http://blog.dscpl.com.au/2012/10/obligations-for-calling-close def close(self): for closable in self._closable_objects: try: closable.close() except Exception: pass self.closed = True signals.request_finished.send(sender=self._handler_class) django/http/response.py
  • 85. class BaseHandler: [...] def get_response(self, request): [...] response = self._middleware_chain(request) response._closable_objects.append(request) # If the exception handler returns a TemplateResponse # that has not been rendered, force it to be rendered. if not getattr(response, 'is_rendered', True) and callable(getattr(response, 'render', None)): response = response.render() [...] return response django/core/handlers/base.py
  • 86. class BaseHandler: [...] def get_response(self, request): [...] response = self._middleware_chain(request) response._closable_objects.append(request) # If the exception handler returns a TemplateResponse # that has not been rendered, force it to be rendered. if not getattr(response, 'is_rendered', True) and callable(getattr(response, 'render', None)): response = response.render() if response.status_code == 404: logger.warning( 'Not Found: %s', request.path, extra={'status_code': 404, 'request': request}) return response django/core/handlers/base.py
  • 87. class WSGIHandler(base.BaseHandler): request_class = WSGIRequest [...] def __call__(self, environ, start_response): set_script_prefix(get_script_name(environ)) signals.request_started.send(sender=self.__class__, environ=environ) request = self.request_class(environ) response = self.get_response(request) [...] django/core/handlers/wsgi.py
  • 88. class WSGIHandler(base.BaseHandler): request_class = WSGIRequest [...] def __call__(self, environ, start_response): [...] response = self.get_response(request) response._handler_class = self.__class__ status = '%d %s' % (response.status_code, response.reason_phrase response_headers = list(response.items()) for c in response.cookies.values(): response_headers.append(('Set-Cookie', c.output(header=''))) start_response(status, response_headers) if (getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper')): response = environ['wsgi.file_wrapper'](response.file_to_str return response django/core/handlers/wsgi.py
  • 92. Function based middleware def simple_middleware(get_response): # One-time configuration and initialization. def middleware(request): # Code to be executed for each request before # the view (and later middleware) are called. response = get_response(request) # Code to be executed for each request/response after # the view is called. return response return middleware
  • 93. Class based middleware class SimpleMiddleware: def __init__(self, get_response): self.get_response = get_response # One-time configuration and initialization. def __call__(self, request): # Code to be executed for each request before # the view (and later middleware) are called. response = self.get_response(request) # Code to be executed for each request/response after # the view is called. return response
  • 94. Additional hooks • process_view(request, view_func, view_args, view_kwargs) • process_exception(request, exception) • process_template_response(request, response)
  • 95. Pre-Django 1.10-style middleware • process_request() • process_response() • django.utils.deprecation.MiddlewareMixin
  • 97. class Middleware1: def __init__(self, get_response): print('> Middleware1.__init__') self.get_response = get_response def __call__(self, request): print('> Middleware1.__call__') response = self.get_response(request) print('< Middleware1.__call__') return response def process_view(self, request, view_func, view_args, view_kwargs): print('> Middleware1.process_view') return None def process_exception(self, request, exception): print('> Middleware1.process_exception') return None def process_template_response(self, request, response): print('> Middleware1.process_template_response') return response middleware/middleware.py
  • 98. SECRET_KEY = 'not-so-secret-123%^&' ROOT_URLCONF = 'middleware.urls' MIDDLEWARE = [ 'middleware.middleware.Middleware1', 'middleware.middleware.Middleware2', 'middleware.middleware.Middleware3', ] middleware/settings.py
  • 100. $ python server.py > Middleware3.__init__ > Middleware2.__init__ > Middleware1.__init__ Serving HTTP on port 8000...
  • 101. class BaseHandler: [...] def load_middleware(self): """ Populate middleware lists from settings.MIDDLEWARE. """ self._request_middleware = [] self._view_middleware = [] self._template_response_middleware = [] self._response_middleware = [] self._exception_middleware = [] handler = convert_exception_to_response(self._get_response) for middleware_path in reversed(settings.MIDDLEWARE): [...] self._middleware_chain = handler django/core/handlers/base.py
  • 102. class BaseHandler: [...] def load_middleware(self): """ Populate middleware lists from settings.MIDDLEWARE. """ [...] handler = convert_exception_to_response(self._get_response) for middleware_path in reversed(settings.MIDDLEWARE): middleware = import_string(middleware_path) try: mw_instance = middleware(handler) except MiddlewareNotUsed as exc: if settings.DEBUG: [...] continue [...] handler = convert_exception_to_response(mw_instance) self._middleware_chain = handler django/core/handlers/base.py
  • 103. settings.MIDDLEWARE = [ 'middleware.middleware.Middleware1', 'middleware.middleware.Middleware2', 'middleware.middleware.Middleware3', ] handler = convert_exception_to_response(self._get_response) for middleware_path in reversed(settings.MIDDLEWARE): middleware = import_string(middleware_path) mw_instance = middleware(handler) handler = convert_exception_to_response(mw_instance) handler = convert_exception_to_response(self._get_response) handler = convert_exception_to_response(Middleware3(handler)) handler = convert_exception_to_response(Middleware2(handler)) handler = convert_exception_to_response(Middleware1(handler))
  • 104. settings.MIDDLEWARE = [ 'middleware1', 'middleware2', 'middleware3', ] handler = convert_exception_to_response(self._get_response) for middleware_path in reversed(settings.MIDDLEWARE): middleware = import_string(middleware_path) mw_instance = middleware(handler) handler = convert_exception_to_response(mw_instance) handler = middleware1(middleware2(middleware3(self._get_response)))))))
  • 105. class BaseHandler: [...] def load_middleware(self): """ Populate middleware lists from settings.MIDDLEWARE. """ [...] handler = convert_exception_to_response(self._get_response) for middleware_path in reversed(settings.MIDDLEWARE): middleware = import_string(middleware_path) try: mw_instance = middleware(handler) except MiddlewareNotUsed as exc: if settings.DEBUG: [...] continue [...] handler = convert_exception_to_response(mw_instance) self._middleware_chain = handler django/core/handlers/base.py
  • 106. class BaseHandler: [...] def load_middleware(self): [...] for middleware_path in reversed(settings.MIDDLEWARE): [...] if hasattr(mw_instance, 'process_view'): self._view_middleware.insert( 0, mw_instance.process_view) if hasattr(mw_instance, 'process_template_response'): self._template_response_middleware.append( mw_instance.process_template_response) if hasattr(mw_instance, 'process_exception'): self._exception_middleware.append( mw_instance.process_exception) handler = convert_exception_to_response(mw_instance) self._middleware_chain = handler django/core/handlers/base.py
  • 107. settings.MIDDLEWARE = [ 'middleware1', 'middleware2', 'middleware3', ] self._view_middleware = [ middleware1_instance.process_view, middleware2_instance.process_view, middleware3_instance.process_view, ] self._template_response_middleware = [ middleware3_instance.process_template_response, middleware2_instance.process_template_response, middleware1_instance.process_template_response, ] self._exception_middleware = [ middleware3_instance.process_exception, middleware2_instance.process_exception, middleware1_instance.process_exception, ]
  • 110. $ python server.py > Middleware3.__init__ > Middleware2.__init__ > Middleware1.__init__ Serving HTTP on port 8000... > Middleware1.__call__ > Middleware2.__call__ > Middleware3.__call__ > Middleware1.process_view > Middleware2.process_view > Middleware3.process_view < Middleware3.__call__ < Middleware2.__call__ < Middleware1.__call__
  • 111. class BaseHandler: def load_middleware(self): """ Populate middleware lists from settings.MIDDLEWARE. """ [...] def get_response(self, request): """ Return an HttpResponse object for the given HttpRequest. """ # Setup default url resolver for this thread set_urlconf(settings.ROOT_URLCONF) response = self._middleware_chain(request) middleware1(middleware2(middleware3(self._get_response))))))) django/core/handlers/base.py
  • 113. class BaseHandler: [...] def _get_response(self, request): """ Resolve and call the view, then apply view, exception, and template_response middleware. This method is everything that happen inside the request/response middleware. """ response = None [...] return response django/core/handlers/base.py
  • 114. class BaseHandler: [...] def _get_response(self, request): """ Resolve and call the view, then apply view, exception, and template_response middleware. This method is everything that happen inside the request/response middleware. """ response = None if hasattr(request, 'urlconf'): urlconf = request.urlconf set_urlconf(urlconf) resolver = get_resolver(urlconf) else: resolver = get_resolver() [...] return response django/core/handlers/base.py
  • 115. [...] @functools.lru_cache(maxsize=None) def get_resolver(urlconf=None): if urlconf is None: from django.conf import settings urlconf = settings.ROOT_URLCONF return URLResolver(RegexPattern(r'^/'), urlconf) [...] django/urls/resolvers/base.py
  • 116. class BaseHandler: [...] def _get_response(self, request): """ Resolve and call the view, then apply view, exception, and template_response middleware. This method is everything that happen inside the request/response middleware. """ [...] resolver_match = resolver.resolve(request.path_info) callback, callback_args, callback_kwargs = resolver_match request.resolver_match = resolver_match [...] return response django/core/handlers/base.py
  • 117. class BaseHandler: [...] def _get_response(self, request): """ Resolve and call the view, then apply view, exception, and template_response middleware. This method is everything that happen inside the request/response middleware. """ [...] callback, callback_args, callback_kwargs = resolver_match [...] # Apply view middleware for middleware_method in self._view_middleware: response = middleware_method(request, callback, callback_args, callback_kwargs) if response: break [...] return response django/core/handlers/base.py
  • 119. class BaseHandler: [...] def _get_response(self, request): """ Resolve and call the view, then apply view, exception, and template_response middleware. This method is everything that happen inside the request/response middleware. """ [...] if response is None: wrapped_callback = self.make_view_atomic(callback) try: response = wrapped_callback(request, *callback_args, **callback_kwargs) except Exception as e: response = self.process_exception_by_middleware( e, request) [...] return response django/core/handlers/base.py
  • 120. class BaseHandler: [...] def _get_response(self, request): """ Resolve and call the view, then apply view, exception, and template_response middleware. This method is everything that happen inside the request/response middleware. """ [...] # Complain if the view returned None (a common error). if response is None: if isinstance(callback, types.FunctionType): # FBV view_name = callback.__name__ else: # CBV view_name = callback.__class__.__name__ + '.__call__' raise ValueError( "The view %s.%s didn't return an HttpResponse object. It "returned None instead." % (callback.__module__, view_na ) [...] return response django/core/handlers/base.py
  • 121. class BaseHandler: [...] def _get_response(self, request): """ Resolve and call the view, then apply view, exception, and template_response middleware. This method is everything that happen inside the request/response middleware. """ [...] # If the response supports deferred rendering, apply template # response middleware and then render the response elif hasattr(response, 'render') and callable(response.render): for middleware_method in self._template_response_middleware: response = middleware_method(request, response) # Complain if the template response middleware # returned None (a common error). if response is None: raise ValueError(...) [...] return response django/core/handlers/base.py
  • 122. class BaseHandler: [...] def _get_response(self, request): """ Resolve and call the view, then apply view, exception, and template_response middleware. This method is everything that happen inside the request/response middleware. """ [...] # If the response supports deferred rendering, apply template # response middleware and then render the response elif hasattr(response, 'render') and callable(response.render): [...] try: response = response.render() except Exception as e: response = self.process_exception_by_middleware(e, reque return response django/core/handlers/base.py
  • 123. class BaseHandler: [...] def process_exception_by_middleware(self, exception, request): """ Pass the exception to the exception middleware. If no middleware return a response for this exception, raise it. """ for middleware_method in self._exception_middleware: response = middleware_method(request, exception) if response: return response raise django/core/handlers/base.py
  • 125. class BaseHandler: [...] def _get_response(self, request): """ Resolve and call the view, then apply view, exception, and template_response middleware. This method is everything that happen inside the request/response middleware. """ [...] # If the response supports deferred rendering, apply template # response middleware and then render the response elif hasattr(response, 'render') and callable(response.render): [...] try: response = response.render() except Exception as e: response = self.process_exception_by_middleware(e, reque return response django/core/handlers/base.py
  • 126. class BaseHandler: [...] def get_response(self, request): """ Return an HttpResponse object for the given HttpRequest. """ # Setup default url resolver for this thread set_urlconf(settings.ROOT_URLCONF) response = self._middleware_chain(request) [...] return response django/core/handlers/base.py
  • 127. class BaseHandler: [...] def get_response(self, request): [...] response = self._middleware_chain(request) [...] return response django/core/handlers/base.py
  • 128. class BaseHandler: [...] def get_response(self, request): [...] response = self._middleware_chain(request) response._closable_objects.append(request) # If the exception handler returns a TemplateResponse # that has not been rendered, force it to be rendered. if not getattr(response, 'is_rendered', True) and callable(getattr(response, 'render', None)): response = response.render() if response.status_code == 404: logger.warning( 'Not Found: %s', request.path, extra={'status_code': 404, 'request': request}) return response django/core/handlers/base.py
  • 129. class WSGIHandler(base.BaseHandler): request_class = WSGIRequest [...] def __call__(self, environ, start_response): [...] response = self.get_response(request) response._handler_class = self.__class__ status = '%d %s' % (response.status_code, response.reason_phrase response_headers = list(response.items()) for c in response.cookies.values(): response_headers.append(('Set-Cookie', c.output(header=''))) start_response(status, response_headers) if (getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper')): response = environ['wsgi.file_wrapper'](response.file_to_str return response django/core/handlers/wsgi.py
  • 130. TIL • Django is made by humans, not wizards • Just code, no magic
  • 131. Room for contribution • Unused attributes _response_middleware and _request_middleware in BaseHandler • Wrong or misleading comments • ExceptionMiddleware does not exist
  • 133. Image credits • https://pixabay.com/en/interior-house-home- curtain-2570933/ • https://unsplash.com/photos/MEW1f-yu2KI • https://pixabay.com/en/rocket-launch-smoke-rocket-take- off-67723/ • https://pixabay.com/en/gears-cogs-machine- machinery-1236578/ • https://pixabay.com/en/balance-background-harmony- stacked-3356546/