User Tools

Site Tools


django:quick-reference

Django Quick Reference

KISS

Keep It Simple, Stupid

Code Placement

Fat Models, Helper Modules, Thin Views, Stupid Templates

Imports

# Stdlib imports
from math import sqrt
from os.path import abspath
 
# Core Django imports
from django.db import models
from django.utils.translation import ugettext_lazy as _
 
# Third-party app imports
from django_extensions.db.models import TimeStampedModel
 
# Imports from your apps
from otherapp.models import BananaSplit
from .models import WaffleCone
from .forms import WaffleConeForm

Code Style

Remember Guido's words: code is read much more often than it is written (reference).

#! /usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
 
_a_global_var = 2   # so it won't get imported by 'from foo import *'
_b_global_var = 3
 
THIS_IS_A_CONSTANT = 'whatever'
 
# 2 empty lines between top-level funcs + classes
def this_is_a_function():
    pass
 
 
class ThisIsSomeClass(object):
    """Write docstrings for ALL public classes, funcs and methods.
 
    Class and exception names are CapWords.
    """
 
    a = 2
    b = 4
    _internal_variable = 3
    class_ = 'foo'      # trailing underscore to avoid conflict with builtin
 
    # this will trigger name mangling to further discourage use from outside
    # this is also very useful if you intend your class to be subclassed, and
    # the children might also use the same var name for something else; e.g. 
    # for simple variables like 'a' above. Name mangling will ensure that 
    # *your* a and the children's a will not collide.
    __internal_var = 4  
 
    # NEVER use double leading and trailing underscores for your own names
    __nooooooodontdoit__ = 0
 
    # some examples of how to wrap code to conform to 79-columns limit:
    def __init__(self):
        pass
 
    # 1 empty line between in-class def'ns
    def this_is_a_method(self, x, y=None):
        """Method and function names are lower_case_with_underscores.
 
        Always use self as first arg.
        """
        pass
 
    @classmethod
    def bar(cls):
        """Use cls!"""
        pass
 
    # This will give a better default string representation of the class
    def __unicode__(self):
        return u"Class Representation"

Modified from http://wwd.ca/blog/2009/07/09/pep-8-cheatsheet/

Settings

Have multiple settings files, ALL of them tracked in the version control system.

settings/
    __init__.py
    base.py
    local.py
    staging.py
    test.py
    production.py

Everything should import from base.py

# settings/local.py
from .base import *

Use environment variables for production

# settings/base.py
import os
 
from django.core.exceptions import ImproperlyConfigured
 
def get_env_variable(var_name):
    """ Get the environment variable or return exception """
    try:
        return os.environ[var_name]
    except KeyError:
        error_msg = "Set the %s environment variable" % var_name
        raise ImproperlyConfigured(error_msg)

And never hardcode paths, use these helper functions.

# At the top of settings/base.py
from os.path import join, abspath, dirname
 
here = lambda *x: join(abspath(dirname(__file__)), *x)
PROJECT_ROOT = here("..", "..")
root = lambda *x: join(abspath(PROJECT_ROOT), *x)
 
# Configuring MEDIA_ROOT
MEDIA_ROOT = root('media')
 
# Additional locations of static files
STATICFILES_DIRS = (
    root('assets'),
)
 
# Configuring TEMPLATE_DIRS
TEMPLATE_DIRS = (
    root('templates'),
)

Execute a diff between default and project settings with this command.

manage.py diff-settings

Requirements

Have multiple requirements files, at least one for every environment.

requirements/
    base.txt
    local.txt
    staging.txt
    test.txt
    production.txt

Everything should import from base.txt

-r base.txt
django-debug-toolbar==0.9.4

If a requirements.txt is needed in the top-level directory, just put this inside.

-r requirements/production.txt

Models

Add Indexes only as needed. It is best to start without any index and just add them when you really need them.

Regarding inheritance, avoid Multi-Table Inheritance when possible. Abstract base classes are a better alternative.

Start Normalized. All data should be stored only once in the database, and reference it from multiple models. Before Denormalizing, try to use the Cache. And Denormalize Only If Absolutely Needed.

Null and Blank

In string-based fields (CharField, TextField, SlugField, EmailField, …) do NOT use null=True.

With BooleanField do NOT use null=True or blank=True (NullBooleanField is available for this case).

Class-Based Views

The http://ccbv.co.uk/ website contains a list of all the Django's class-based generic views, with detailed descriptions, full methods and attributes.

To view the auto-generated documentation of the CBV (including the UML) http://epydoc.pythondiary.com/generic-views/ has a really good reference.

When creating CBV, mixins go to the left side, and the classes provided by Django to the far right side.

Testing

Test messages framework

self.assertIn("Content of the message to check", response.cookies['messages'].value)

Test form field errors

self.assertIsNotNone(response.context['form']['field_to_test'].errors)
self.assertEqual(form["field_to_test"].errors, [force_text(form.error_messages['error_message'])])

Check that the cookie is only valid for the current browser session:

self.assertEquals(self.client.cookies.get('sessionid')['expires'], "")

Check that the cookie has an expiration date set:

self.assertNotEquals(self.client.cookies.get('sessionid')['expires'], "")

Utils

 
django/quick-reference.txt · Last modified: 2013/04/09 14:53 by josep