Django settings template
The previous post contained a script1 to organize your Django settings into multiple files. You can just run this script inside of an existing project folder instead of doing everything by hand.
This one will be about my own settings additions. They are simpler to add in a new project than in the existing one, as opposed to allowing to split settings.py into different files, that you can do automagically in a pretty safe way.
Standard settings.py makes some basically wrong assumptions which are going against industry “best practice” and has several unconvenient defaults. I resolve these problem in my very own way, and I don’t expect it to be the best way possible (please make comments), but I thought if I had a common place to copy/paste or import default settings from that would be awesome, and I could share it. So here it goes.
The idea is that I have a settings.defaults2 module from where all my global defaults come. I just import it in the settings.base module and customize things.
If you don’t want to do it this way you can just copy/paste snippets from below or a repository.
Project structure
My default project structure is quite simple:
settings/
__init__.py
defaults.py
base.py
local.py?
urls.py
test_suite.py
project-specific-app1/ # I dislike having a different folder for apps
project-specific-app2/ # (it does not seem pythonic)
etc/ # several things like a database
templates/
static/ # handled automagically, don't touch it
uploads/
assets/
staticfiles/ # add files as you wish
css/ # for example
manage.py
__init__.py
requirements.txt
It’s actually better for bigger projects, but for bigger projects it’s hard to make something common and comprehensive. If you used my settings snippet, you should only create two folders in the very beginning:
mkdir etc static staticfiles
touch {etc,static,staticfiles}/touch.me # git can't add an empty folder
Paths
A very strange assumption Django’s settings are making is that your paths are same in all environments. They actually insist on placing absolute paths in settings - something you want to avoid. So I had to add this snippet every time a started a new Django project:
# into settings/defaults.py
import os
here = lambda * x: os.path.join(os.path.abspath(os.path.dirname(__file__)), *x)
PROJECT_ROOT = here('..')
root = lambda * x: os.path.join(os.path.abspath(PROJECT_ROOT), *x)
These two lambdas do some pretty simple thing: they are making a path. Usage is very simple:
SOME_COOL_FOLDER = root('etc', 'cool') # -> .../your-project/etc/cool/
They are platform independent (or seem to be). PROJECT_ROOT is just a convenient constant.
Project module
We’ll want to know a toplevel project module:
# into settings/defaults.py
PROJECT_MODULE = '.'.join(__name__.split('.')[:-2]) # cut .settings.base
Debug
DEBUG = True
TEMPLATE_DEBUG = DEBUG
Database
If I wanted my project not to use database, I’d actually not used Django (okay, I’m not completely honest). So all my projects do use it. I have strong opinions about production and deployment environment similarity, at least in the case of a database (more on this - in the later blog posts), so Sqlite databases are not really common to me. But they are, at least, a sane default.
# into settings/defaults.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': root('etc', 'development.db'),
}
}
Media and static
I have a common folder for statically served files with two subfolders: for uploads and for assets. So setting them is straightforward:
# into settings/defaults.py
MEDIA_ROOT = root('static', 'uploads')
MEDIA_URL = '/static/uploads/'
STATIC_ROOT = root('static', 'assets')
STATIC_URL = '/static/assets/'
ADMIN_MEDIA_PREFIX = '/static/assets/admin/'
STATICFILES_DIRS = (
root('staticfiles'),
)
We will add code to urls.py to serve them later. And you should just ask Nginx to serve3 the folder for production environment:
# into nginx configuration
location /static/ {
root .../your-project-folder/; # don't add /static/ to the path
}
I have no idea why uploads and assets are named “media” and “static” in Django. I guess it’s a legacy issue.
Urls
I like to keep global project urls in the settings/ folder. We should tell Django about this decision:
# into settings/defaults.py
ROOT_URLCONF = '%s.settings.urls' % PROJECT_MODULE
Now create settings/urls.py and copy this code there:
# into settings/urls.py
from django.conf.urls.defaults import patterns, include, url
from django.conf import settings
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)),
)
urlpatterns += staticfiles_urlpatterns()
if settings.DEBUG:
urlpatterns += patterns('',
url(r'^static/media/(?P<path>.*)$', 'django.views.static.serve', {
'document_root': settings.MEDIA_ROOT,
}),
)
This will enable admin, admindocs and serving files from your static/ directory in DEBUG environment.
Template directories
I use one directory for all project-specific templates and try to keep generic ones in app directories. I need to reflect this in settings:
# into settings/defaults.py
TEMPLATE_DIRS = (
root('templates'),
)
Installed apps
These ones seem mandatory, altrough I don’t really like south:
# into settings/defaults.py
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.admin',
'django.contrib.admindocs',
# external
'south',
# internal
# 'app1',
)
SITE_ID = 1
Logging
Logging is cool, please use it.
# into settings/defaults.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': ('%(levelname)s %(asctime)s |'
'%(pathname)s:%(lineno)d (in %(funcName)s) |'
' %(message)s ')
},
'simple': {
'format': '%(levelname)s %(message)s'
},
},
'handlers': {
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler'
},
'logfile': {
'level': 'DEBUG',
'class': 'logging.handlers.WatchedFileHandler',
'filename': root('%s.log' % PROJECT_MODULE),
'formatter': 'verbose',
},
},
'loggers': {
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': True,
},
PROJECT_MODULE: {
'handlers': ['logfile'],
'level': 'DEBUG'
}
}
}
Quick tutorial: every time you want to log something, add in the top of the file:
import logging
logger = logging.getLogger(__name__)
And use logger as you wish:
logger.warn("I'm the warning")
Testing
If you run python manage.py test, it will test all Django’s code, too. This will just slow up your development. I have a simple test runner which excludes unneeded modules:
# into settings/defaults.py
TEST_EXCLUDE = ('django',)
TEST_RUNNER = '%s.settings.test_suite.AdvancedTestSuiteRunner' % PROJECT_MODULE
Test Runner:
wget https://github.com/va1en0k/django_settings_template/raw/master/settings/test_suite.py -O settings/test_suite.py
requirements.txt
Simple as pie:
# into requirements.txt
django
south
.gitignore
.gitignore is a part of settings files of a project, and I have no idea why not make it a template (I’m actually using a global ‘.gitignore’ file, but I should worry about my teammates convenience too).
# into .gitignore
*~
*.pyc
*#
*#*
.ropeproject
*.db
*.log
static/media/*
static/assets/*
settings/local.py
Finally
# into settings/base.py
from .defaults import *
# override things now. If you used a default settings.py for settings/base.py,
# you can just remove everything except SECRET_KEY
And we can try to work with a project:
python manage.py syncdb
python manage.py collectstatic
python manage.py runserver
python manage.py test
So, what do I do with all this stuff?
If you like it, you have several options. First of all, here is a repository of everything, you can use it. I’m thinking about writing a shell script to set it all up. Or you can just add this page (and/or the repository) to your bookmarks and copypaste things.
If you have any additions and spot any mistakes, please let me know. Thank you very much. You can also fork a repository and send me a pull request.
-
I added everything related to settings to a repository. You can just open it and use one thing or another. I think it’s convenient to have a small source for better settings instead of doing everything by hand again and again.
↩ -
There is no problem in making it not a local module, and you can, if you wish. I have no idea why would I.
↩ -
Disclaimer: I actually copypasted almost everything in the post from different places on my servers, and I don’t promise they will work as is right away. Please send your fixes.
↩
Please send your comments, ideas, rants and job offers at v.golev@gmail.com.
Made with Nginx, Jekyll, Git, EC2 and Emacs.
Regarding all the advertising and readability money-related stuff: I'm sorry. I'm really interested in whether one can make money by showing ads in his blog. Seems like I can't. If someone is really annoyed by adwords, drop me a line, I'll reconsider.