Friday, February 8, 2013

Two Scoops of Django Review

I've just finished reading Two Scoops of Django: Best Practices for Django 1.5 by Daniel Greenfield and Audrey Roy, currently for sale as an e-book for $12 and easily the least expensive (except free) form of professional investment a Django developer can make.


It's not a "I couldn't put it down!" book; rather you will want to put it down about every four pages so you can implement an idea before moving on to the next topic.

I've had one big Django project under my belt, that was my full time gig for over a year (the now defunct 10LocalCoupons.com). I had a team of developers and, for any of us, it was our first Django project and our first python project -- not for a lack of trying to find experienced djangonauts. I recall finishing the django tutorial and then looking for a resource to learn best practices and not finding much. This book fills that need well.

I'm currently working on a solo project in Django, and am still in the early stages, so I easily revamped my project to include many of the best practices espoused here, the first being project layout. Having a project root for your apps and within that a configuration root for settings, urls and wsgi makes sense to me.

The next idea was a sane settings management that avoids many of the pitfalls we encountered in the past: tests that fail in different environments due to different settings, local settings that don't get tracked in source control, and forcing developers to track changes to local-example.py settings.

Other topics include best practices for class based views, templates, how to structure apps, and plenty more.
So it took a week to get through the book because I kept stopping to put the ideas to work.

Plently of the big ideas espoused I already adhere to: using South for database migrations, pip and virtualenv for environment building, and others. Some of the more subtle ones, like using environment variables for your secret key and using a separate requirements file for each environment don't apply to my early stage project yet but in the future I'll be sure to adopt.

Some of the content in the book should be fleshed out a bit more. For example, the book recommends using logging and gives reasons why, but doesn't show logging settings or an example of how to use it. 
For those interested, here is how I use logging in my local development environment where I want to see all things my code is doing; most of this is bolierplate django settings but I've left them in so you can see how it interacts with my customizations. I'm not suggesting that my method is to be considered a best practice, just something to get you started. The console handler and the catchall logger '' are the key parts.

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        }
    },
    'formatters': {
        'verbose': {
            'format':
                '%(asctime)s %(name)s %(levelname)s %(module)s %(message)s',
            'datefmt': '%Y-%m-%d %H:%M:%S'
        },
        'simple': {
            'format': '%(asctime)s %(name)s %(levelname)s %(message)s',
            'datefmt': '%Y-%m-%d %H:%M:%S'
        },
    },
    'handlers': {
        'null': {
            'level': 'DEBUG',
            'class': 'django.utils.log.NullHandler',
            },
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler'
            },
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'simple',
            },
        'file': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': '/var/log/my_project/my_project.log',
            'maxBytes': 20000000,
            'backupCount': 5,
            'formatter': 'verbose',
            }
    },
    'loggers': {
        'django': {
            'handlers': ['null'],
            'propagate': False,
            'level': 'INFO',
            },
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
        'south': {
            'handlers': ['null'],
            'propagate': False,
            'level': 'INFO',
            },
        '':{
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
            'propagate': True,
            'disabled': False
        }
    }
}

And here is a drop-dead simple use of logging:
import logging

LOG = logging.getLogger(__name__)
LOG.setLevel(logging.DEBUG)

def my_function(my_arg):
    my_var = my_arg + 1
    LOG.debug('my_var: %s' % my_var)
    return my_var

Another place that could have done with some easy examples is reStructuredText; I know its the fancy way to document and could clicked a link learn all about it, but this book could give a little bit more to whet my whistle than "study the documentation for reStructuredText and learn at least the basics." How do others use it for django apps?

This is a good book and well-worth the money. It'll make you more productive, and so is easily worth the money. I look forward to reading further revisions.

Two Scoops of Django: Best Practices for Django 1.5