My Django dev setup

Posted on Mar 30, 2020

I’ve finally nailed (what for now seems to be) the “perfect” Django dev setup. This can be useful for some of you as well. It’s heavily inspired by Jacob Kaplan-Moss’ one (Jacob is the creator of Django).

My main objectives for a perfect Django setup are:

  • Repeatability: all team members should be able to re-create the same project environment, regardless of the operating system they use.
  • “Correctness”: by using Poetry, we can guarantee no conflicts with dependencies and generate a Lock File, reproducible in production.
  • Fast: I can quickly fire up a terminal and run a project, or tests, or whatever.

I use zsh (default shell in macOS, starting with 10.15 Catalina) and ohmyzsh. I’ve incorporated a few ohmyzsh plugins to finally get everything working:

Image

Example of my dev setup

In the gif above ☝️, keep an eye on how I can run py.test and django-admin without any extra settings, all read from my local env. By the way, I haven’t sped up the video, that’s how fast it all works 😁.

As a reference, here’s a sample project with everything discussed in this post: https://github.com/santiagobasulto/django-demo-project

And finally, this is the detail of packages and plugins used:

pyenv

We have multiple Django projects in production. Some have been around for a long time, so they run on Python 3.6.9. The new ones are on Python 3.8.0. A single dev in our team must be able to work on all these projects. With pyenv, we can ensure that we’re all working in the same Python version. There’s a file .python-version that lists the version supported by the project. When a new dev starts working on the project, they can just do pyenv install and pyenv will take care of installing (and using) the correct Python version.

Poetry

Poetry was a life changer. We used to have dependencies listed in requirements.txt files (with the usual solution of base.txt, dev.txt and prod.txt). We’ve ran into some dependency conflicts and they were very hard to solve and keep correct.

If you’re not sure why Poetry is important, you should know that dependency resolution is a big deal in software. Poetry uses a deterministic algorithm to generate a “Lock File” and avoid Dependency Hell. Most major programming languages’ package managers have some sort of deterministic package resolution and lock files, including Java/Kotlin’s Gradle and Javascript’s npm. Read more about reproducible builds here. If you’re really interested in this topic, check out Nix.

When a new dev joins the project, they just need to do a poetry install and they’re good to go, ensuring their environment is the same as other dev’s and production.

Issues with Poetry

Before poetry, I was using virtualenv and virtualenvwrapper. I could set the PYTHONPATH and DJANGO_SETTINGS_MODULE environment variables in the postactivate file ($PATH_TO_VIRTUALENV/bin/postactivate) . So when the virtualenv was activated, I could just execute any django-admin command without the need of passing extra parameters.

With Poetry that wasn’t so simple. First, to “activate” the virtualenv created by poetry, you need to run the command poetry shell. And there’s no way to specify a script to run after the environment is activated, which meant there was no way to setup the Django environment variables. So I ended up running long commands like django-admin runserver --settings demo_project.settings.dev --pythonpath demo_project. This, of course, conflicts with my 3rd objective: a fast and dynamic environment. The solution was to include a few ohmyzsh plugins: poetry and autoenv.

With the Poetry plugin, when I cd into a Poetry based project, the virtualenv is automatically activated (poetry shell). With autoenv, I can define a file .in that contains the env variables to activate.

The result is exactly what I wanted: now I can cd into my project (I also use autojump, which makes it a lot easier) and just run a Django command directly: django-admin runserver. Check it out:

Image

A few extra ohmyzsh that I use and love are

  • zsh-autosuggestions: the suggestions you can see when I start typing
  • per-directory-history: self explanatory, each directory has its own shell history.

This post was automatically migrated from Medium. It still lives at: https://medium.com/@santiagobasulto/my-django-dev-setup-cd1d96dd5e19