Django under microscope

Artem Malyshev

@proofit404

Who

Use Python for 5 years
Early rannts
Django Channels 1.0
Dry Python

Installation

$ pip install Django
# setup.py
from setuptools import find_packages, setup

setup(
    name='Django',
    entry_points={
      'console_scripts': [
        'django-admin =
          django.core.management:execute_from_command_line'
      ]
    },
)
Bootstrap
# django.core.management
django.setup()
Configure settings
import django.conf.global_settings
import_module(os.environ["DJANGO_SETTINGS_MODULE"])
Populate apps
for entry in settings.INSTALLED_APPS:
    cfg = AppConfig.create(entry)
    cfg.import_models()

Management command

# django.core.management
subcommand = sys.argv[1]
app_name = find(pkgutils.iter_modules(settings.INSTALLED_APPS))
module = import_module(
    '%s.management.commands.%s' % (app_name, subcommand)
)
cmd = module.Command()
cmd.run_from_argv(self.argv)

Commands

# django.core.management.commands.runserver
naiveip_re = re.compile(r"""^(?:
(?P<addr>
    (?P<ipv4>\d{1,3}(?:\.\d{1,3}){3}) |         # IPv4 address
    (?P<ipv6>\[[a-fA-F0-9:]+\]) |               # IPv6 address
    (?P<fqdn>[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*) # FQDN
):)?(?P<port>\d+)$""", re.X)

Commands

# django.core.management.commands.runserver
class Command(BaseCommand):

    def handle(self, *args, **options):

        httpd = WSGIServer(*args, **options)
        handler = WSGIHandler()
        httpd.set_app(handler)
        httpd.serve_forever()

WSGI Server

WSGI Handler

# django.core.handlers.wsgi
class WSGIHandler:

    def __call__(self, environ, start_response):

        signals.request_started.send()
        request = WSGIRequest(environ)
        response = self.get_response(request)
        start_response(response.status, response.headers)
        return response

What's going on

Request
Middlewares
Routing
View
Form
ORM
Template
Response

Request

# django.core.handlers.wsgi
class WSGIRequest(HttpRequest):
    @cached_property
    def GET(self):
        return QueryDict(self.environ['QUERY_STRING'])

    @property
    def POST(self):
        self._load_post_and_files()
        return self._post

    @cached_property
    def COOKIES(self):
        return parse_cookie(self.environ['HTTP_COOKIE'])

Middlewares

Settings
# settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
]
Define
class Middleware:

    def __init__(self, get_response=None):
        self.get_response = get_response

    def __call__(self, request):
        return self.get_response(request)

Middlewares

# django.core.handlers.base
def load_middleware(self):

    handler = convert_exception_to_response(self._get_response)
    for middleware_path in reversed(settings.MIDDLEWARE):
        middleware = import_string(middleware_path)
        instance = middleware(handler)
        handler = convert_exception_to_response(instance)
    self._middleware_chain = handler
Apply
def get_response(self, request):

    set_urlconf(settings.ROOT_URLCONF)
    response = self._middleware_chain(request)
    return response

Routing

# django.core.handlers.base
def _get_response(self, request):

    resolver = get_resolver()
    view, args, kwargs = resolver.resolve(request.path_info)
    response = view(request, *args, **kwargs)
    return response
Urls
# urls.py
urlpatterns = [
    path('articles/2003/', views.special_case_2003),
    path('articles/<int:year>/', views.year_archive),
    path('articles/<int:year>/<int:month>/', views.month_archive)
]

Resolver

# django.urls.resolvers
_PATH_RE = re.compile(
    r'<(?:(?P<converter>[^>:]+):)?(?P<parameter>\w+)>'
)

def resolve(self, path):
    for pattern in self.url_patterns:
        match = pattern.search(path)
        if match:
            return ResolverMatch(
                self.resolve(match[0])
            )
    raise Resolver404({'path': path})
View
# django.views.generic.edit
class ContactView(FormView):
    template_name = 'contact.html'
    form_class = ContactForm
    success_url = '/thanks/'
Method flowchart
self.dispatch()
self.post()
self.get_form()
self.form_valid()
self.render_to_response()
Form
Content-Type: multipart/form-data;boundary="boundary"

--boundary
name="field1"

value1
--boundary
name="field2";

value2
Parser
# django.http.multipartparser
self._post = QueryDict(mutable=True)
stream = LazyStream(ChunkIter(self._input_data))
for field, data in Parser(stream):
    self._post.append(field, force_text(data))

ORM

# models.py
Entry.objects.exclude(
    pub_date__gt=date(2005, 1, 3),
    headline='Hello',
)
SQL
SELECT * WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')

Queryset

# django.db.models.query
sql.Query(Entry).where.add(
    ~Q(
        Q(F('pub_date') > date(2005, 1, 3)) &
        Q(headline='Hello')
    )
)
Compiler
# django.db.models.expressions
class Q(tree.Node):
    AND = 'AND'
    OR = 'OR'

    def as_sql(self, compiler, connection):
        return self.template % self.field.get_lookup('gt')

Output

>>> Q(headline='Hello')
# headline = 'Hello'
>>> F('pub_date')
# pub_date
>>> F('pub_date') > date(2005, 1, 3)
# pub_date > '2005-1-3'
>>> Q(...) & Q(...)
# ... AND ...
>>> ~Q(...)
# NOT ...

DB routing

# django.db.utils
class ConnectionRouter:
    def db_for_read(self, model, **hints):
        if model._meta.app_label == 'auth':
            return 'auth_db'
Connection pool
# django.db.backends.base.base
class BaseDatabaseWrapper:
    def commit(self):
        self.validate_thread_sharing()
        self.validate_no_atomic_block()
        with self.wrap_database_errors:
            return self.connection.commit()

Template

from django.template.loader import render_to_string
render_to_string('my_template.html', {'entries': ...})
Code
<ul>
{% for entry in entries %}
    <li>{{ entry.name }}</li>
{% endfor %}
</ul>

Parser

# django.template.base
BLOCK_TAG_START = '{%'
BLOCK_TAG_END = '%}'
VARIABLE_TAG_START = '{{'
VARIABLE_TAG_END = '}}'
COMMENT_TAG_START = '{#'
COMMENT_TAG_END = '#}'

tag_re = (re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' %
          (re.escape(BLOCK_TAG_START),
           re.escape(BLOCK_TAG_END),
           re.escape(VARIABLE_TAG_START),
           re.escape(VARIABLE_TAG_END),
           re.escape(COMMENT_TAG_START),
           re.escape(COMMENT_TAG_END))))

Lexer

# django.template.base
def tokenize(self):
    for bit in tag_re.split(template_string):
        lineno += bit.count('\n')
        yield bit
Parser
def parse():
    while tokens:
        token = tokens.pop()
        if token.startswith(BLOCK_TAG_START):
            yield TagNode(token)
        elif token.startswith(VARIABLE_TAG_START):
            ...

For loop

# django.template.defaulttags
@register.tag('for')
def do_for(parser, token):
    args = token.split_contents()
    body = parser.parse(until=['endfor'])
    return ForNode(args, body)
For node
class ForNode(Node):
    def render(self, context):
         with context.push():
             for i in self.args:
                 yield self.body.render(context)

Response

Understand your tools

Do a better job

The end