We build a lot with Django. It’s one of our favorite frameworks, along with Flask, Rails and Phoenix.
A common headache of any new project is prepping for local dev, production configs, and sane test integrations.
To help solve this, we create “starter projects”. They’re hosted on GitHub. To use them, simply fork the project and get to work.
Today, I’d like to share our Django Starter project.
First out the gate is local development. How do we run the project? Virtualenv? Vagrant? Docker? Which is our default database and what are the credentials?
We prefer to use Virtualenv. We like its support for both Python 2 and 3 and is much simpler than Vagrant or Docker. Python 3’s venv is cool but isn’t backwards compatible.
For our database, we use Postgres. That simply comes down to team familiarity and that we like to use Heroku for almost all deployments. Each database is named after the project and everyone has a postgres superuser that can create and migrate tables. With this approach, the local file can be identical across all workstations and makes sharing dev credentials much easier.
There are a few methods for organizing Django. The most common two are having Django apps at the root level or within an apps/ subdirectory. We prefer the latter.
There are two reasons for this:
It makes the top level structure much easier to read and traverse
It helps decouple app specific logic from other “general” modules
Each app also contains all of its own logic, including tests. Some teams prefer to have a dedicated tests directory. Our approach is to keep each area of functionality nicely encapsulated. If you’re working on that area of the project, theres only one place to look. No surprises.
Here’s a quick example:
apps/ someapp/ views.py tests.py ... libs/ filters.py ... project/ settings/ common.py local.py production.py
The default settings.py file is broken into a settings/ subdirectory with common (global) configs and environment specific configs. This aids in local development and production specifics while sharing common configs across both. Without this, the standard settings file quickly becomes unwieldily and hard to read.
Anything that isn’t specific to an app should go into the root libs directory. This is typically where we’d have custom modules for handling template helpers, project (not app) specific logic, etc.
For the most part, each bit of functionality is put in one file. One exception to this is template filters, where all of them exist in one.
Logging is notoriously difficult to get right in Python and Django. Due to this, we wanted to have a standard that would work across local as well as production.
Our logging configuration is specified in the “common” config file and addresses each area of the project including Django itself, apps and celery tasks. Having each broken into their own setup allows us to easily dial up the logs in different areas while completely hiding them in others.
This has a few beneficial side effects:
Common logging formats across all our projects, making it easier to scan logs when debugging
Developers are less inclined to use print statements because they can’t get the log to work
Log output can be updated in a single location, whether we want to stream to stdout or a 3rd party service like Papertrail
Not all our projects require background jobs, but we decided to include some examples since not everyone is familiar with the setup.
Our preferred tools are Celery and RabbitMQ. When these aren’t used in the project, we simply delete the related celery and task files.
Worker queues are typically used for batch processing. This might be image resizing, bulk emails, Twilio text messages or multi-chain API calls.
Because we use Heroku, spinning up a fleet of workers becomes a trivial step instead of spending unecessary time configuring containers or EC2 instances. Just update the Procfile and you’re good to go.
We like to keep tests simple and focused. If you need to start testing your tests, you’ve gone too far.
We use the default Python unittest along with Django’s TestCase suite. FactoryBoy and Faker are used for simplifying test data.
Lastly, we use TravisCI for running tests against our pull requests, which helps keep our development cycle tight.
Our preferred method of production is to use Heroku. To that end, we include a default Procfile which includes the common patterns for web and worker dynos.
After a bit of environment config housekeeping, deployment is as easy as
git push heroku master
When it comes time to hand-off the project to our clients, transferring ownership is instant and doesn’t require any changes. Whats not to like?
Thats what we’ve got so far and its been working great. We plan to add a few additional things. Mostly related to front-end.
At the moment we reach for React first. We’re thinking of either adding some NPM build scripts and examples to the project itself or a dedicated React starter project that can be dropped into any of the framework starters we make.
We also plan to creating some starter projects for Phoenix and Flask.