abelcastro.dev

Django and htmx

2021-08-27

HtmxDjango

Using variables in the Dockerfile FROM statement

2021-06-01

Docker

Create and use "dummy" Models in a Test Case in Django

2021-04-17

TestingDjango
12
...
7
8
9

Abel Castro 2025 - checkout the source code of this page on GitHub - Privacy Policy

With htmx is possible to build dynamic Webapps without REST-APIs and JavaScript. Just simple Django views that returns html.

You can learn more about it in this interesting Django chat podcast episode with the htmx creator Carson Gross.

I really like the idea and I wanted since a while to try htmx working together with Django and finally, I managed to work on that. I created this repository with some implementations with Django of the code examples from the htmx docs page.

**Update: **also this website has some htmx magic on it. For example the post pagination or the search function are built with htmx.

This demonstrates how to use a variable as image for the Dockerfile FROM statement.

Given this docker-compose.yml / Dockerfile setup:

docker-compose.yml

version: '3'
services:
	postgres:
	    build:
	      context: .
	      dockerfile: ./compose/postgres/Dockerfile
	    volumes:
				- postgres_data:/var/lib/postgresql/data
	    env_file:
	      - .env
volumes:
    postgres_data:

Dockerfile

FROM postgres:12.3

The Postgres service will always use the same FROM image defined in the Dockerfile. If we want instead to set the FROM image using a variable, we can do the following:

Docker-compose.yml

  • pass a build_image arg with a default value
version: '3'
services:
	postgres:
	    build:
	      context: .
	      dockerfile: ./compose/postgres/Dockerfile
	      args:
	       build_image: "${BUILD_IMAGE:-postgres:12.3}" 
	    volumes:
				- postgres_data:/var/lib/postgresql/data
	    env_file:
	      - .env
volumes:
    postgres_data:

Dockerfile

  • use the passed build_image arg in the FROM statement
# this will be the default image
ARG build_image="postgres:12.3"

# The default image will be overriden if other build image is passed as ARG
ARG build_image=$build_image
FROM $build_image

.env

  • Pass the desired value in .env
BUILD_IMAGE=postgis:9.6

Let's image we need a new model only for a test case and we don't really want to register in our project. We can create something similar than this:

example_app.tests.test_app.models.TestModel

from django.db import models


class TestModel(models.Model):
    field_a = models.IntegerField()
    field_b = models.IntegerField()

    class Meta:
        app_label = 'test_app'

We could try to use TestModel and create objects in a test case: test_models.py

from django.test import TestCase

from example_app.tests.test_app.models import TestModel


class TestOverridingInstalledApps(TestCase):
    def setUp(self):
        self.test_model = TestModel.objects.create(
            field_a=1,
            field_b=2,
        )

    def test_objects(self):
        self.assertEqual(TestModel.objects.count(), 1)

But if you run the tests like this, the test will fail and return something similar than that:

./manage.py test

django.db.utils.ProgrammingError: relation "test_app_testmodel" does not exist
LINE 1: INSERT INTO "test_app_testmodel" ("field_a", "field_b") VALU...

It fails because Django needs to have TestModel registered in INSTALLED_APPS but we don't really want to add our example_app.tests.test_app to INSTALLED_APPS because we only need it when we run the tests.

The solution is to to add the test_app to the settings with modify_settings and calling migrate.

test_models.py

from django.core.management import call_command
from django.test import TestCase, modify_settings

from example_app.tests.test_app.models import TestModel


@modify_settings(INSTALLED_APPS={
    'append': 'example_app.tests.test_app',
})
class TestOverridingInstalledApps(TestCase):
    def setUp(self):
        call_command('migrate', run_syncdb=True)
        self.test_model = TestModel.objects.create(
            field_a=1,
            field_b=2,
        )

    def test_objects(self):
        self.assertEqual(TestModel.objects.count(), 1)

You can see the complete source code here.