I've been using Django for nearly two decades now and it's still my favorite development environment. It bills itself as "the web framework for perfectionists with deadlines", and I find it to be such an apt description. It's way less magic than Rails. It's a lot more old school than React. And it's certainly not the solution for all problems. I do see myself switching more to a combination of React for web and native app development for mobile, but there will always be a place for Django in my toolkit for many years to come. I'm not sure what it is about Django that I love so much. I'm just so, so fast on it. I love that it's a batteries included framework that a lot of the stuff you need is just built in. It has an incredible ORM. It has a very rigid way of organizing code. Which makes it amazing when revisiting code that you've written years ago or that other people have written. There's a pretty good chance that you can pick up and maintain someone else's project with very little onboarding time. That's something that I can't say of any other framework I've used. That said, for a batteries included framework, it's remarkably poor at handling JavaScript and SCSS compilation. It also doesn't have much of an opinion on how modern web applications should be built using it. These are some of my notes on how I configure Django, along with a few other notes that I find useful to reference. # JS Compilation I've tried to avoid using JavaScript compilers as much as possible, but it's very hard to avoid them these days. The experience of working with purely native JavaScript is just too poor for it to be effective most of the time. Most of the time I try to keep things as simple as possible - one main JavaScript file that's compiled with a very simple webpack command. ## Setting up NPM Initialize npm and install the packages. I'm also installing HTMX and Alpine since they are pretty much a must on all my Django projects: ``` npm init npm install --save-dev webpack webpack-cli npm install alpinejs htmx.org ``` Add the compilation scripts: ``` "scripts": { "dev": "webpack --watch", "build": "webpack", "heroku-postbuild": "npm run build" }, ``` The complete `package.json` should look something like this: ``` { "name": "xxx", "version": "1.0.0", "description": "xxx", "main": "index.js", "scripts": { "dev": "webpack --watch", "build": "webpack", "heroku-postbuild": "npm run build" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "webpack": "^5.91.0", "webpack-cli": "^5.1.4" }, "dependencies": { "alpinejs": "^3.13.7", "htmx.org": "^1.9.11" } } ``` ## Configure Webpack I like to go with the simplest webpack configuration to start, extending as needed over time. I'll set up one input file, typically in my main `/app/static/` folder, and one output file in `/app/static/dist ``` const path = require('path'); module.exports = { entry: './app/static/main.js', output: { filename: 'main.js', path: path.resolve(__dirname, 'app/static/dist'), } }; ``` I don't commit `dist` to the repository, so I make sure to have a `.gitignore`. rule for it. A simple `main.js` file with HTMX and Alpine might look like this: ``` import Alpine from 'alpinejs' import htmx from 'htmx.org'; window.Alpine = Alpine window.htmx = htmx; Alpine.start() ``` I'll add the reference to my `base.html` template: ``` <script defer src="{% static 'dist/main.js' %}"></script> ``` When I'm working with JS, I'll have webpack running in the background and watching for changes: ``` npm run dev ``` # SCSS I used to compile my SCSS using Webpack by injecting it in JavaScript and configuring Webpack loaders for it. But I'm not a big fan of that approach. I like having a bit of a separation between the JavaScript and the SDSS. This isn't something that makes a huge amount of sense - it's not like a massive performance gain or anything and certainly not a hill I'd die on, but it's a personal preference thing. I think part of it comes from reducing the complexity and configuration of Webpack and in some cases even reducing the need to use it all together. I use `django-sass-processor` to handle my SASS compilation. Ironically, this does mean that my installation is a lot more complicated. First step is to install the packages: ``` poetry add libsass django-compressor django-sass-processor ``` Then configure `settings.py`: ``` INSTALLED_APPS = [ 'sass_processor', ] # SASS # Adds `sass_processor` to the list of default staticfiles finders; will allow for automatic detection of # .css files that are compiled on-demand by sass_processor during development STATICFILES_FINDERS = [ 'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 'sass_processor.finders.CssFinder', ] # Add the node_modules directory to the list of directories that the SASS processor will look in for files # Allows for references to modules in node_modules from .scss files (e.g. @import 'bootstrap/scss/bootstrap';) SASS_PROCESSOR_INCLUDE_DIRS = [ os.path.join(BASE_DIR, 'node_modules'), ] ``` Then I add the SASS import tag into my main stylesheet: ``` {% load sass_tags %} <!-- styles --> <link href="{% sass_src 'main.scss' %}" rel="stylesheet"> ``` This will cause SASS to be dynamically compiled on development, which is pretty sweet. You get that nice SASS compilation without having to run any additional servers or compilers. Deploying to Heroku is a little more complicated because the SASS compiler, thankfully, doesn't compile on demand, on production. So you need to manually trigger the compilation during the deploy stage. This is pretty easy though, as Heroku supports post-compilation commands. Just create a `bin/post_compile` file and populate it with the following: ``` #!/bin/bash echo "Running post-compile tasks..." # Compile SCSS files ./manage.py compilescss # Collect static files python manage.py collectstatic --noinput # Your other post-compile tasks (if any) can go here echo "Post-compile tasks completed." ``` It's not necessarily easier than using Webpack, but I do like having that separation between Webpack being purely for JavaScript and my SASS compiler for stylesheets. # Developing with production database Whenever possible, I try to avoid using a one-to-one copy of the production database on local. That said, there are some times when it's really helpful to have access to that for debugging. I use a tool called Paragraphs to help me usually get data off of our Heroku servers into a local environment. Vergress is a package that makes it easy to pull data from a Heroku database into your local environment. It's super easy to use. Install it using the following command: `pip install paragres` Then set up your database credentials. I typically have a dedicated `paragres_settings.py` file that looks like this: ``` DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': '<db_name>', 'USER': '<username>', 'PASSWORD': '<password>', 'HOST': '<host>', 'PORT': '<port>', } } ``` ` Lastly, run the `paragres` command: `paragres -s <heroku_app_name> -c -t path/to/paragres_settings.py` This will initiate a database capture on Paroku, download it, and refresh your local database - as defined in your `paragres_settings.py` file - with the data.