nickjj / docker-django-example

A production ready example Django app that's using Docker and Docker Compose.
MIT License
1.17k stars 249 forks source link

Code reloading stopped working consistently when updating HTML templates #36

Closed nickjj closed 1 year ago

nickjj commented 1 year ago

Set up

Situation

After changing watched files you should see something like this:

hellodjango-web-1       | [2023-02-18 00:22:45 +0000] [8] [INFO] Worker reloading: /app/src/config/gunicorn.py modified
hellodjango-web-1       | [2023-02-18 00:22:45 +0000] [8] [INFO] Worker exiting (pid: 8)
hellodjango-web-1       | [2023-02-18 00:22:45 +0000] [10] [INFO] Booting worker with pid: 10

The above sometimes works if you edit the src/templates/layouts/index.html or src/pages/templates/pages/home.html and always works if you edit a Python file such as any views.py file. JS and CSS also get updated on change in development which is using esbuild and tailwind instead of gunicorn.

The appropriate env vars defined in the .env file appear to be set in the container's runtime environment:

nick@kitt ~/src/github/docker-django-example (main) $ run shell
python@49922f0f9045:/app/src$ python3
Python 3.11.1 (main, Feb  4 2023, 11:23:15) [GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> from distutils.util import strtobool
>>> bool(strtobool(os.getenv("DEBUG", "false")))
True
>>> bool(strtobool(os.getenv("WEB_RELOAD", "false")))
True
>>>

I also confirmed this in the app by going to the src/config/settings.py file and dropping in:

print(bool(strtobool(os.getenv("DEBUG", "false"))))
print(DEBUG)

The Docker Compose logs produce True for both. That is what we want since both are set to true in the .env.

The TEMPLATES configuration is:

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [os.path.join(BASE_DIR, "templates")],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]

Is that not correct?

I also removed the assets container thinking maybe esbuild 0.17.X is having an effect due to both containers sharing the same Docker bind mount and I wanted to reduce variables but the same problem happens. My other Flask example app is using the same version of gunicorn and esbuild and its HTML code reloading is working.

Little help?

chrisjsimpson commented 1 year ago

Hey @nickjj 👋

I'm assuming you've already played with the compose options

tty

and

std-in

For attaching stdin (similar to when forcing a tty on ssh connections)

Sorry I've not dug deep but wanted to throw those out there incase you've not turned that stone

nickjj commented 1 year ago

Hi @chrisjsimpson,

Thanks, tty: true is set and stdin_open: true has no effect (it's not in my compose file but I did try it just now).

At some point in the past all of this worked in this app. My Flask, Rails, Node and Phoenix examples all work with HTML template, code and asset changes using the same Docker settings across the board too.

One step I should take is to start checking out older versions of this code and identify the last known good state. I'll probably start by looking for commits that change the versions of gunicorn and django.

nickjj commented 1 year ago

This has been resolved in https://github.com/nickjj/docker-django-example/commit/f706a63ce92c1bef081b615981abfaf045447e8f.

It was due to Django 4.1+ caching templates by default even with DEBUG = True set.

Thanks @jefftriplett for pointing me to the changelog at https://docs.djangoproject.com/en/dev/releases/4.1/#templates when I tweeted this out https://twitter.com/nickjanetakis/status/1626938661500604417.

At least now I know I'm not crazy because these templates did reload in the past.

If anyone has a better implementation than what I did in that commit please open a PR.

nickjj commented 1 year ago

So this is fun, I was able to determine why it was so inconsistent and how it sort of worked before the patch.

Before this patch if you edited any HTML template it would not get updated. However if you edited a Python file such as the settings file after editing the HTML then the HTML would update. While testing things initially I reduced variables by hard coding DEBUG = True but that action caused a Python file update, the code itself wasn't important. That's how I arrived at inconsistent behavior. In the end this likely means Django will reload cached templates if Python files are updated.