├── .gitignore ├── Dockerfile ├── Procfile ├── README.md ├── djheroku ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py ├── docker-compose.yml ├── keygen ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── templates │ └── keygen │ │ └── index.html ├── urls.py └── views.py ├── manage.py ├── requirements.txt ├── runtime.txt └── screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Pull base image 2 | FROM python:3.9-slim-buster 3 | 4 | # Install psql so that "python manage.py dbshell" works 5 | RUN apt-get update -qq && apt-get install -y postgresql-client 6 | 7 | # Set environment variables 8 | ENV PYTHONDONTWRITEBYTECODE 1 9 | ENV PYTHONUNBUFFERED 1 10 | 11 | # Set work directory 12 | WORKDIR /app 13 | 14 | # Install dependencies 15 | COPY requirements.txt /app/requirements.txt 16 | RUN pip install -r /app/requirements.txt 17 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | release: python manage.py migrate 2 | 3 | web: gunicorn djheroku.wsgi --log-file - 4 | 5 | worker: python manage.py rqworker default 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Django Docker Heroku Tutorial 2 | 3 | This tutorial demonstrates how to configure a [Django](https://www.djangoproject.com/) 4 | app for development in [Docker](https://en.wikipedia.org/wiki/Docker_%28software%29) 5 | and deployment to [Heroku](https://www.heroku.com/what). 6 | 7 | While the app is intentionally very simple—it generates random tokens that can 8 | be used as Django secret keys—the configuration is relatively complex: 9 | 10 | * [Postgres](https://www.postgresql.org/) is used for the database 11 | * [Redis](https://redis.io/) is used for the cache 12 | * [Django-RQ](https://github.com/rq/django-rq) is used to process background jobs 13 | 14 | Additionally [WhiteNoise](http://whitenoise.evans.io/en/stable/django.html) is 15 | used to manage static files and [Django Debug Toolbar](https://django-debug-toolbar.readthedocs.io/en/latest/) 16 | is configured to run in the Docker development environment. 17 | 18 | Please note that this is *not* a general tutorial on Django development or using 19 | Docker. I assume that you're familiar with Django and are at least aware of 20 | Docker, and the benefits of containerization more generally, but want to learn 21 | how to configure a simple but real-world Django application for development in 22 | Docker, and, as a bonus, deployment to Heroku. 23 | 24 | ## Contents 25 | 26 | - [Developing in Docker](#developing-in-docker) 27 | - [Quick Start](#quick-start) 28 | - [Django Dockerfile](#django-dockerfile) 29 | - [Docker Compose Services](#docker-compose-services) 30 | - [Running Django Debug Toolbar in Docker Compose](#running-django-debug-toolbar-in-docker-compose) 31 | - [Deploying to Heroku](#deploying-to-heroku) 32 | - [Quick Start](#quick-start-1) 33 | - [Heroku Procfile](#heroku-procfile) 34 | - [Why Deploy to Heroku](#why-deploy-to-heroku) 35 | - [Why Not Deploy to Heroku](#why-not-deploy-to-heroku) 36 | - [Further Reading](#further-reading) 37 | - [Conclusion](#conclusion) 38 | 39 | ## Developing in Docker 40 | 41 | ### Quick Start 42 | 43 | Prerequisites: 44 | 45 | * [Git](https://git-scm.com/) 46 | * [Docker](https://docs.docker.com/install/) 47 | * [Docker Compose](https://docs.docker.com/compose/install/) 48 | 49 | Download the tutorial code into a new directory: 50 | 51 | ```sh 52 | $ git clone https://github.com/jbarham/django-docker-heroku-tutorial.git djheroku 53 | $ cd djheroku 54 | ``` 55 | 56 | Run `docker-compose up -d --build` to download the Docker images and bring 57 | up the development environment in Docker Compose. This will take a while the 58 | first time, but on subsequent runs will be much quicker as the Docker images 59 | will be cached. 60 | 61 | Assuming the above step succeeded, you should now have a Django app running 62 | in a Docker container service named `web`, connected to other container 63 | services running Postgres and Redis, and a separate background task runner. 64 | 65 | However, the Postgres database will be empty. Populate it by running: 66 | 67 | ```sh 68 | $ docker-compose exec web python manage.py migrate 69 | ``` 70 | 71 | The above command runs the command `python manage.py migrate` in the Django 72 | Docker container. You could accomplish the same by running: 73 | 74 | ```sh 75 | $ docker-compose exec web bash 76 | # python manage.py migrate 77 | # exit 78 | ``` 79 | 80 | All going well you should now be able to open the Django app at 81 | http://localhost:8000/. 82 | 83 |  84 | 85 | Obviously it doesn't take two seconds to generate a random 86 | key but the background task sleeps for two seconds to simulate a real-world 87 | time consuming operation such as sending an email or generating a PDF. 88 | 89 | When you're finished with the app just run `docker-compose down` to stop 90 | the app and its supporting services. 91 | 92 | ### Django Dockerfile 93 | 94 | At the heart of Docker is the `Dockerfile`, a simple plain text file that 95 | defines how to build a Docker *image* which can be run as a Docker *container*. 96 | 97 | Here is the `Dockerfile` for our Django app in its entirety: 98 | 99 | ``` 100 | # Pull base image 101 | FROM python:3.9-slim-buster 102 | 103 | # Install psql so that "python manage.py dbshell" works 104 | RUN apt-get update -qq && apt-get install -y postgresql-client 105 | 106 | # Set environment variables 107 | ENV PYTHONDONTWRITEBYTECODE 1 108 | ENV PYTHONUNBUFFERED 1 109 | 110 | # Set work directory 111 | WORKDIR /app 112 | 113 | # Install dependencies 114 | COPY requirements.txt /app/requirements.txt 115 | RUN pip install -r /app/requirements.txt 116 | ``` 117 | 118 | By itself, though, this Dockerfile doesn't provide much more than we 119 | could get from developing our Django app in a traditional Python virtual 120 | environment. 121 | 122 | This is where Docker Compose comes in. 123 | 124 | ### Docker Compose Services 125 | 126 | Here is a summary of Docker Compose from the [official 127 | documentation](https://docs.docker.com/compose/): 128 | 129 | > Compose is a tool for defining and running multi-container Docker 130 | applications. With Compose, you use a YAML file to configure your application’s 131 | services. Then, with a single command, you create and start all the services 132 | from your configuration. 133 | 134 | Our Docker Compose configuration file, [`docker-compose.yml`](./docker-compose.yml), 135 | defines four services, `web`, `worker`, `db` and `redis`, each of which runs in 136 | a separate Docker container. (Note that the internal hostname for each service 137 | is the same as the service name. So to connect to the Redis server from Django we 138 | use `redis:6379` as the *hostname:port* pair.) 139 | 140 | Taking each service in turn: 141 | 142 | ``` 143 | web: 144 | build: . 145 | command: python manage.py runserver 0.0.0.0:8000 146 | environment: 147 | DEBUG: 1 148 | volumes: 149 | - .:/app 150 | ports: 151 | - 8000:8000 152 | depends_on: 153 | - db 154 | - redis 155 | ``` 156 | 157 | The `web` service runs our Django app in a Docker container defined by our 158 | `Dockerfile`. 159 | 160 | We set the environment variable `DEBUG=1` which sets the `DEBUG` variable in the 161 | app's [`settings.py`](djheroku/settings.py) file to `True`. 162 | 163 | The `volumes` section says that we want to map the current directory to the 164 | `/app` mountpoint in the Docker container. This means that any changes made to 165 | the application code while the container is running cause the Django server to 166 | reload, as it would it if were running outside of Docker. (Tangentially, this 167 | also explains why we set the `PYTHONDONTWRITEBYTECODE` flag in our Dockerfile. 168 | Since Django is run by the `root` user inside the `web` container, we don't 169 | want the root user to save cached bytecode files to our application directory.) 170 | 171 | ``` 172 | worker: 173 | build: . 174 | command: python manage.py rqworker default 175 | environment: 176 | DEBUG: 1 177 | volumes: 178 | - .:/app 179 | depends_on: 180 | - web 181 | ``` 182 | 183 | The `worker` service runs [Django-RQ](https://github.com/rq/django-rq) to 184 | process background tasks. Since Django-RQ runs as a Django management command, 185 | it's configured very similarly to the Django app server, 186 | with the notable exception that we don't define a port mapping since it doesn't 187 | have a web interface. 188 | 189 | ``` 190 | db: 191 | image: postgres:latest 192 | restart: always 193 | environment: 194 | POSTGRES_PASSWORD: postgres 195 | POSTGRES_USER: postgres 196 | POSTGRES_DB: djheroku 197 | volumes: 198 | - pgdata:/var/lib/postgresql/data/ 199 | ``` 200 | 201 | The `db` service installs and configures a full-blown Postgres database server, 202 | creating a database for our Django app, using the most recent Postgres Docker 203 | image. This is where using Docker really shines. 204 | 205 | For more details on the configuration environment variables see the 206 | [Docker Postgres documentation](https://hub.docker.com/_/postgres/). 207 | 208 | We define a `volumes` section for our database so that the data itself is saved 209 | outside of the database container. Otherwise when the container is shut down 210 | we'd lose the contents of our database! 211 | 212 | ``` 213 | redis: 214 | image: redis:latest 215 | ``` 216 | 217 | Short and sweet, the `redis` service says that we want a Redis server in our 218 | Docker Compose environment, and we're happy with the default configuration. 219 | 220 | ### Running Django Debug Toolbar in Docker Compose 221 | 222 | [Django Debug Toolbar](https://django-debug-toolbar.readthedocs.io/en/latest/) 223 | is an invaluable plugin for Django developers as it provides very detailed 224 | runtime profiling information that you can use to optimize your app's database 225 | queries and templates. 226 | 227 | By default Django Debug Toolbar only runs if the Django settings `DEBUG` 228 | flag is set to `True` and the development server's IP address is defined in the 229 | [INTERNAL_IPS](https://docs.djangoproject.com/en/dev/ref/settings/#internal-ips) 230 | list. In development `INTERNAL_IPS` is typically set to `['localhost', '127.0.0.1']`. 231 | 232 | However, services running in Docker Compose are assigned an ephemeral IP address 233 | so Django Debug Toolbar won't run. To enable Django Debug Toolbar in Docker 234 | Compose we instead used the following configuration option in our 235 | [`settings.py`](djheroku/settings.py): 236 | 237 | ```python 238 | DEBUG_TOOLBAR_CONFIG = { 239 | 'SHOW_TOOLBAR_CALLBACK': lambda request: DEBUG, 240 | } 241 | ``` 242 | 243 | See the [Django Debug Toolbar documentation](https://django-debug-toolbar.readthedocs.io/en/latest/configuration.html#show-toolbar-callback) for more details. 244 | 245 | ## Deploying to Heroku 246 | 247 | ### Quick Start 248 | 249 | Prerequisites: 250 | 251 | * [Create a free Heroku account](https://signup.heroku.com/) 252 | * [Install and log into the Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli) 253 | 254 | Run `heroku create` to create a new Heroku app with a randomly generated name. 255 | 256 | Create a new Heroku Postgres database: 257 | 258 | ```sh 259 | $ heroku addons:create heroku-postgresql:hobby-dev 260 | ``` 261 | 262 | Once Heroku has finished creating the Postgres database, it will create the 263 | environment variable `DATABASE_URL` which contains the login credentials to the 264 | database. You can view the Heroku app's environment by running `heroku config`. 265 | 266 | Create a new Heroku Redis server: 267 | 268 | ```sh 269 | $ heroku addons:create heroku-redis:hobby-dev 270 | ``` 271 | 272 | Similarly Heroku will create an environment variable `REDIS_URL` once it's 273 | finished creating the Redis instance. 274 | 275 | Since Heroku is our production environment, for security we should override the 276 | default Django SECRET_KEY by setting the SECRET_KEY environment variable. 277 | Copy a generated key from the development app and run: 278 | 279 | ```sh 280 | $ heroku config:set SECRET_KEY='replace me with a generated secret key' 281 | ``` 282 | 283 | Time to go live! Upload and deploy our app to Heroku: 284 | 285 | ```sh 286 | $ git push heroku master 287 | ``` 288 | 289 | This will take some time as Heroku builds the app and deploys it to a web dyno, 290 | comparable to a Docker container. 291 | 292 | You should now able to view the Django app running in Heroku by running 293 | `heroku open`. 294 | 295 | The background task runner isn't run by default so spin one up: 296 | 297 | ```sh 298 | $ heroku ps:scale worker=1 299 | ``` 300 | 301 | Shut down the app in Heroku by running: 302 | 303 | ```sh 304 | $ heroku ps:scale web=0 worker=0 305 | ``` 306 | 307 | Free Heroku web dynos will automatically [go to sleep after 30 minutes of inactivity](https://devcenter.heroku.com/articles/free-dyno-hours#dyno-sleeping) but worker dynos will keep running until you 308 | explicitly shut them down. 309 | 310 | To permanently delete the Heroku app, including the Postgres database and Redis 311 | server, run `heroku apps:destroy`. 312 | 313 | ### Heroku Procfile 314 | 315 | Our app directory already contains a top-level `requirements.txt` file, which 316 | tells Heroku that this is a Python app and it should `pip` install the packages 317 | in the requirements file when it builds the app. 318 | 319 | How our app should be run after it's been built is defined in a `Procfile`: 320 | 321 | ``` 322 | release: python manage.py migrate 323 | 324 | web: gunicorn djheroku.wsgi --log-file - 325 | 326 | worker: python manage.py rqworker default 327 | ``` 328 | 329 | The `release` line specifies that we want Heroku to run database migrations on 330 | the app in the app's release phase. See the 331 | [Heroku documentation](https://devcenter.heroku.com/articles/release-phase) 332 | for more details. 333 | 334 | The `web` process type defines how to run our Django web server. Since we're running 335 | in production we use `gunicorn` instead of Django's test server that we use in 336 | Docker. See the 337 | [Heroku documentation](https://devcenter.heroku.com/articles/getting-started-with-python?singlepage=true#define-a-procfile) 338 | for more details. 339 | 340 | Finally the `worker` process type defines how to run our background task runner. 341 | Note that the command is exactly the same as the worker service in our Docker 342 | Compose configuration. 343 | 344 | ### Why Deploy to Heroku 345 | 346 | We've seen above how we can deploy a Django app to Heroku, using Postgres and 347 | Redis, with a background job runner, by writing a three line `Procfile` config 348 | file and running `git push heroku master`. That's really as easy as it gets to 349 | deploy a web app to production. 350 | 351 | By comparison 352 | [this tutorial](https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-ubuntu-18-04) 353 | shows how to set up Django with Postgres, Nginx and Gunicorn to run on a 354 | [Digital Ocean](https://www.digitalocean.com/) virtual machine. It's a clear, 355 | comprehensive tutorial which I've referenced many times myself. You'll also notice 356 | that it's very long, with lots of steps, each of which must be completed 357 | without making a mistake to get your app up and running. If you want to get the 358 | same app running on a different (e.g., bigger) virtual machine, you'll have to 359 | repeat the process all over again, or write a script. 360 | 361 | ### Why Not Deploy to Heroku 362 | 363 | By default Django uses SQLite for its database. SQLite is an excellent option for 364 | Django sites that are [predominantly 365 | read-only](https://docs.djangoproject.com/en/dev/ref/databases/#sqlite-notes) 366 | and will only ever run on a single server. But using SQLite as your database on 367 | Heroku isn't an option since [Heroku dyno filesystems are 368 | ephemeral](https://devcenter.heroku.com/articles/dynos#ephemeral-filesystem). 369 | 370 | I run a hobby site, [M43 Lenses](https://www.m43lenses.com/), that gets light 371 | traffic and is only updated by me so it runs happily on a $5/month Digital Ocean 372 | VM using SQLite for its database. 373 | 374 | Another consequence of the ephemeral nature of the filesystem on Heroku dynos 375 | is that if you have a site that allows users to upload e.g. image files, you 376 | will need to configure your app to save those files to an external file storage service 377 | such as [AWS S3](https://aws.amazon.com/s3/). This is pretty straightforward to 378 | set up in Django using [django-storages](https://django-storages.readthedocs.io/en/latest/index.html), 379 | but you may decide that it's more trouble than it's worth to set up on Heroku 380 | and stick with conventional VM hosting providers like Linode or Digital Ocean 381 | that provide permanent disk storage with your VM. 382 | 383 | ## Further Reading 384 | 385 | [Will Vincent](https://wsvincent.com/) has published many excellent Django 386 | tutorials on his website [LearnDjango.com](https://learndjango.com/). 387 | I also highly recommend [his books](https://learndjango.com/books/) for beginner 388 | and experienced Django developers. 389 | 390 | Similar to this tutorial, Will develops a detailed example in his book 391 | [*Django for Professionals*](https://djangoforprofessionals.com/) on how to 392 | develop a Django app in Docker and deploy it to Heroku. My tutorial is different 393 | in that I do not use Pipenv (which seems to have fallen out of fashion since 394 | *Django for Professionals* was published) and I don't deploy to Heroku's 395 | alternative Docker runtime environment. 396 | 397 | For intermediate to advanced Django developers I highly recommend the 398 | [*Two Scoops of Django*](https://www.feldroy.com/collections/two-scoops-press) 399 | series by Daniel and Audrey Feldroy (formerly Daniel and Audrey Roy Greenfeld). 400 | 401 | Vitor Freitas has published a number of very hiqh quality, in-depth articles 402 | about Django development on his website 403 | [Simple is Better Than Complex](https://simpleisbetterthancomplex.com/). 404 | 405 | For the official Heroku Python/Django tutorial read [Getting Started on Heroku with Python](https://devcenter.heroku.com/articles/getting-started-with-python). 406 | 407 | [The Twelve-Factor App website](https://12factor.net/) lists best practices for 408 | developing and deploying software-as-a-service web apps. The 12 factors were 409 | formulated by Adam Wiggins, one of the cofounders of Heroku. 410 | 411 | ## Conclusion 412 | 413 | If you've got this far, congratulations! My goal in writing this tutorial was 414 | to present some of the knowledge that I've learned by trial and error developing 415 | Django web apps in Docker and deploying them to Heroku. Your particular context 416 | will be different from mine, but I hope that this tutorial is a useful starting 417 | point to make you more productive developing your own Django apps. 418 | 419 | Please feel free to email me at [john@wombatsoftware.com](mailto:john@wombatsoftware.com) 420 | if you have any comments or questions about this tutorial. 421 | I also do Django consulting through my company, [Wombat Software](https://www.wombatsoftware.com/). 422 | 423 | Thanks for reading! 424 | 425 | John Barham, [Wombat Software](https://www.wombatsoftware.com/) 426 | -------------------------------------------------------------------------------- /djheroku/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbarham/django-docker-heroku-tutorial/93765bebd7b5f2a24921470e3518185dcf017a55/djheroku/__init__.py -------------------------------------------------------------------------------- /djheroku/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for djheroku project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.2.11. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.2/ref/settings/ 11 | """ 12 | 13 | import os 14 | from pathlib import Path 15 | import dj_database_url 16 | 17 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 18 | BASE_DIR = Path(__file__).resolve().parent.parent 19 | 20 | 21 | # Quick-start development settings - unsuitable for production 22 | # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ 23 | 24 | # SECURITY WARNING: keep the secret key used in production secret! 25 | # Use SECRET_KEY environment variable to override default key. 26 | SECRET_KEY = os.getenv('SECRET_KEY', 'q#br=ggq_)j+c@2zrvr0hpxl+0di(@!l!#3u7gem-dgs0nw#l4') 27 | 28 | # SECURITY WARNING: don't run with debug turned on in production! 29 | DEBUG = int(os.getenv('DEBUG', 0)) 30 | 31 | ALLOWED_HOSTS = ['.herokuapp.com', 'localhost', '127.0.0.1'] 32 | 33 | 34 | # Application definition 35 | 36 | INSTALLED_APPS = [ 37 | 'whitenoise.runserver_nostatic', 38 | 'django.contrib.admin', 39 | 'django.contrib.auth', 40 | 'django.contrib.contenttypes', 41 | 'django.contrib.sessions', 42 | 'django.contrib.messages', 43 | 'django.contrib.staticfiles', 44 | 'django_rq', 45 | 'debug_toolbar', 46 | 'keygen', 47 | ] 48 | 49 | MIDDLEWARE = [ 50 | 'django.middleware.security.SecurityMiddleware', 51 | 'whitenoise.middleware.WhiteNoiseMiddleware', 52 | 'django.contrib.sessions.middleware.SessionMiddleware', 53 | 'django.middleware.common.CommonMiddleware', 54 | 'django.middleware.csrf.CsrfViewMiddleware', 55 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 56 | 'django.contrib.messages.middleware.MessageMiddleware', 57 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 58 | 'debug_toolbar.middleware.DebugToolbarMiddleware', 59 | ] 60 | 61 | ROOT_URLCONF = 'djheroku.urls' 62 | 63 | TEMPLATES = [ 64 | { 65 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 66 | 'DIRS': [], 67 | 'APP_DIRS': True, 68 | 'OPTIONS': { 69 | 'context_processors': [ 70 | 'django.template.context_processors.debug', 71 | 'django.template.context_processors.request', 72 | 'django.contrib.auth.context_processors.auth', 73 | 'django.contrib.messages.context_processors.messages', 74 | ], 75 | }, 76 | }, 77 | ] 78 | 79 | WSGI_APPLICATION = 'djheroku.wsgi.application' 80 | 81 | 82 | # Configure database using DJ-Database-URL. 83 | # See docs at https://github.com/jacobian/dj-database-url#dj-database-url. 84 | 85 | DATABASES = { 86 | # If DATABASE_URL environment variable isn't set, use Docker Compose Postgres database. 87 | 'default': dj_database_url.config( 88 | default='postgres://postgres:postgres@db:5432/djheroku', 89 | conn_max_age=600, 90 | ) 91 | } 92 | 93 | 94 | # Password validation 95 | # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators 96 | 97 | AUTH_PASSWORD_VALIDATORS = [ 98 | { 99 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 100 | }, 101 | { 102 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 103 | }, 104 | { 105 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 106 | }, 107 | { 108 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 109 | }, 110 | ] 111 | 112 | 113 | # Internationalization 114 | # https://docs.djangoproject.com/en/3.2/topics/i18n/ 115 | 116 | LANGUAGE_CODE = 'en-us' 117 | 118 | TIME_ZONE = 'UTC' 119 | 120 | USE_I18N = True 121 | 122 | USE_L10N = True 123 | 124 | USE_TZ = True 125 | 126 | 127 | # Static files (CSS, JavaScript, Images) 128 | # https://docs.djangoproject.com/en/3.2/howto/static-files/ 129 | 130 | STATIC_URL = '/static/' 131 | STATIC_ROOT = BASE_DIR / 'staticfiles' 132 | # Enable WhiteNoise compression and caching support. 133 | STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' 134 | 135 | 136 | # Configure Redis cache and Django-RQ. 137 | 138 | # https://django-redis-cache.readthedocs.io/en/latest/intro_quick_start.html#quick-start 139 | CACHES = { 140 | 'default': { 141 | 'BACKEND': 'redis_cache.RedisCache', 142 | # By default use Docker Compose Redis instance. 143 | 'LOCATION': os.getenv('REDIS_URL', 'redis:6379'), 144 | }, 145 | } 146 | 147 | # https://github.com/rq/django-rq#support-for-django-redis-and-django-redis-cache 148 | RQ_QUEUES = { 149 | 'default': { 150 | 'USE_REDIS_CACHE': 'default', 151 | 'DEFAULT_TIMEOUT': 360, 152 | }, 153 | } 154 | RQ_SHOW_ADMIN_LINK = True 155 | 156 | 157 | # Show Debug Toolbar if DEBUG is True. 158 | 159 | DEBUG_TOOLBAR_CONFIG = { 160 | 'SHOW_TOOLBAR_CALLBACK': lambda request: DEBUG, 161 | } 162 | 163 | # Default primary key field type 164 | # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field 165 | 166 | DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' 167 | -------------------------------------------------------------------------------- /djheroku/urls.py: -------------------------------------------------------------------------------- 1 | """djheroku URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.2/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.conf import settings 17 | from django.contrib import admin 18 | from django.urls import include, path 19 | 20 | urlpatterns = [ 21 | path('', include('keygen.urls')), 22 | path('admin/', admin.site.urls), 23 | path('django-rq/', include('django_rq.urls')), 24 | ] 25 | 26 | if settings.DEBUG: 27 | import debug_toolbar 28 | urlpatterns = [ 29 | path('__debug__/', include(debug_toolbar.urls)), 30 | ] + urlpatterns 31 | -------------------------------------------------------------------------------- /djheroku/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for djheroku 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/3.2/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', 'djheroku.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | services: 4 | web: 5 | build: . 6 | command: python manage.py runserver 0.0.0.0:8000 7 | environment: 8 | DEBUG: 1 9 | volumes: 10 | - .:/app 11 | ports: 12 | - 8000:8000 13 | depends_on: 14 | - db 15 | - redis 16 | 17 | worker: 18 | build: . 19 | command: python manage.py rqworker default 20 | environment: 21 | DEBUG: 1 22 | volumes: 23 | - .:/app 24 | depends_on: 25 | - web 26 | 27 | db: 28 | image: postgres:latest 29 | environment: 30 | POSTGRES_PASSWORD: postgres 31 | POSTGRES_USER: postgres 32 | POSTGRES_DB: djheroku 33 | volumes: 34 | - pgdata:/var/lib/postgresql/data/ 35 | 36 | redis: 37 | image: redis:latest 38 | 39 | volumes: 40 | pgdata: 41 | -------------------------------------------------------------------------------- /keygen/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbarham/django-docker-heroku-tutorial/93765bebd7b5f2a24921470e3518185dcf017a55/keygen/__init__.py -------------------------------------------------------------------------------- /keygen/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import Secret 4 | 5 | admin.site.register(Secret) 6 | -------------------------------------------------------------------------------- /keygen/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class KeygenConfig(AppConfig): 5 | name = 'keygen' 6 | -------------------------------------------------------------------------------- /keygen/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.10 on 2020-02-22 00:21 2 | 3 | import django.core.management.utils 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Secret', 17 | fields=[ 18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('created', models.DateTimeField(auto_now_add=True)), 20 | ('key', models.CharField(default=django.core.management.utils.get_random_secret_key, max_length=50)), 21 | ], 22 | options={ 23 | 'ordering': ['-created'], 24 | }, 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /keygen/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbarham/django-docker-heroku-tutorial/93765bebd7b5f2a24921470e3518185dcf017a55/keygen/migrations/__init__.py -------------------------------------------------------------------------------- /keygen/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.core.management.utils import get_random_secret_key 3 | 4 | 5 | class Secret(models.Model): 6 | created = models.DateTimeField(auto_now_add=True) 7 | key = models.CharField(max_length=50, default=get_random_secret_key) 8 | 9 | class Meta: 10 | ordering = ['-created'] 11 | 12 | def __str__(self): 13 | return self.key 14 | -------------------------------------------------------------------------------- /keygen/templates/keygen/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |{{ key }}
No secret keys.
28 | {% endif %} 29 | Generate New Key 30 | Generate New Key in Background 31 | {% if secrets %} 32 | Delete All Keys 33 | {% endif %} 34 |