├── .dockerignore
├── .editorconfig
├── .env-dist
├── .github
└── workflows
│ └── actions.yml
├── .gitignore
├── .pre-commit-config.yaml
├── Dockerfile
├── LICENSE
├── README.md
├── compose-entrypoint.sh
├── compose.yml
├── config
├── __init__.py-tpl
├── settings.py-tpl
├── tests
│ └── test_file.py
├── urls.py-tpl
└── wsgi.py-tpl
├── conftest.py-tpl
├── frontend
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── apple-touch-icon.png
├── favicon-16x16.png
├── favicon-32x32.png
├── favicon.ico
└── site.webmanifest
├── justfile
├── manage.py-tpl
├── pyproject.toml
├── requirements.in
└── requirements.txt
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/__pycache__
2 | *.pyc
3 | .*
4 | compose.override.yml
5 | Dockerfile
6 | justfile
7 | LICENSE
8 | README.md
9 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | end_of_line = lf
8 | indent_size = 4
9 | indent_style = space
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
13 | [*.py]
14 | max_line_length = 100
15 |
16 | [*.{css,html,js,json,sass,scss,vue,yaml,yml}]
17 | indent_style = space
18 | indent_size = 2
19 |
20 | [*.md]
21 | indent_size = 4
22 | trim_trailing_whitespace = false
23 |
--------------------------------------------------------------------------------
/.env-dist:
--------------------------------------------------------------------------------
1 | ALLOWED_HOSTS=*
2 | DATABASE_URL=postgres://postgres@db/postgres
3 | DJANGO_DEBUG=true
4 |
--------------------------------------------------------------------------------
/.github/workflows/actions.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - main
8 |
9 | env:
10 | DOCKER_BUILDKIT: "1"
11 |
12 | jobs:
13 | test:
14 | runs-on: ubuntu-latest
15 | name: Tests with Python
16 |
17 | steps:
18 | - uses: actions/checkout@v4
19 | with:
20 | fetch-depth: 1
21 | path: ./src/github.com/${{ github.repository }}-git
22 |
23 | - name: Set up Python 3.12
24 | uses: actions/setup-python@v5
25 | with:
26 | python-version: "3.12"
27 | cache: "pip"
28 |
29 | - name: Install dependencies
30 | run: |
31 | python -m pip install --upgrade pip uv
32 |
33 | - name: Create a project based on our settings
34 | run: |
35 | uv run --with=django \
36 | django-admin startproject \
37 | --extension=ini,py,toml,yaml,yml \
38 | --template=./src/github.com/${{ github.repository }}-git/ \
39 | test_project
40 |
41 | - name: Freeze our requirements
42 | run: |
43 | cd test_project
44 | uv pip compile requirements.in \
45 | --output-file requirements.txt
46 |
47 | - name: Docker - Build image from starter project
48 | run: |
49 | cd test_project
50 | docker compose pull
51 | docker compose build
52 |
53 | - name: Docker - Test generated starter project
54 | run: |
55 | cd test_project
56 | docker compose run --rm utility pytest
57 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 | cover/
54 |
55 | # Translations
56 | *.mo
57 | *.pot
58 |
59 | # Django stuff:
60 | *.log
61 | local_settings.py
62 | db.sqlite3
63 | db.sqlite3-journal
64 |
65 | # Flask stuff:
66 | instance/
67 | .webassets-cache
68 |
69 | # Scrapy stuff:
70 | .scrapy
71 |
72 | # Sphinx documentation
73 | docs/_build/
74 |
75 | # PyBuilder
76 | .pybuilder/
77 | target/
78 |
79 | # Jupyter Notebook
80 | .ipynb_checkpoints
81 |
82 | # IPython
83 | profile_default/
84 | ipython_config.py
85 |
86 | # pyenv
87 | # For a library or package, you might want to ignore these files since the code is
88 | # intended to run in multiple environments; otherwise, check them in:
89 | # .python-version
90 |
91 | # pipenv
92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
95 | # install all needed dependencies.
96 | #Pipfile.lock
97 |
98 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
99 | __pypackages__/
100 |
101 | # Celery stuff
102 | celerybeat-schedule
103 | celerybeat.pid
104 |
105 | # SageMath parsed files
106 | *.sage.py
107 |
108 | # Environments
109 | .env
110 | .venv
111 | env/
112 | venv/
113 | ENV/
114 | env.bak/
115 | venv.bak/
116 |
117 | # Spyder project settings
118 | .spyderproject
119 | .spyproject
120 |
121 | # Rope project settings
122 | .ropeproject
123 |
124 | # mkdocs documentation
125 | /site
126 |
127 | # mypy
128 | .mypy_cache/
129 | .dmypy.json
130 | dmypy.json
131 |
132 | # Pyre type checker
133 | .pyre/
134 |
135 | # pytype static type analyzer
136 | .pytype/
137 |
138 | # Cython debug symbols
139 | cython_debug/
140 |
141 | # Custom compose options
142 | compose.override.yml
143 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | default_language_version:
2 | python: python3.12
3 | repos:
4 | - repo: https://github.com/pre-commit/pre-commit-hooks
5 | rev: v5.0.0
6 | hooks:
7 | - id: check-added-large-files
8 | - id: check-case-conflict
9 | - id: check-json
10 | - id: check-merge-conflict
11 | - id: check-symlinks
12 | - id: check-toml
13 | - id: check-yaml
14 | - id: end-of-file-fixer
15 | - id: trailing-whitespace
16 | - repo: https://github.com/astral-sh/ruff-pre-commit
17 | rev: v0.11.9
18 | hooks:
19 | - id: ruff
20 | args:
21 | - --fix
22 | - id: ruff-format
23 | - repo: https://github.com/asottile/pyupgrade
24 | rev: v3.19.1
25 | hooks:
26 | - id: pyupgrade
27 | args:
28 | - --py312-plus
29 | - repo: https://github.com/adamchainz/django-upgrade
30 | rev: 1.24.0
31 | hooks:
32 | - id: django-upgrade
33 | args:
34 | - --target-version
35 | - '5.0'
36 | - repo: https://github.com/rtts/djhtml
37 | rev: 3.0.7
38 | hooks:
39 | - id: djhtml
40 | entry: djhtml --tabwidth 4
41 | alias: autoformat
42 | - repo: https://github.com/adamchainz/djade-pre-commit
43 | rev: "1.4.0"
44 | hooks:
45 | - id: djade
46 | args: [--target-version, "5.2"]
47 | - repo: https://github.com/asottile/blacken-docs
48 | rev: 1.19.1
49 | hooks:
50 | - id: blacken-docs
51 | alias: autoformat
52 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # ------------------------------------------------------------
2 | # Base/builder layer
3 | # ------------------------------------------------------------
4 |
5 | FROM python:3.13-slim-bookworm AS builder
6 |
7 | ENV PIP_DISABLE_PIP_VERSION_CHECK 1
8 | ENV PYTHONDONTWRITEBYTECODE 1
9 | ENV PYTHONPATH /srv
10 | ENV PYTHONUNBUFFERED 1
11 |
12 | COPY requirements.txt /tmp/requirements.txt
13 |
14 | # add ",sharing=locked" if release should block until builder is complete
15 | RUN --mount=type=cache,target=/root/.cache,sharing=locked,id=pip \
16 | python -m pip install --upgrade pip uv just-bin
17 |
18 | RUN --mount=type=cache,target=/root/.cache,sharing=locked,id=pip \
19 | python -m uv pip install --system --requirement /tmp/requirements.txt
20 |
21 | # ------------------------------------------------------------
22 | # Dev/testing layer
23 | # ------------------------------------------------------------
24 |
25 | FROM builder AS release
26 |
27 | COPY . /src/
28 |
29 | WORKDIR /src/
30 |
31 | CMD ["python", "-m", "manage", "runserver", "--skip-checks", "0.0.0.0:8000"]
32 |
33 | # ------------------------------------------------------------
34 | # TODO: Add Production notes
35 | # ------------------------------------------------------------
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2018, Jeff Triplett
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Welcome to django-startproject 👋
2 |
3 |
4 |
5 |
6 |
7 |
8 | > Django startproject template with batteries
9 |
10 | ## :triangular_flag_on_post: Core Features
11 |
12 | - Django 5.2
13 | - Python 3.13
14 | - Docker Compose (I prefer Orbstack)
15 | - Justfile recipes
16 | - Postgres auto updates
17 | - uv support
18 | - pre-commit support
19 |
20 | ## :triangular_flag_on_post: Django Features
21 |
22 | - django-click
23 | - environs[django]
24 | - psycopg[binary]
25 | - whitenoise
26 |
27 | ## :shirt: Linting/auto-formatting
28 |
29 | - djade
30 | - django-upgrade
31 | - djhtml
32 | - pre-commit
33 | - pyupgrade
34 | - ruff
35 |
36 | ### :green_heart: CI
37 |
38 | - django-test-plus
39 | - model-bakery
40 | - pytest
41 | - pytest-cov
42 | - pytest-django
43 |
44 | ### 🏠 [Homepage](https://github.com/jefftriplett/django-startproject)
45 |
46 | ## :wrench: Install
47 |
48 | ```shell
49 | $ uv run --with=django django-admin startproject \
50 | --extension=ini,py,toml,yaml,yml \
51 | --template=https://github.com/jefftriplett/django-startproject/archive/main.zip \
52 | example_project
53 |
54 | $ cd example_project
55 |
56 | $ just bootstrap
57 | ```
58 |
59 | ## :rocket: Usage
60 |
61 | ```shell
62 | # Bootstrap our project
63 | $ just bootstrap
64 |
65 | # Build our Docker Image
66 | $ just build
67 |
68 | # Run Migrations
69 | $ just manage migrate
70 |
71 | # Create a Superuser in Django
72 | $ just manage createsuperuser
73 |
74 | # Run Django on http://localhost:8000/
75 | $ just up
76 |
77 | # Run Django in background mode
78 | $ just start
79 |
80 | # Stop all running containers
81 | $ just down
82 |
83 | # Open a bash shell/console
84 | $ just console
85 |
86 | # Run Tests
87 | $ just test
88 |
89 | # Lint the project / run pre-commit by hand
90 | $ just lint
91 |
92 | # Re-build PIP requirements
93 | $ just lock
94 | ```
95 |
96 | ## `just` Commands
97 |
98 | ```shell
99 | $ just --list
100 | ```
101 |
110 | ```
111 | Available recipes:
112 | bootstrap *ARGS # Initialize project with dependencies and environment
113 | build *ARGS # Build Docker containers with optional args
114 | console # Open interactive bash console in utility container
115 | down *ARGS # Stop and remove containers, networks
116 | lint *ARGS # Run pre-commit hooks on all files
117 | lock *ARGS # Compile requirements.in to requirements.txt
118 | logs *ARGS # Show logs from containers
119 | manage *ARGS # Run Django management commands
120 | pg_dump file='db.dump' # Dump database to file
121 | pg_restore file='db.dump' # Restore database dump from file
122 | restart *ARGS # Restart containers
123 | run *ARGS # Run command in utility container
124 | start *ARGS="--detach" # Start services in detached mode by default
125 | stop *ARGS # Stop services (alias for down)
126 | tail # Show and follow logs
127 | test *ARGS # Run pytest with arguments
128 | up *ARGS # Start containers
129 | upgrade # Upgrade dependencies and lock
130 | ```
131 |
132 |
133 | ## Author
134 |
135 | 👤 **Jeff Triplett**
136 |
137 | * Website: https://jefftriplett.com
138 | * Micro Blog: https://micro.webology.dev
139 | * Mastodon: [@webology@mastodon.social](https://mastodon.social/@webology)
140 | * Xwitter: [@webology](https://twitter.com/webology)
141 | * GitHub: [@jefftriplett](https://github.com/jefftriplett)
142 | * Hire me: [revsys](https://www.revsys.com)
143 |
144 | ## 🌟 Community Projects
145 |
146 | * [Django News Newsletter](https://django-news.com)
147 | * [Django News Jobs](https://jobs.django-news.com)
148 | * [Django Packages](https://djangopackages.org)
149 | * [DjangoCon US](https://djangocon.us)
150 | * [Awesome Django](https://awesomedjango.org)
151 |
152 | ## 🤝 Contributing
153 |
154 | Contributions, issues and feature requests are welcome!
Feel free to check [issues page](https://github.com/jefftriplett/django-startproject/issues).
155 |
156 | ## Show your support
157 |
158 | Give a ⭐️ if this project helped you!
159 |
--------------------------------------------------------------------------------
/compose-entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -eo pipefail
3 |
4 | python -m manage migrate --noinput --skip-checks
5 |
6 | python -m manage collectstatic --noinput --skip-checks
7 |
8 | exec "$@"
9 |
--------------------------------------------------------------------------------
/compose.yml:
--------------------------------------------------------------------------------
1 | x-common-settings: &common-settings
2 | build:
3 | context: .
4 | dockerfile: ./Dockerfile
5 | target: release
6 | depends_on:
7 | db:
8 | condition: service_healthy
9 | environment:
10 | - "ALLOWED_HOSTS=*"
11 | - "DATABASE_URL=postgres://postgres@db/postgres"
12 | - "DJANGO_DEBUG=true"
13 | restart: on-failure
14 | volumes:
15 | - .:/src:cache
16 |
17 | services:
18 |
19 | db:
20 | environment:
21 | - "POSTGRES_HOST_AUTH_METHOD=trust"
22 | healthcheck:
23 | test: ["CMD-SHELL", "pg_isready", "-d", "postgres"]
24 | interval: 10s
25 | timeout: 3s
26 | retries: 3
27 | image: "pgautoupgrade/pgautoupgrade:latest"
28 | init: true
29 | volumes:
30 | - .:/src:cache
31 | - postgres-data:/var/lib/postgresql/data/
32 |
33 | utility:
34 | <<: *common-settings
35 | tty: true
36 |
37 | web:
38 | <<: *common-settings
39 | command: ["python", "-m", "manage", "runserver", "--skip-checks", "0.0.0.0:8000"]
40 | entrypoint: ["/src/compose-entrypoint.sh"]
41 | init: true
42 | ports:
43 | - "8000:8000"
44 | tty: true
45 |
46 | volumes:
47 | postgres-data:
48 |
--------------------------------------------------------------------------------
/config/__init__.py-tpl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jefftriplett/django-startproject/f93b32ca95a1a0ce44f2b7f681c0a79d3d47158f/config/__init__.py-tpl
--------------------------------------------------------------------------------
/config/settings.py-tpl:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for {{ project_name }} project.
3 |
4 | Generated by 'django-admin startproject' using Django {{ django_version }}.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/{{ docs_version }}/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/
11 | """
12 |
13 | from environs import env
14 | from pathlib import Path
15 |
16 | # Build paths inside the project like this: BASE_DIR / 'subdir'.
17 | BASE_DIR = Path(__file__).resolve(strict=True).parent.parent
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/{{ docs_version }}/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = env.str(
24 | "SECRET_KEY",
25 | default="{{ secret_key }}",
26 | )
27 |
28 | # SECURITY WARNING: don't run with debug turned on in production!
29 | DEBUG = env.bool("DJANGO_DEBUG", default=False)
30 |
31 | ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=[])
32 |
33 | CSRF_TRUSTED_ORIGINS = env.list("CSRF_TRUSTED_ORIGINS", default=[])
34 |
35 | # Application definition
36 |
37 | INSTALLED_APPS = [
38 | "django.contrib.admin",
39 | "django.contrib.auth",
40 | "django.contrib.contenttypes",
41 | "django.contrib.messages",
42 | "django.contrib.sessions",
43 | "django.contrib.sites",
44 | "django.contrib.staticfiles",
45 | ]
46 |
47 | # Third-party apps
48 |
49 | INSTALLED_APPS += []
50 |
51 | # Our apps
52 |
53 | INSTALLED_APPS += []
54 |
55 | MIDDLEWARE = [
56 | "django.middleware.security.SecurityMiddleware",
57 | "whitenoise.middleware.WhiteNoiseMiddleware",
58 | "django.contrib.sessions.middleware.SessionMiddleware",
59 | "django.middleware.common.CommonMiddleware",
60 | "django.middleware.csrf.CsrfViewMiddleware",
61 | "django.contrib.auth.middleware.AuthenticationMiddleware",
62 | "django.contrib.messages.middleware.MessageMiddleware",
63 | "django.middleware.clickjacking.XFrameOptionsMiddleware",
64 | ]
65 |
66 | ROOT_URLCONF = "config.urls"
67 |
68 | TEMPLATES = [
69 | {
70 | "BACKEND": "django.template.backends.django.DjangoTemplates",
71 | "DIRS": [
72 | str(BASE_DIR.joinpath("templates")),
73 | ],
74 | "APP_DIRS": True,
75 | "OPTIONS": {
76 | "context_processors": [
77 | "django.template.context_processors.debug",
78 | "django.template.context_processors.request",
79 | "django.contrib.auth.context_processors.auth",
80 | "django.contrib.messages.context_processors.messages",
81 | ],
82 | "debug": DEBUG,
83 | },
84 | },
85 | ]
86 |
87 | WSGI_APPLICATION = "config.wsgi.application"
88 |
89 | # Database
90 | # https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/#databases
91 |
92 | DATABASES = {
93 | "default": env.dj_db_url("DATABASE_URL", default="postgres:///{{ project_name }}"),
94 | }
95 |
96 |
97 | # Password validation
98 | # https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/#auth-password-validators
99 |
100 | AUTH_PASSWORD_VALIDATORS = [
101 | {
102 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
103 | },
104 | {
105 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
106 | },
107 | {
108 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
109 | },
110 | {
111 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
112 | },
113 | ]
114 |
115 | # Internationalization
116 | # https://docs.djangoproject.com/en/{{ docs_version }}/topics/i18n/
117 |
118 | LANGUAGE_CODE = "en-us"
119 |
120 | TIME_ZONE = "UTC"
121 |
122 | USE_I18N = True
123 |
124 | USE_TZ = True
125 |
126 | # Static files (CSS, JavaScript, Images)
127 | # https://docs.djangoproject.com/en/{{ docs_version }}/howto/static-files/
128 |
129 | STATIC_ROOT = str(BASE_DIR.joinpath("static"))
130 | STATIC_URL = "/static/"
131 | STATICFILES_DIRS = (str(BASE_DIR.joinpath("frontend")),)
132 | # STORAGES = {
133 | # "staticfiles": {
134 | # "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"
135 | # }
136 | # }
137 |
138 | MEDIA_URL = "/media/"
139 | MEDIA_ROOT = str(BASE_DIR.joinpath("media"))
140 |
141 | if DEBUG:
142 | EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
143 | else:
144 | email = env.dj_email_url("EMAIL_URL", default="smtp://maildev")
145 | EMAIL_HOST = email["EMAIL_HOST"]
146 | EMAIL_HOST_PASSWORD = email["EMAIL_HOST_PASSWORD"]
147 | EMAIL_HOST_USER = email["EMAIL_HOST_USER"]
148 | EMAIL_PORT = email["EMAIL_PORT"]
149 | EMAIL_USE_TLS = email["EMAIL_USE_TLS"]
150 |
151 | # Parse cache URLS, e.g "redis://localhost:6379/0"
152 | CACHES = {"default": env.dj_cache_url("CACHE_URL", default="locmem://")}
153 |
154 | # Our settings
155 |
156 | ADMIN_URL = env.str("ADMIN_URL", default="admin/")
157 |
158 | SITE_ID = 1
159 |
--------------------------------------------------------------------------------
/config/tests/test_file.py:
--------------------------------------------------------------------------------
1 | def test_equal():
2 | assert 1 == 1
3 |
--------------------------------------------------------------------------------
/config/urls.py-tpl:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.conf.urls.static import static
3 | from django.contrib import admin
4 | from django.urls import path
5 |
6 |
7 | urlpatterns = [
8 | path(settings.ADMIN_URL, admin.site.urls),
9 | ]
10 |
11 | if settings.DEBUG:
12 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
13 |
--------------------------------------------------------------------------------
/config/wsgi.py-tpl:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for {{ project_name }} project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/{{ docs_version }}/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/conftest.py-tpl:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import logging
4 |
5 | import pytest
6 |
7 |
8 | logging.disable(logging.CRITICAL)
9 |
10 |
11 | @pytest.fixture(autouse=True)
12 | def use_test_settings(settings):
13 | settings.DEBUG = False
14 |
15 | settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
16 |
17 | settings.MIDDLEWARE = [
18 | middleware for middleware in settings.MIDDLEWARE if middleware != "whitenoise.middleware.WhiteNoiseMiddleware"
19 | ]
20 |
21 | # User a faster password hasher
22 | settings.PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
23 |
24 | settings.STORAGES = {"staticfiles": {"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"}}
25 |
26 | settings.WHITENOISE_AUTOREFRESH = True
27 |
--------------------------------------------------------------------------------
/frontend/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jefftriplett/django-startproject/f93b32ca95a1a0ce44f2b7f681c0a79d3d47158f/frontend/android-chrome-192x192.png
--------------------------------------------------------------------------------
/frontend/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jefftriplett/django-startproject/f93b32ca95a1a0ce44f2b7f681c0a79d3d47158f/frontend/android-chrome-512x512.png
--------------------------------------------------------------------------------
/frontend/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jefftriplett/django-startproject/f93b32ca95a1a0ce44f2b7f681c0a79d3d47158f/frontend/apple-touch-icon.png
--------------------------------------------------------------------------------
/frontend/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jefftriplett/django-startproject/f93b32ca95a1a0ce44f2b7f681c0a79d3d47158f/frontend/favicon-16x16.png
--------------------------------------------------------------------------------
/frontend/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jefftriplett/django-startproject/f93b32ca95a1a0ce44f2b7f681c0a79d3d47158f/frontend/favicon-32x32.png
--------------------------------------------------------------------------------
/frontend/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jefftriplett/django-startproject/f93b32ca95a1a0ce44f2b7f681c0a79d3d47158f/frontend/favicon.ico
--------------------------------------------------------------------------------
/frontend/site.webmanifest:
--------------------------------------------------------------------------------
1 | {"name":"","short_name":"","icons":[{"src":"/static/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/static/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
2 |
--------------------------------------------------------------------------------
/justfile:
--------------------------------------------------------------------------------
1 | set dotenv-load := false
2 |
3 | # Show list of available commands
4 | @_default:
5 | just --list
6 |
7 | # Initialize project with dependencies and environment
8 | bootstrap *ARGS:
9 | #!/usr/bin/env bash
10 | set -euo pipefail
11 |
12 | if [ ! -f ".env" ]; then
13 | cp .env-dist .env
14 | echo ".env created"
15 | fi
16 |
17 | if [ -n "${VIRTUAL_ENV-}" ]; then
18 | python -m pip install --upgrade pip uv
19 | else
20 | echo "Skipping pip steps as VIRTUAL_ENV is not set"
21 | fi
22 |
23 | if [ ! -f "requirements.txt" ]; then
24 | uv pip compile --output-file requirements.txt requirements.in
25 | echo "requirements.txt created"
26 | fi
27 |
28 | just upgrade
29 |
30 | if [ -f "compose.yml" ]; then
31 | just build {{ ARGS }} --pull
32 | fi
33 |
34 | # Build Docker containers with optional args
35 | @build *ARGS:
36 | docker compose build {{ ARGS }}
37 |
38 | # Generate README content with cogapp
39 | [private]
40 | @cog:
41 | uv tool run --from cogapp cog -r README.md
42 |
43 | # Open interactive bash console in utility container
44 | @console:
45 | docker compose run \
46 | --no-deps \
47 | --rm \
48 | utility /bin/bash
49 |
50 | # Stop and remove containers, networks
51 | @down *ARGS:
52 | docker compose down {{ ARGS }}
53 |
54 | # Format justfile with unstable formatter
55 | [private]
56 | @fmt:
57 | just --fmt --unstable
58 |
59 | # Run pre-commit hooks on all files
60 | @lint *ARGS:
61 | uv tool run --from pre-commit-uv pre-commit run {{ ARGS }} --all-files
62 |
63 | # Compile requirements.in to requirements.txt
64 | @lock *ARGS:
65 | docker compose run \
66 | --no-deps \
67 | --rm \
68 | utility \
69 | bash -c "uv pip compile {{ ARGS }} \
70 | --output-file requirements.txt \
71 | requirements.in"
72 |
73 | # Show logs from containers
74 | @logs *ARGS:
75 | docker compose logs {{ ARGS }}
76 |
77 | # Run Django management commands
78 | @manage *ARGS:
79 | docker compose run \
80 | --no-deps \
81 | --rm \
82 | utility \
83 | python -m manage {{ ARGS }}
84 |
85 | # Dump database to file
86 | @pg_dump file='db.dump':
87 | docker compose run \
88 | --no-deps \
89 | --rm \
90 | db pg_dump \
91 | --dbname "${DATABASE_URL:=postgres://postgres@db/postgres}" \
92 | --file /src/{{ file }} \
93 | --format=c \
94 | --verbose
95 |
96 | # Restore database dump from file
97 | @pg_restore file='db.dump':
98 | docker compose run \
99 | --no-deps \
100 | --rm \
101 | db pg_restore \
102 | --clean \
103 | --dbname "${DATABASE_URL:=postgres://postgres@db/postgres}" \
104 | --if-exists \
105 | --no-owner \
106 | --verbose \
107 | /src/{{ file }}
108 |
109 | # Restart containers
110 | @restart *ARGS:
111 | docker compose restart {{ ARGS }}
112 |
113 | # Run command in utility container
114 | @run *ARGS:
115 | docker compose run \
116 | --no-deps \
117 | --rm \
118 | utility {{ ARGS }}
119 |
120 | # Start services in detached mode by default
121 | @start *ARGS="--detach":
122 | just up {{ ARGS }}
123 |
124 | # Stop services (alias for down)
125 | @stop *ARGS:
126 | just down {{ ARGS }}
127 |
128 | # Show and follow logs
129 | @tail:
130 | just logs --follow
131 |
132 | # Run pytest with arguments
133 | @test *ARGS:
134 | docker compose run \
135 | --no-deps \
136 | --rm \
137 | utility python -m pytest {{ ARGS }}
138 |
139 | # Start containers
140 | @up *ARGS:
141 | docker compose up {{ ARGS }}
142 |
143 | # Upgrade dependencies and lock
144 | @upgrade:
145 | just lock --upgrade
146 |
--------------------------------------------------------------------------------
/manage.py-tpl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 |
4 | import os
5 | import sys
6 |
7 |
8 | def main():
9 | """Run administrative tasks."""
10 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
11 | try:
12 | from django.core.management import execute_from_command_line
13 | except ImportError as exc:
14 | raise ImportError(
15 | "Couldn't import Django. Are you sure it's installed and "
16 | "available on your PYTHONPATH environment variable? Did you "
17 | "forget to activate a virtual environment?"
18 | ) from exc
19 | execute_from_command_line(sys.argv)
20 |
21 |
22 | if __name__ == "__main__":
23 | main()
24 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "{{ project_name }}"
3 | version = "0.1.0"
4 | description = "Add your description here"
5 | readme = "README.md"
6 | requires-python = ">=3.12"
7 | dependencies = [
8 | "django<5.2",
9 | "django-click",
10 | "environs[django]",
11 | "psycopg[binary]",
12 | "whitenoise",
13 | ]
14 |
15 | [tool.black]
16 | target-version = ["py312"]
17 |
18 | [tool.coverage.run]
19 | omit = [
20 | "*/admin.py",
21 | "*/manage.py",
22 | "*/migrations/*",
23 | "*/tests/*",
24 | "conftest.py",
25 | ]
26 |
27 | [tool.pytest.ini_options]
28 | DJANGO_SETTINGS_MODULE = "config.settings"
29 | addopts = "--cov --nomigrations --reuse-db"
30 | norecursedirs = ".git* frontend media static templates"
31 | python_files = "test_*.py"
32 |
33 | [tool.ruff]
34 | # Exclude a variety of commonly ignored directories.
35 | exclude = [
36 | ".bzr",
37 | ".direnv",
38 | ".eggs",
39 | ".git",
40 | ".github",
41 | ".hg",
42 | ".ruff_cache",
43 | ".svn",
44 | ".tox",
45 | ".venv",
46 | "__pypackages__",
47 | "_build",
48 | "build",
49 | "dist",
50 | "migrations",
51 | "node_modules",
52 | "static",
53 | ]
54 | # Same as Black.
55 | line-length = 120
56 | # Assume Python 3.12.
57 | target-version = "py312"
58 |
59 | [tool.ruff.lint]
60 | # Allow unused variables when underscore-prefixed.
61 | dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
62 | # Allow autofix for all enabled rules (when `--fix`) is provided.
63 | fixable = ["A", "B", "C", "D", "E", "F"]
64 | ignore = ["E501", "E741"] # temporary
65 | per-file-ignores = {}
66 | # Enable Pyflakes `E` and `F` codes by default.
67 | select = ["E", "F"]
68 | unfixable = []
69 |
--------------------------------------------------------------------------------
/requirements.in:
--------------------------------------------------------------------------------
1 | Django<6.0
2 | django-click
3 | environs[django]
4 | psycopg[binary]
5 | whitenoise
6 |
7 | django-test-plus
8 | model-bakery
9 | pre-commit
10 | pytest
11 | pytest-cov
12 | pytest-django
13 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | # This file was autogenerated by uv via the following command:
2 | # uv pip compile --output-file requirements.txt requirements.in
3 | asgiref==3.8.1
4 | # via django
5 | cfgv==3.4.0
6 | # via pre-commit
7 | click==8.2.0
8 | # via django-click
9 | coverage==7.8.0
10 | # via pytest-cov
11 | distlib==0.3.9
12 | # via virtualenv
13 | dj-database-url==2.3.0
14 | # via environs
15 | dj-email-url==1.0.6
16 | # via environs
17 | django==5.2.1
18 | # via
19 | # -r requirements.in
20 | # dj-database-url
21 | # model-bakery
22 | django-cache-url==3.4.5
23 | # via environs
24 | django-click==2.4.1
25 | # via -r requirements.in
26 | django-test-plus==2.2.4
27 | # via -r requirements.in
28 | environs==14.1.1
29 | # via -r requirements.in
30 | filelock==3.18.0
31 | # via virtualenv
32 | identify==2.6.10
33 | # via pre-commit
34 | iniconfig==2.1.0
35 | # via pytest
36 | marshmallow==4.0.0
37 | # via environs
38 | model-bakery==1.20.4
39 | # via -r requirements.in
40 | nodeenv==1.9.1
41 | # via pre-commit
42 | packaging==25.0
43 | # via pytest
44 | platformdirs==4.3.8
45 | # via virtualenv
46 | pluggy==1.5.0
47 | # via pytest
48 | pre-commit==4.2.0
49 | # via -r requirements.in
50 | psycopg==3.2.8
51 | # via -r requirements.in
52 | psycopg-binary==3.2.8
53 | # via psycopg
54 | pytest==8.3.5
55 | # via
56 | # -r requirements.in
57 | # pytest-cov
58 | # pytest-django
59 | pytest-cov==6.1.1
60 | # via -r requirements.in
61 | pytest-django==4.11.1
62 | # via -r requirements.in
63 | python-dotenv==1.1.0
64 | # via environs
65 | pyyaml==6.0.2
66 | # via pre-commit
67 | sqlparse==0.5.3
68 | # via django
69 | typing-extensions==4.13.2
70 | # via dj-database-url
71 | virtualenv==20.31.2
72 | # via pre-commit
73 | whitenoise==6.9.0
74 | # via -r requirements.in
75 |
--------------------------------------------------------------------------------