Learning Django: the hard way (1)

What does "runserver" do?

Django provides a light-weight web server for development and test use. You can just start it using the command something like this-

python manage.py runserver 0.0.0.0:8000

manage.py just calls -

django.core.management.execute_from_command_line(sys.argv)

OK, so we need to dig into it a little to bit to see where the runserver comes into play. Finally, it turns out the parameter "runserver" would create a new instance of Command from the module django.core.management.commands.runserver.py.

There is one method to dynamically load the command class based on the command parameter, like runserver. The code is like this -

def load_command_class(app_name, name):
"""
Given a command name and an application name, returns the Command
class instance. All errors raised by the import process
(ImportError, AttributeError) are allowed to propagate.
"""
module = import_module('%s.management.commands.%s' % (app_name, name))
return module.Command()

OK, so far the corresponding command object of runserver has been created. Next, the method handle of the command object is called which in turns would call the method inner_run -

def inner_run(self, *args, **options):
from django.conf import settings
from django.utils import translation threading = options.get('use_threading')
shutdown_message = options.get('shutdown_message', '')
quit_command = 'CTRL-BREAK' if sys.platform == 'win32' else 'CONTROL-C' self.stdout.write("Performing system checks...\n\n")
self.validate(display_num_errors=True)
try:
self.check_migrations()
except ImproperlyConfigured:
pass
now = datetime.now().strftime('%B %d, %Y - %X')
if six.PY2:
now = now.decode(get_system_encoding())
self.stdout.write((
"%(started_at)s\n"
"Django version %(version)s, using settings %(settings)r\n"
"Starting development server at http://%(addr)s:%(port)s/\n"
"Quit the server with %(quit_command)s.\n"
) % {
"started_at": now,
"version": self.get_version(),
"settings": settings.SETTINGS_MODULE,
"addr": '[%s]' % self.addr if self._raw_ipv6 else self.addr,
"port": self.port,
"quit_command": quit_command,
})
# django.core.management.base forces the locale to en-us. We should
# set it up correctly for the first request (particularly important
# in the "--noreload" case).
translation.activate(settings.LANGUAGE_CODE) try:
handler = self.get_handler(*args, **options)
run(self.addr, int(self.port), handler,
ipv6=self.use_ipv6, threading=threading)
except socket.error as e:
# Use helpful error messages instead of ugly tracebacks.
ERRORS = {
errno.EACCES: "You don't have permission to access that port.",
errno.EADDRINUSE: "That port is already in use.",
errno.EADDRNOTAVAIL: "That IP address can't be assigned-to.",
}
try:
error_text = ERRORS[e.errno]
except KeyError:
error_text = str(e)
self.stderr.write("Error: %s" % error_text)
# Need to use an OS exit because sys.exit doesn't work in a thread
os._exit(1)
except KeyboardInterrupt:
if shutdown_message:
self.stdout.write(shutdown_message)
sys.exit(0)

The meat and potatoes of this code is the following two lines -

handler = self.get_handler(*args, **options)
run(self.addr, int(self.port), handler, ipv6=self.use_ipv6, threading=threading)

Before looking into the code, it's better to have a review about PEP333 (WSGI), or just take a look at here

What are the two roles played by Django?

Django, itself, is a web framwork which complies with WSGI. From the framework side, it should provides one application handler(callable) wich takes in two parameters: environ and start_response. At the same time, as Django also provides one web server, it needs to provide a mechnisam to invoke the application handler(callable) for each request it receives from an Http client.

OK, after understanding these two roles can we move on to look at the code mentioned above.

How the application and web server interact?

First, let's focus on the application handler.

handler = self.get_handler(*args, **options)

The handler is the one either we configured in Django settings.py or by calling "get_wsgi_application()", seen from the method get_internal_wsgi_application() which is called by get_handler()

Note Django project will create an application by default, and it's also created by calling

"get_wsgi_application()"

In "settings.py" -

WSGI_APPLICATION = '.wsgi.application'

In "wsgi.py":

from django.core.wsgi import get_wsgi_application

application = get_wsgi_application()

