Djereo's features
djereo
is a Django project template with opinionated tooling.
When invoked by following the instructions in the Quickstart the user is prompted to answer questions and make choices via a CLI. These answers are used to create a ready-to-use opinionated Django project.
This page documents the features of a Django project as generated by djereo
.
βοΈ Setup questionnaire
copier.yaml
defines the questionnaire that copier
will use to prompt the user when setting up a new
project. The questions defined in this file include sensible defaults (where appropriate)
and validation rules to prevent incoherent answers.
Options? - I thought this was an opinionated template!
djereo
is opinionated insofar as it embodies beliefs such as 'postgres is the best
database for a new project' or 'htmx for frontends is great'. It lays out paved paths
and sensible defaults if you choose to configure a new project with these tools. The
aim isn't to be restrictive or force e.g. htmx
or django-waffle
on anyone.
Versions
Users are asked three version-related questions when initialising a project:
Django version
Django is a popular web
framework written in Python. djereo
gives you the choice between creating projects using
Django versions:
- 5.1 (latest)
- 4.2 (LTS, long-term support version)
See https://endoflife.date/django for more information.
Python version
djereo
(and its base, pycliche)
support Python 3.12 and above. The user's choice is recorded in a .python-version
file and in the requires-python
field of pyproject.toml.
See https://endoflife.date/python for more information.
Postgres version
djereo
is configured to work with a postgres database and contains utilities and checks
to help with this. See Postgres database below for more information.
Note that it is trivial to switch from postgres to SQLite (a single-line change in .env.in
)
if you prefer that database engine.
Keeping versions consistent
The django-version-checks
package is used to ensure all environments (local, CI, dev,
prod) use the same versions of Python & postgres.
π¦ Semantic Versioning
The generated project follows Semantic Versioning, with users prompted to provide an
initial version at project setup. The project's version is tracked in the version
field
of pyproject.toml.
See Release Please below for information on how this version can be used alongside Conventional Commits to automatically generate changelogs and new semver tags.
See https://semver.org/ for more information.
Checking the app's version
djereo
's base template & its override of Django Admin's base template both
include a footer showing the deployed tag:
πͺ Git pre-commit hooks
Every new project comes with a set of sensible pre-commit git hooks. These are defined in .pre-commit-config.yaml and include:
- Common core hooks such as
check-merge-conflict
orend-of-file-fixer
. mypy
to perform static type checks on Python code.asottile/pyupgrade
to convert syntax when the version of Python is upgraded.astral-sh/ruff-pre-commit
to lint & format Python code at lightspeed with Ruff. Check thetool.ruff.lint
table inpyproject.toml
for a full list of rules and options.DavidAnson/markdownlint-cli2
to lint markdown files with rules configured in .markdownlint-cli2.yaml.biomejs/pre-commit
to lint & format frontend code (JS, TS, CSS) following config in biome.jsonc.adamchainz/djade-pre-commit
to format Django templates.adamchainz/django-upgrade
to automatically migrate code to a new version of Django.alessandrojcm/commitlint-pre-commit-hook
to enforce Conventional Commits.
Optional GitHub features
As part of the setup questionnaire users are asked whether the project they are creating will be hosted on GitHub. If this is the case, GitHub Actions config files are added to their project. The resulting custom actions and workflows are described below.
Custom GitHub actions
Four re-usable custom actions are available:
pre-commit
: runs all pre-commit hooks exceptno-commit-to-branch
as this would make merge pipelines fail. In workflows (see below) all jobs depend on this action succeeding.django-checks
: runs Django's system checks (manage.py check
). Optionally with the--deploy
flag set. This check is strict by default, failing on warnings. Add checks toSILENCED_SYSTEM_CHECKS
insettings.py
to ignore specific warnings.containerise
: builds a container image and pushes it to the specified registry. Accepts a list of platforms to build for, a list of tags and the path to a Dockerfile.
N.B. If wishing to use the GitHub Container Registry (ghcr.io, the default) make sure you follow the click-ops steps detailed in.github/actions/containerise/action.yaml
.service-health
: calls the liveness & health endpoints (see Healthchecks below), allowing you to check the status of a service after a deployment.
See .github/actions/ for the definitions of these actions.
GitHub Actions workflows
Four workflows are defined:
PR
(pull request)
Runs whenever a pull request is opened against a branch or an existing PR receives new commits.
CI
(continuous integration)
As with PR
, but acts when a pull request is closed and changes merged into the main
branch.
Release Please
Refreshes a pull request that updates the changelog & bumps the Semantic Version every
time the main
branch is merged to.
.release-please-config.json
configures the tool while .release-please-manifest.json
is the source of truth for the latest SemVer tag.
N.B. conventional commits (as enforced by the relevant git hook) are a prerequisite for Release Please to generate changelogs and calculate new SemVer tags.
In order for Release Please to automate the above process, a GitHub Actions 'repository secret'
called RELEASE_PLEASE_TOKEN
must exist in GitHub (under
contents: write
pull-requests: write
For more information, consult the release-please-action project.
On tag
Runs whenever a tag matching the pattern vM.m.p
is pushed to GitHub (where M.m.p
is a
SemVer tag, see Semantic Versioning above).
This is intended as the foundation providing 80% of your deployment pipeline. Out of the
box the on-tag
workflow runs the following jobs:
This can be extended to end with the 'service-health' job (see Custom GitHub actions
above). This is not shown in the above workflow since djereo
does not implement a 'deploy'
job that would be necessary to bridge 'containerise' and 'service-health'.
Dependabot
Configured to update Python dependencies & GitHub actions on a weekly schedule.
See .github/workflows/ and .github/dependabot.yaml for the definitions of these workflows.
π€ Justfile
Four recipes are ready to use in every new project. Each takes care of setting up a virtualenv and installing dependencies (including the project itself in editable mode).
manage
: wrapper around Django'smanage.py
. Takes one or more arguments, which defaults to 'help'.runserver
: execute Django'srunserver
management command using Python in Development Mode. This mode shows additional warnings (Deprecation, Import, Resource) and enables extra debug hooks. Development Mode can be disabled by passing an empty string i.e.just runserver ""
shell
: run Django's management command to drop into a shell. By default this is IPython in all new projects.test
: install test dependencies, run all unit tests via Django's test runner and generate and display a coverage report (both on-screen and in HTML format).
π» Developer Experience enhancements
Use IPython as your shell
IPython
, an improved Python shell, is installed as a development dependency. Django picks
it up by default and uses it instead of the default Python shell. Access it directly with
the just shell
recipe.
By default IPython's debugger (ipdb
) will launch when a breakpoint()
is reached while
running the application.
ipdb
is also used to debug tests and will launch when a test fails. The file .pdbrc
contains aliases that can be used in ipdb
debugger sessions.
Developer tools
The project comes with some ready-to-use developer tools installed as dev dependencies and configured for use locally:
- django-browser-reload: watch project files for changes and reload the browser.
- django-debug-toolbar: display information about the request/response cycle on each page.
- rich: nicer formatting and colours
for
runserver
logs.
Custom system checks
djereo
adds two custom system checks that run when the check
management command is
invoked.
check_dev_mode
: alerts to the application running in debug mode yet Python's Development Mode not being enabled, indicating that there are some warnings and debug hooks you may be missing out on.check_model_names
: enforces consistent model names across the application.
The django-checks
GitHub action will run the default Django checks and the above custom
checks as part of the continuous integration pipeline.
While strictly speaking not a system test, the django-linear-migrations
package is enabled
on all projects generated by djereo
. This package updates a max_migration.txt
file
whenever makemigrations
runs, triggering a merge conflict if there is an attempt to apply
a non-linear migration history.
Use project metadata in the Django app
{{project_name}}/__init__.py
exposes project metadata such as version and author information.
To use it in a view:
from django.http import HttpResponse
from {{project_name}} import __version__
def view(request):
return HttpResponse(f"on version v{__version__}")
The custom context_processors.metadata
processor also exposes the version for use in
templates.
βοΈ settings.py
settings.py
takes values for certain configuration options from environment variables,
managed via the environs
package. When no values are specified, it falls back to sensible
defaults.
settings.py
is divided into five sections:
- Setup
Settings such as determining whether the application is running under test conditions or defining theBASE_DIR
. - Django Core Settings
INSTALLED_APPS
is defined by combining three lists containing apps according to their provenance.LOGGING
is configured to userich
when DEBUG is True andstructlog
otherwise. - Django Contrib Settings
[left empty for the user to populate] - Third Party Settings
Settings fordjango-allauth
and dev tools such as the debug toolbar anddjango-version-checks
. - Project Settings
[left empty for the user to populate]
The tool.ruff.lint.flake8-tidy-imports.banned-api
table in pyproject.toml
includes
configuration to have the ruff
pre-commit hook enforce a ban on importing the settings
module directly. It suggests that you use from django.conf import settings
instead as
this is safer and avoids complications if override_settings
is used in tests.
πͺ΅ Logging
Logging differs between hosted environments (eg. production) and development.
In development (runserver
) rich is
used to output coloured logs:
When DEBUG=False
, as in production, structlog
is used instead.
Logging configuration is dynamically generated based on the DEBUG
environment variable.
The LoggingConfigFactory
class is used to generate settings for filters, formaters,
handlers and loggers.
Models
The core app defines an UuidModel
abstract model. Models that subclass it will have
a UUID as a primary key instead of the default BigAutoField
. This is used by the models
in the existing 'users' app and can be used by any new models you create.
Utility abstract models CreatedAtModel
, UpdatedAtModel
and DeletedAtModel
(for
soft-deletion) are available to store timestamps.
π Postgres database
djereo
projects are configured to use a postgres
database out-of-the-box.
This can easily be switched to use a SQLite database instead by changing a single line in
the dotenv template, .env.in
.
Set up & tear down scripts
The _db/
directory contains the following database scripts:
set_up.sql
creates a postgres user and database with the same name as the project.tear_down.sql
undoesset_up.sql
and removes the database and user.
Django's default test database behaviour (creating a new database with a name formed by
prepending the name of the default database with test_
) is left as-is.
Database connection string
The database connection is configured in settings.py
using the environs
package's
'Django database URL' extension. That is, as a single database connection string that is
located in .env
as the environment variable DATABASE_URL
.
Connection pooling
Djereo takes advantage of Django's built-in support for postgres connection pooling (Django docs). While this is a premature optimisation for most projects, the overhead of opening a new postgres connection is considerable, so connection pooling is enabled to alleviate this.
seed_database
management command
A seed_database
management command is included for quickstart purposes and as a starting
point for you to populate with models needed for local development as your application grows.
In a new project seed_database
creates three users: admin, staff and a non-privileged
regular user.
Invoke it with just manage seed_database
.
π User authentication
Projects generated with djereo
follow the 'custom User + UserProfile' pattern common in
modern Django apps. Authentication functionality is implemented via the django-allauth
package for simplicity and to ease future extensibility. A dedicated Django app, users
,
centralises models, template overrides and configuration for auth-related features.
users
app model factories
Model factories for AuthUser
and UserProfile
are provided as a convenience. This includes
subfactories to keep tests lean. This means that in a test one may generate a fleshed-out
user with:
from users.factories import UserProfileFactory
from users.models import AuthUser
profile = UserProfileFactory()
assert isinstance(up.user, AuthUser)
Frontend
Default pages & styling
Generated projects include a vendorised copy of mvp.css.
This is used to style the default welcome page and the templates used by django-allauth
for the pages under /accounts/
.
Styling django-allauth
templates
See the section on Styling Existing Templates
in the django-allauth
docs for instructions on doing so. Out of the box djereo
keeps
changes minimal, only overriding django-allauth
's base template (users/templates/allauth/layouts/base.html
).
Optional htmx
frontend
Projects generated using djereo
include a vendorised copy of htmx
that is loaded in
the _base.html
template via django-htmx
's {% htmx_script %}
tag. You can opt out
from this when completing the setup questionnaire.
The django-htmx
package is used to smoothly integrate htmx
into projects, abstracting
away header checks and allowing views to change their behaviour and responses based on the
request.htmx
attribute.
See the django-htmx documentation for more.
π€ Third-party packages
whitenoise
whitenoise is included as a dependency. It is configured to serve static files using Brotli compression and add unique hashes to filenames so that each version can be cached for a long time.
π Security
settings.py
is configured to detect whether the application is running in a hosted
environment and enable security enhancements. A hosted environment is detected by checking
that DEBUG
is False and that localhost is not amongst the CSRF_TRUSTED_ORIGINS
.
- Strict-Transport-Security: this header is enabled via the
SECURE_HSTS_*
settings. Configured for a duration of 3600 seconds to begin with, it is recommended this is increased to 2592000 seconds (30 days). - Content-Security-Policy: django-csp is used to set this header to a locked-down default.
SECURE_SSL_REDIRECT
is not set explicitly under the assumption that the application will sit behind a reverse proxy that will take care of redirecting HTTP to HTTPS.SECURE_PROXY_SSL_HEADER
is also configured to ease this.- django-permissions-policy
is used to apply sensible restrictions to block annoying & intrusive web APIs. See the
PERMISSIONS_POLICY
setting for details. - Cross-Origin-Opener-Policy: by default Django sets this to the secure
same-origin
value. - Cross-Origin-Embedder-Policy & Cross-Origin-Resource-Policy: these are set to their most
secure values by means of a custom middleware,
SecurityHeadersMiddleware
. There is an open ticket to add these to Django (#31923).
π§ͺ Tests
djereo
projects come with all the tooling needed to get you writing tests quickly, as
well as a couple of tests ready out-of-the-box.
To run these tests call nox
, passing the same optional arguments you would when using
manage.py test
e.g. nox -- package.module
.
Nox is used to automate testing across different Python versions. Test sessions are
configured via noxfile.py
. coverage
reporting will only run for test runs for the
oldest and latest Python versions.
The PendingMigrationsTests
class will fail if any model changes are not yet captured in
a migration, with the aim of negating the possibility of a deployment attempt that fails
due to missing migrations.
The test_checks
module tests the custom Django system checks added by djereo
.
Profiling tests
Invoke just profile_tests
to output a speedscope-compatible
profile file to understand bottlenecks in your tests.
N.B. this requires py-spy
to be available globally on your system.
Tests in GitHub Actions
The test
job in GitHub Actions uses the matrix strategy. This runs each Nox session
(i.e. Python version test run) as a separate pipeline job.
π Deployment
While djereo
eschews Docker throughout development, the advantages of containerisation
for tagged, reproducible builds in hosted environments are evident.
The _deploy/
directory of projects generated with djereo
includes a Dockerfile that
uses the local development toolchain (the uv
project manager) to containerise web
applications.
The Dockerfile runs pre-production steps, such as enabling bytecode compilation to trade a longer
installation time for faster start-up times, or running Django's collectstatic
command.
The container's entrypoint runs gunicorn
as the WSGI web server to make the Django application available.
The resulting container has been tested in production behind nginx
as the reverse proxy, with gunicorn
as the WSGI web server. nginx
configuration and
setting up SSL certificates is beyond the scope of djereo
, but hopefully the Docker image
provides a head-start on deploying applications generated by djereo
.
gunicorn
is included as a dependency of all djereo
projects. If you are interested in
using ASGI instead of WSGI, check out uvicorn.
Healthchecks
Generated projects pull in django-alive to expose two endpoints with which to monitor the health of an application instance:
/-/alive/
/-/health/
Continuous Deployment
djereo
aims to provide a foundation on which to build your continuous deployment pipeline.
Given the diversity of hosting & deployment solutions it doesn't attempt to provide a
solution for the step that deploys your application. However, you can extend the on-tag
GitHub Actions workflow, which contains the following sequence of jobs:
pre-commit
: run all pre-commit hooks.test
: run Django unit tests.django_checks
: run thecheck
management command with the--deploy
flag.containerise
: build an image containing your application and push to GitHub Container Registry.
(see Custom GitHub actions above for more).
You can easily add a custom GitHub Action to deploy your code after this last step.
Finally, round off this pipeline with the pre-configured service-health
action to confirm
that your app deployment worked.