2014-08-28

Pyramid Single File Tasks Tutorial runs under mod_wsgi

I try to make the Pyramid Single File Tasks Tutorial runs under mod_wsgi, and figure that two changes are needed to make it runs both standalone (http://localhost:6543) and under mod_wsgi (http://localhost/task).

I would like to share these two changes:

[tasks.py]

FROM:
if __name__ == '__main__':
    # configuration settings
    settings = {}
    settings['reload_all'] = True
    settings['debug_all'] = True
    settings['mako.directories'] = os.path.join(here, 'templates')
    settings['db'] = os.path.join(here, 'tasks.db')
    # session factory
    session_factory = UnencryptedCookieSessionFactoryConfig('itsaseekreet')
    # configuration setup
    config = Configurator(settings=settings, session_factory=session_factory)
    # routes setup
    config.add_route('list', '/')
    config.add_route('new', '/new')
    config.add_route('close', '/close/{id}')
    # static view setup
    config.add_static_view('static', os.path.join(here, 'static'))
    # scan for @view_config and @subscriber decorators
    config.scan()
    # serve app
    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()


TO:
def get_app():
    # configuration settings
    settings = {}
    settings['reload_all'] = True
    settings['debug_all'] = True
    settings['mako.directories'] = os.path.join(here, 'templates')
    settings['db'] = os.path.join(here, 'tasks.db')
    # session factory
    session_factory = UnencryptedCookieSessionFactoryConfig('itsaseekreet')
    # configuration setup
    config = Configurator(settings=settings, session_factory=session_factory)
    # routes setup
    config.add_route('list', '/')
    config.add_route('new', '/new')
    config.add_route('close', '/close/{id}')
    # static view setup
    config.add_static_view('static', os.path.join(here, 'static'))
    # scan for @view_config and @subscriber decorators
    config.scan()
    # serve app
    app = config.make_wsgi_app()
    return app

application = get_app()
if __name__ == '__main__':
    server = make_server('0.0.0.0', 8080, application)
    server.serve_forever()


[templates/layout.mako]

FROM:
  <link rel="shortcut icon" href="/static/favicon.ico">
  <link rel="stylesheet" href="/static/style.css">

TO:
  <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico">
  <link rel="stylesheet" href="${request.application_url}/static/style.css">


I am using Debian 6, and the following is the relevant section in /etc/apache2/mods-enabled/wsgi.conf, where task.wsgi is a symbolic link to task.py in the same directory.

WSGIApplicationGroup %{GLOBAL}
WSGIPassAuthorization On

WSGIDaemonProcess pyramid user=david group=david threads=4 \
   python-path=/home/david/tasks/env/lib/python2.6/site-packages

WSGIScriptAlias /task /home/david/tasks/env/task/task.wsgi

<Directory /home/david/tasks/env>
  WSGIProcessGroup pyramid
  Order allow,deny
  Allow from all
</Directory>


[end]

2014-08-25

Django logging setting, part 2

This is a follow up to the Django logging setting post in 2013.
This time, it makes use of the hierarchical nature of the logging system, removing the need to define a separate section for each app in the settings file. Notice the dot after the <project_name> in U_LOGGER_ROOT.
=== settings.py ===

U_LOGFILE_NAME = r'/path/to/log.txt'
U_LOGFILE_SIZE = 1 * 1024 * 1024
U_LOGFILE_COUNT = 2
U_LOGGER_ROOT = '<project_name>.'

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format' : "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
        },
    },
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        }
    },
    'handlers': {
        'logfile': {
            'level':'DEBUG',
            'class':'logging.handlers.RotatingFileHandler',
            'filename': U_LOGFILE_NAME,
            'maxBytes': U_LOGFILE_SIZE,
            'backupCount': U_LOGFILE_COUNT,
            'formatter': 'standard',
        },
    },
    'loggers': {
        U_LOGGER_ROOT.rstrip('.'): {
            'handlers': ['logfile'],
            'level': 'DEBUG',
            'propagate': True,
        },
    }
}

=== in any app module ===

import logging
from  import settings

logger = logging.getLogger(settings.U_LOGGER_ROOT + __name__)
logger.info('some useful information')

2014-08-19

Showing the description of a choice in a Django template

Sometimes you would like to show the description of a choice instead of its code in a Django template.  One way to achieve that is to define a property in the model and referencing it in a template as below:

    {{ object.status_desc }}

=== models.py ===

from django.db.models import IntegerField, CharField, Model

class Invoice(Model):

    STATUS_TYPE = (
        ('A', 'Active'),
        ('I', 'Inactive'),
    )

    invoice_no = IntegerField()
    status = CharField(max_length=1, choices=STATUS_TYPE, default='A')

    @property
    def status_desc(self):
        return dict(self.STATUS_TYPE)[self.status]
 
EDITED 2015-07-13 Turn out there is a django function for this purpose: Model.get_FOO_display()

2014-08-16

Django File Upload

The following is a minimalistic but complete example of handling file upload in Django 1.6.


=== forms.py ===

from django import forms

class UploadFileForm(forms.Form):
    file = forms.FileField()

=== views.py ===

import os.path
from django.http import HttpResponse
from django.shortcuts import render
from .forms import UploadFileForm
          
def home(request):
    if request.method == 'POST':
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            handle_uploaded_file(request.FILES['file'])
            return HttpResponse("Upload Successful.")
    else:
        form = UploadFileForm()
    return render(request, 'home.html', {'form': form})

def handle_uploaded_file(f):
    with open(os.path.join('/path/to/', f.name), 'wb+') as destination:
        for chunk in f.chunks():
            destination.write(chunk)

=== home.html ===
<html>
<body>

<form action="" method="post" {% if form.is_multipart %}enctype="multipart/form-data"{% endif %}>
  {{ form.as_p }}
  {% csrf_token %}
  <input type="submit" value="Submit" />
</form>

</body>
</html>