def get_internal_wsgi_application():
"""
Loads and returns the WSGI application as configured by the user in
``settings.WSGI_APPLICATION``. With the default ``startproject`` layout,
this will be the ``application`` object in ``projectname/wsgi.py``. This function, and the ``WSGI_APPLICATION`` setting itself, are only useful
for Django's internal servers (runserver, runfcgi); external WSGI servers
should just be configured to point to the correct application object
directly. If settings.WSGI_APPLICATION is not set (is ``None``), we just return
whatever ``django.core.wsgi.get_wsgi_application`` returns. """
from django.conf import settings
app_path = getattr(settings, 'WSGI_APPLICATION')
if app_path is None:
return get_wsgi_application() try:
return import_string(app_path)
except ImportError as e:
msg = (
"WSGI application '%(app_path)s' could not be loaded; "
"Error importing module: '%(exception)s'" % ({
'app_path': app_path,
'exception': e,
})
)
six.reraise(ImproperlyConfigured, ImproperlyConfigured(msg),
sys.exc_info()[2])

Let's continue looking at function get_wsgi_application() -

def get_wsgi_application():
"""
The public interface to Django's WSGI support. Should return a WSGI
callable. Allows us to avoid making django.core.handlers.WSGIHandler public API, in
case the internal WSGI implementation changes or moves in the future. """
django.setup()
return WSGIHandler()

Please note the specification of WSGIHandler does comply with PEP333: take in "environ" and "start_response".

class WSGIHandler(base.BaseHandler):
initLock = Lock()
request_class = WSGIRequest def __call__(self, environ, start_response):
# Set up middleware if needed. We couldn't do this earlier, because
# settings weren't available.
if self._request_middleware is None:
with self.initLock:
try:
# Check that middleware is still uninitialized.
if self._request_middleware is None:
self.load_middleware()
except:
# Unload whatever middleware we got
self._request_middleware = None
raise set_script_prefix(get_script_name(environ))
signals.request_started.send(sender=self.__class__)
try:
request = self.request_class(environ)
except UnicodeDecodeError:
logger.warning('Bad Request (UnicodeDecodeError)',
exc_info=sys.exc_info(),
extra={
'status_code': 400,
}
)
response = http.HttpResponseBadRequest()
else:
response = self.get_response(request) response._handler_class = self.__class__ status = '%s %s' % (response.status_code, response.reason_phrase)
response_headers = [(str(k), str(v)) for k, v in response.items()]
for c in response.cookies.values():
response_headers.append((str('Set-Cookie'), str(c.output(header=''))))
start_response(force_str(status), response_headers)
return response

So far, we have figure out the application handler. Now let's turn to web server and see the function "run" in the module "django.core.servers.basehttp.py".

def run(addr, port, wsgi_handler, ipv6=False, threading=False):
server_address = (addr, port)
if threading:
httpd_cls = type(str('WSGIServer'), (socketserver.ThreadingMixIn, WSGIServer), {})
else:
httpd_cls = WSGIServer
httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
httpd.set_app(wsgi_handler)
httpd.serve_forever()

Here it will create a WSGIServer object, please note that the server will have a "WSGIRequestHandler" and also take into account wsgi appliation (wsgi_handler) just created.

Note: Django WSGIServer inherited from python provided "WSGIServer" - WSGIServer from the module

"simple_server.py" which can be found in the folder "/Lib/wsgiref/"

"WSGIRequestHandler" also inherited from python provided "WSGIRequestHandler", just like "WSGIServer"

WSGIRequestHandler has a method "run", which will do the real stuff. Please note this function specification also complies with PEP333, and it will call the WSGI application handler passing in the environ and start_response function.

def run(self, application):
"""Invoke the application"""
# Note to self: don't move the close()! Asynchronous servers shouldn't
# call close() from finish_response(), so if you close() anywhere but
# the double-error branch here, you'll break asynchronous servers by
# prematurely closing. Async servers must return from 'run()' without
# closing if there might still be output to iterate over.
try:
self.setup_environ()
self.result = application(self.environ, self.start_response)
self.finish_response()
except:
try:
self.handle_error()
except:
# If we get an error handling an error, just give up already!
self.close()
raise # ...and let the actual server figure it out.

