Django Applications¶
A Django project typically consists of many applications declared in INSTALLED_APPS
. Django applications should follow the Unix philosopy of, “Do one thing and do it well.” [1], with a focus on being small and modular, mirroring Django’s “loose coupling” design philosophy [2].
James Bennett’s Reusable Apps talk at the first DjangoCon is an excellent primer on the subject of building good Django applications.
[1] | http://en.wikipedia.org/wiki/Unix_philosophy#McIlroy:_A_Quarter_Century_of_Unix |
[2] | https://docs.djangoproject.com/en/dev/misc/design-philosophies/#loose-coupling |
Code Organization¶
The only requirement of a Django application is that it provides a models.py
file. In practice, however, Django applications are made up of many different files. When building your own applications, follow common file naming conventions. Start with the framework Django provides via manage.py startapp <foo>
and build out from there as needed.
__init__.py
admin.py
context_processors.py
feeds.py
forms.py
managers.py
middleware.py
models.py
receivers.py
signals.py
templates/app_name/
templatetags/
__init__.py
app_name.py
tests.py
ortests/
urls.py
views.py
What lives in each of these files should be self-explanatory. Let’s dive into some of the meatier ones though.
Models¶
Style¶
Follow Django’s defined conventions for model code.
Make ‘em Fat¶
A common pattern in MVC-style programming is to build thick/fat models and thin controllers. For Django this translates to building models with lots of small methods attached to them and views which use those methods to keep their logic as minimal as possible. There are lots of benefits to this approach.
- DRY: Rather than repeating the same logic in multiple views, it is defined once on the model.
- Testable: Breaking up logic into small methods on the model makes your code easier to unit test.
- Readable: By giving your methods friendly names, you can abstract ugly logic into something that is easily readable and understandable.
For a good example of a fat model in Django, look at the definition of django.contrib.auth.models.User
.
Managers¶
Similar to models, it’s good practice to abstract common logic into methods on a manager. More specifically, you’ll probably want a chainable method that you can use on any queryset. This involves some boilerplate that I always forget, so here’s an example for (mostly) cutting and pasting:
import datetime
from django.db import models
from django.db.models.query import QuerySet
class PostQuerySet(QuerySet):
def live(self):
"""Filter out posts that aren't ready to be published"""
now = datetime.datetime.now()
return self.filter(date_published__lte=now, status="published")
class PostManager(models.Manager):
def get_query_set(self):
return PostQuerySet(self.model)
def __getattr__(self, attr, *args):
# see https://code.djangoproject.com/ticket/15062 for details
if attr.startswith("_"):
raise AttributeError
return getattr(self.get_query_set(), attr, *args)
class Post(models.Model):
# field definitions...
objects = PostManager()
This code will let you call our new method live both directly on the manager Post.objects.live()
and chain it on a queryset Post.objects.filter(category="tech").live()
. At the time of writing, there is an open bug to make this less painful.