all will be well finally

Django application as a stand-alone desktop application

with 2 comments

There are few options available to package Django as a stand-alone desktop apps. They are:

  • cx_freeze
  • PyInstaller
  • Py2exe
  • dbuilder.py

Siddharta already packaged Django as a windows application using cx_freeze. I based my experiment on his entry. Though he has explained it pretty well, being a python/django newbie, I needed a lot more than what he explained in his article.

I read pages after pages and spent about a week in making a desktop app of SOL. I didn’t make much progress and was very frustrated. (I wrote about it here). After a time, I switched to py2exe, as I was not able to locate much info about cx_freeze.

I used py2exe, sqlite and cherrypy for packaging as a desktop app. As it is only for demo purpose, I would’ve preferred to have the default development server itself. But I don’t know how to do it; and info about cherrypy was readily available.

Fundamentally, you need to import all of the required Django modules. That takes the most of the time. Whenever I got, ‘module’ object has no attribute ‘xxxx’, I had to trace it to one of Django’s module and import it.

Once I got all of the modules, I had to integrate with CherryPy. It wasn’t difficult as info was already available. However, admin css were not delivered. I read through ‘AdminMediaHandler’ code and understood that I had to pass an absolute path for admin media folder. That got me both local media and admin media done.

I had to make only one change to settings.py. I replaced

os.path.dirname(os.path.abspath(__file__))

with

sys.argv[0]

I was glad that I persevered to make this. Here I post the setup, imports and the build script with the hope that it might be useful for others.

This is the first file – sol.py

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import os, sys
os.environ['DJANGO_SETTINGS_MODULE'] = "settings"
#these pertain to your application
import sol.models
import sol.views
import urls
import manage
import settings
#these are django imports
import django.template.loaders.filesystem
import django.template.loaders.app_directories
import django.middleware.common
import django.contrib.sessions.middleware
import django.contrib.auth.middleware
import django.middleware.doc
import django.contrib.auth
import django.contrib.contenttypes
import django.contrib.sessions
import django.contrib.sessions.backends.db
import django.contrib.sites
import django.contrib.admin
import django.core.cache.backends
import django.db.backends.sqlite3.base
import django.db.backends.sqlite3.introspection
import django.db.backends.sqlite3.creation
import django.db.backends.sqlite3.client
import django.template.defaulttags
import django.template.defaultfilters
import django.template.loader_tags
import django.contrib.admin.urls
from django.conf.urls.defaults import *
import django.contrib.admin.views.main
import django.core.context_processors
import django.contrib.auth.views
import django.contrib.auth.backends
import django.views.static
import django.contrib.admin.templatetags.adminmedia
import django.contrib.admin.templatetags.adminapplist
import django.contrib.admin.templatetags.admin_list
import django.contrib.admin.templatetags.admin_modify
import django.contrib.admin.templatetags.log
import django.contrib.admin.views.auth
import django.contrib.admin.views.doc
import django.contrib.admin.views.template
import django.conf.urls.shortcut
import django.views.defaults
#dont need to import these pkgs
#need to know how to exclude them
import email.mime.audio
import email.mime.base
import email.mime.image
import email.mime.message
import email.mime.multipart
import email.mime.nonmultipart
import email.mime.text
import email.charset
import email.encoders
import email.errors
import email.feedparser
import email.generator
import email.header
import email.iterators
import email.message
import email.parser
import email.utils
import email.base64mime
import email.quoprimime
import django.core.cache.backends.locmem
import django.templatetags.i18n
import django.views.i18n
#let us hook up cherrypy
#is it possible to hook up the dev server itself?
from cherrypy import wsgiserver
import cherrypy
from django.core.handlers.wsgi import WSGIHandler
from django.core.servers.basehttp import AdminMediaHandler
if __name__ == "__main__":
    print '*****************************************************'
    print 'Open your browser and point to http://localhost:8000'
    print 'To close, press ctrl-c'
    print ''
    print 'local user id is: jjude; password is also jjude'
    print 'admin user id is: admin; password is also admin'
    print '*****************************************************'
    os.environ["DJANGO_SETTINGS_MODULE"] = "settings"
    # Set up site-wide config first so we get a log if errors occur.
    cherrypy.config.update({'environment': 'production',
                            'log.error_file': 'site.log',
                            'log.screen': False})
    try:
        sys.path.insert(0,"..")
        #2nd param to AdminMediaHandler should be absolute path to the admin media files
        cherrypy.tree.graft(AdminMediaHandler(WSGIHandler(),media_dir=os.path.dirname(os.path.abspath(sys.argv[0])) + settings.ADMIN_MEDIA_PREFIX), '/')
        cherrypy.server.socket_port = 8000
        cherrypy.server.quickstart()
        cherrypy.engine.start()
    except KeyboardInterrupt:
        cherrypy.server.stop()

This is setup.py

from distutils.core import setup
import py2exe
import glob
setup(
    options = {"py2exe": {"compressed": 1,
                          "optimize": 2,
                          "ascii": 1,
                          "bundle_files": 1,
                          "packages":["encodings"],
                           "excludes" : ["pywin", "pywin.debugger", "pywin.debugger.dbgcon","pywin.dialogs",
                                       "pywin.dialogs.list","Tkconstants","Tkinter","tcl"],
                            }},
    #these are the data files like templates, site media and admin media
    data_files = [(".",["sol.db"]),
        ("templates",glob.glob("templates\*.*")),
        #("files",glob.glob("files\*.*")),
        ("media",glob.glob("media\*.*")),
        ("media\css",glob.glob("media\css\*.*")),
        ("templates\admin",glob.glob("C:\Python25\Lib\site-packages\django\contrib\admin\templates\admin\*.*")),
        ("templates\admin\auth\user",glob.glob("C:\Python25\Libsite-packages\django\contrib\admin\templates\admin\auth\user\*.*")),
        ("templates\admin_doc",glob.glob("C:\Python25\Lib\site-packages\django\contrib\admin\templates\admin_doc\*.*")),
        ("templates\widget",glob.glob("C:\Python25\Lib\site-packages\django\contrib\admin\templates\widget\*.*")),
        ("templates\registration",glob.glob("C:\Python25\Lib\site-packages\django\contrib\admin\templates\registration\*.*")),
        ("adminmedia\css",glob.glob("C:\Python25\Lib\site-packages\django\contrib\admin\media\css*.*")),
        ("adminmedia\js",glob.glob("C:\Python25\Lib\site-packages\django\contrib\admin\media\js\*.*")),
        ("adminmedia\img",glob.glob("C:\Python25\Lib\site-packages\django\contrib\admin\media\img\*.*")),
        ],
    zipfile = None,
    console=['sol.py'],
    )

This is the build script.

python -OO setup.py py2exe --b 2 --optimize 2 --dist-dir y:sol
rd /s /q build

You can download the demo from code.google.com. I’ll keep updating these scripts as I learn more. So get the updated scripts from code.google.com.

If you are to engage in such a exercise, you need lot of patience.

Next steps:

  • Integrate with UPX
  • Integrate with Inno Setup (or some other windows setup makers)

I’m sure this can be done in a better way. Feel free to comment.

Reference:

  • Deploying a Django app on the desktop
  • My frustration with packaging django
  • Hosting a Django Site with Pure Python
  • Py2exe

Related Posts:

  • S-I-P goes online
  • Understanding Django Template Tags
  • Say In Pixels – a photoblog in Django

发表回复