Learning Django: the hard way (1)的更多相关文章

  1. Learning Django Resources

    Learning Django Django makes it easier to build better Web apps more quickly and with less code. Web ...

  2. django-filter version 2.0 改动

    今天使用django-filter时候遇到了下面这个问题: django-filter: TypeError at /goods/ init() got an unexpected keyword a ...

  3. Python Django Learning Notes..

    The first time I came across django was last month.. Since then I was considering it as the better c ...

  4. 【Machine Learning】Python开发工具:Anaconda+Sublime

    Python开发工具:Anaconda+Sublime 作者:白宁超 2016年12月23日21:24:51 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现 ...

  5. Machine and Deep Learning with Python

    Machine and Deep Learning with Python Education Tutorials and courses Supervised learning superstiti ...

  6. Django Web开发【1】Django简介

    前言 看完<Django Book>之后, 总想找个实例来实战开发下,无奈国内Django的书籍相当少,只能从英文书籍中吸取养料,偶然之后得到Learning Website Develo ...

  7. 利用python web框架django实现py-faster-rcnn demo实例

    操作系统.编程环境及其他: window7  cpu  python2.7  pycharm5.0  django1.8x 说明:本blog是上一篇blog(http://www.cnblogs.co ...

  8. Django学习(2)数据宝库

    数据库是一所大宝库,藏着各种宝贝.一个没有数据库的网站,功能有限.在Django中,支持的数据库有以下四种: SQLite3 MySQL PostgreSQL Oracle 其中SQLite3为Dja ...

  9. Django学习(4)表单,让数据库更美好

    表单,在HTML中的标签为<form></form>,在网页中主要负责数据采集功能.我们在浏览网站时,常常会碰到注册账号.账号登录等,这就是表单的典型应用. 在Django学习 ...

随机推荐

  1. 从Ubuntu12.04LTS到Foreda19再到Foreda8

    装Ubuntu的初衷是以为它能识别我的PCI无线网卡,但装了两遍没有做到. 昨天在Ubuntu装jdk7,其过程与正常Linux安装jdk差别不小,有点背离的意思.另外VI的用法也和正常Unix/Li ...

  2. 如何使用奥特歌词制作双语LRC字幕

    AutoLyric(奥特歌词)使用常见问题和技巧 1.如何设置桌面歌词字体的大小? 桌面歌词字体的大小由桌面歌词窗口的缩放来条设置,把鼠标放在桌面歌词上稍后就会出现半透明的窗口,对该窗口就可以进行拖动 ...

  3. 【CSS】瀑布流布局的两种方式:传统多列浮动和绝对定位布局

    传统多列浮动 各列固定宽度,并且左浮动: 一列中的数据块为一组,列中的每个数据块依次排列即可: 更多数据加载时,需要分别插入到不同的列上: 优点: (1)布局简单,应该说没啥特别的难点: (2)不用明 ...

  4. ADS ARM 汇编和GNU ARM汇编

    Linux/Unix内核源代码用的编译器是GCC,而GCC采用的是AT&T的汇编格式,这与ADS下使用的汇编格式是不同的. 两种汇编格式的部分对比如下: GNU ARM汇编 ADS ARM汇编 ...

  5. Modifying a Request or Response

    To make custom changes to web requests and responses, use FiddlerScript to add rules to Fiddler's On ...

  6. fiddler自动保存请求报文

    先来看一张自动保存的请求片段   重点来了,下面是实现的js代码 操作步骤 Fiddler菜单 >> Rules >> Customize Rules 如果提示没有下载Fidd ...

  7. vue 数组 新增元素 响应式原理 7种方法

    1.问题 思考一个问题,以下代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8" ...

  8. 解决vsftpd乱码

    不管是中文环境还是英文环境,需要改的地方如下: /etc/sysconfig/i18n 其内容改为: LANG="zh_CN.GB2312"SYSFONT="latarc ...

  9. KnockoutJS + My97DatePicker

    如何将Knockoutjs和其他脚本库结合使用?这里给出一个Knockoutjs与my97datepicker配合使用的例子,例子中使用了ko的自定义绑定功能: ko.bindingHandlers. ...

  10. 统计一个文件中出现字符'a'的次数

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #统计一个文件中出现字符'a'的次数 #http://www.cnblogs.com/hongten/p/ho ...