Hosting a Django application on a CherryPy server

Recently at work I’ve had the requirement to host a Django application in a CherryPy server. I first looked for various projects I knew were doing just that. Unfortunately, after trying them I was rather disapointed. Their approach is to provide a command similar to the famous Django runserver‘s one but I’ve found it to be more complex than necessary. So I wrote my own module that performs those operations by staying much closer to how CherryPy does work, most specifically by using the process bus coming with CherryPy.

I’m sharing a stripped down version of the module I wrote which shows how one could host a Django application in a CherryPy server. Hopefully this might help some of you.

# Python stdlib imports
import sys
import logging
import os, os.path
# Third-party imports
import cherrypy
from cherrypy.process import wspbus, plugins
from cherrypy import _cplogging, _cperror
from django.conf import settings
from django.core.handlers.wsgi import WSGIHandler
from django.http import HttpResponseServerError
class Server(object):
    def __init__(self):
        self.base_dir = os.path.join(os.path.abspath(os.getcwd()), "cpdjango")
        conf_path = os.path.join(self.base_dir, "..", "server.cfg")
        # This registers a plugin to handle the Django app
        # with the CherryPy engine, meaning the app will
        # play nicely with the process bus that is the engine.
        DjangoAppPlugin(cherrypy.engine, self.base_dir).subscribe()
    def run(self):
        engine = cherrypy.engine
        if hasattr(engine, "console_control_handler"):
class DjangoAppPlugin(plugins.SimplePlugin):
    def __init__(self, bus, base_dir):
        CherryPy engine plugin to configure and mount
        the Django application onto the CherryPy server.
        plugins.SimplePlugin.__init__(self, bus)
        self.base_dir = base_dir
    def start(self):
        self.bus.log("Configuring the Django application")
        # Well this isn't quite as clean as I'd like so
        # feel free to suggest something more appropriate
        from cpdjango.settings import *
        app_settings = locals().copy()
        del app_settings['self']
        self.bus.log("Mounting the Django application")
        self.bus.log("Setting up the static directory to be served")
        # We server static files through CherryPy directly
        # bypassing entirely Django
        static_handler ="/", dir="static",
        cherrypy.tree.mount(static_handler, '/static')
class HTTPLogger(_cplogging.LogManager):
    def __init__(self, app):
        _cplogging.LogManager.__init__(self, id(self), cherrypy.log.logger_root) = app
    def __call__(self, environ, start_response):
        Called as part of the WSGI stack to log the incoming request
        and its response using the common log format. If an error bubbles up
        to this middleware, we log it as such.
            response =, start_response)
            self.access(environ, response)
            return response
            return HttpResponseServerError(_cperror.format_exc())
    def access(self, environ, response):
        Special method that logs a request following the common
        log format. This is mostly taken from CherryPy and adapted
        to the WSGI's style of passing information.
        atoms = {'h': environ.get('REMOTE_ADDR', ''),
                 'l': '-',
                 'u': "-",
                 't': self.time(),
                 'r': "%s %s %s" % (environ['REQUEST_METHOD'], environ['REQUEST_URI'], environ['SERVER_PROTOCOL']),
                 's': response.status_code,
                 'b': str(len(response.content)),
                 'f': environ.get('HTTP_REFERER', ''),
                 'a': environ.get('HTTP_USER_AGENT', ''),
        for k, v in atoms.items():
            if isinstance(v, unicode):
                v = v.encode('utf8')
            elif not isinstance(v, str):
                v = str(v)
            # Fortunately, repr(str) escapes unprintable chars, \n, \t, etc
            # and backslash for us. All we have to do is strip the quotes.
            v = repr(v)[1:-1]
            # Escape double-quote.
            atoms[k] = v.replace('"', '\\"')
            self.access_log.log(logging.INFO, self.access_log_format % atoms)
if __name__ == '__main__':

You can find the code along side a minimal Django application showing how this works here (BSD licence). I used Django 1.3 to generate a default project but the code above works well with older version of Django.

Edit 16/03/2012: Thanks to Damien Tougas, I’ve wrapped up a better recipe for hosting a Django application into a CherryPy application server.


  1. Damien Tougas
      September 5, 2011

    Thanks for this. I took your script and made some changes that you may (or may not) be interested in. Namely, a more elegant way to load the Django settings, and secondly, the static information is loaded from the Django config rather than being hard-coded. If you send me an email I would be happy to send you a copy.

    • Bill Tsay
        November 8, 2011

      Hi Damien,

      Wonder if you can email me the copy of your modified code? We have a need to embed django into cherrypy too.



      • Sylvain Hellegouarch
          December 13, 2011

        Hi Bill,

        I apologise for taking so long to approve your comments… WP didn’t notify me for some reason.

    • Krishna
        November 9, 2011

      Damien, We are trying to the exact thing – run CherryPy and Django apps in the on the CherryPy server. Our need is to be able to share the session and auth done in CherryPy with apps being developed in Django.

      Can you share the changes you made?


      • Sylvain Hellegouarch
          December 13, 2011

        Hey Krishna,

        Like for Bill, I apologise for taking so long to approve your comment.

  2. Damien Tougas
      December 13, 2011

    I would be happy to share my changes with anyone that would like them. If you are interested, contact me on my blog contact page.

  3. Paul Hartley
      November 6, 2012

    I am looking for a way to avoid using apache for my django application. The traffic will be small so I will try this cherrypy solution.
    The three .py files on Lawouach work once the name of the application is changed.
    Not sure why the main .py file is – I renamed it!
    Thank you Sylvain and Damien for this project.

    • Sylvain Hellegouarch
        November 6, 2012

      Great to hear it helped.

  4. Nick Bonfatti
      June 8, 2013

    I can’t get this to work. It starts up OK when I rename to, and run python . in the folder, but when i connect to the website, the chat window says Connection closed by server: 1006 "" and if I try sending output, it goes to the console but doesn’t show up in the window. I pip installed django, cherrypy, and ws4py. Is there something I’m missing?

    • Nick Bonfatti
        June 8, 2013

      forgot to mention the bowser: newest version of chrome.

    • Sylvain Hellegouarch
        June 8, 2013

      Hi, this isn’t the right channel for technical questions. Would you mind using the ws4py mailing list instead?

      In any case, have you tried this code?

      • Nick Bonfatti
          June 8, 2013

        Sorry about that, no problem. I’ll check out that code too.

  5. baxeico
      October 14, 2013

    Thank you, this post was very helpful for a work I described here: