Additional Basic Settings to Dockerized Django project with Cookiecutter

In previous post, I started a django project with cookiecutter. Then, I added poetry and ruff to the project. In this post, I will add more settings/configurations for the project, so I can use this as my template for other projects.

Prerequisite

Makefile

First thing I want to add is makefile. We can create a file to use as scripts, but I think makefile will be much easier to run the commands on console. The main reason of this is docker and poetry. Docker, docker-compose and poetry commands are quite complicated for me, so I will put all the commands into make file to make the commands easy. Create a file named makefile in root directory.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
SHELL=/bin/bash
start: ## Start the docker containers
@echo "Starting the docker containers"
@docker-compose -f local.yml up
@echo "Containers started - http://localhost:8000"
up: start # alias for start
poetry-export:
@echo "Exporting poetry requirements to requirements/local.txt"
@poetry export --without-hashes --with dev -f requirements.txt -o requirements/local.txt
@echo "Exporting poetry requirements to requirements/base.txt"
@poetry export --without-hashes -f requirements.txt -o requirements/base.txt
@echo "Exporting poetry requirements to requirements/production.txt"
@poetry export --without-hashes --with prod -f requirements.txt -o requirements/production.txt
stop: ## Stop Containers
@docker-compose -f local.yml down --remove-orphans
build: stop ## Build Containers
@docker-compose -f local.yml build
build-new: poetry-export stop ## Build Containers
@docker-compose -f local.yml build --no-cache --pull
migrations: ## Create DB migrations in the container
@docker-compose -f local.yml run --rm --user="$(id -u):$(id -g)" django python manage.py makemigrations
migrate: ## Run DB migrations in the container, optionally with app and migration as `make app=<app> migration=<migration_file_without_.py> migrate`
@docker-compose -f local.yml run --rm --user="$(id -u):$(id -g)" django python manage.py migrate $(app) $(migration)
SHELL=/bin/bash start: ## Start the docker containers @echo "Starting the docker containers" @docker-compose -f local.yml up @echo "Containers started - http://localhost:8000" up: start # alias for start poetry-export: @echo "Exporting poetry requirements to requirements/local.txt" @poetry export --without-hashes --with dev -f requirements.txt -o requirements/local.txt @echo "Exporting poetry requirements to requirements/base.txt" @poetry export --without-hashes -f requirements.txt -o requirements/base.txt @echo "Exporting poetry requirements to requirements/production.txt" @poetry export --without-hashes --with prod -f requirements.txt -o requirements/production.txt stop: ## Stop Containers @docker-compose -f local.yml down --remove-orphans build: stop ## Build Containers @docker-compose -f local.yml build build-new: poetry-export stop ## Build Containers @docker-compose -f local.yml build --no-cache --pull migrations: ## Create DB migrations in the container @docker-compose -f local.yml run --rm --user="$(id -u):$(id -g)" django python manage.py makemigrations migrate: ## Run DB migrations in the container, optionally with app and migration as `make app=<app> migration=<migration_file_without_.py> migrate` @docker-compose -f local.yml run --rm --user="$(id -u):$(id -g)" django python manage.py migrate $(app) $(migration)
SHELL=/bin/bash

start: ## Start the docker containers
	@echo "Starting the docker containers"
	@docker-compose -f local.yml up
	@echo "Containers started - http://localhost:8000"
up: start # alias for start

poetry-export:
	@echo "Exporting poetry requirements to requirements/local.txt"
	@poetry export --without-hashes --with dev -f requirements.txt -o requirements/local.txt
	@echo "Exporting poetry requirements to requirements/base.txt"
	@poetry export --without-hashes -f requirements.txt -o requirements/base.txt
	@echo "Exporting poetry requirements to requirements/production.txt"
	@poetry export --without-hashes --with prod -f requirements.txt -o requirements/production.txt

stop: ## Stop Containers
	@docker-compose -f local.yml down --remove-orphans

build: stop ## Build Containers
	@docker-compose -f local.yml build

build-new: poetry-export stop ## Build Containers
	@docker-compose -f local.yml build --no-cache --pull

migrations: ## Create DB migrations in the container
	@docker-compose -f local.yml run --rm --user="$(id -u):$(id -g)" django python manage.py makemigrations

migrate: ## Run DB migrations in the container, optionally with app and migration as `make app=<app> migration=<migration_file_without_.py> migrate`
	@docker-compose -f local.yml run --rm --user="$(id -u):$(id -g)" django python manage.py migrate $(app) $(migration)

Script files

The makefile sometimes does not take the variable as I expected. I couldn’t find out the correct way to pass the variable. In this case, we can make the script file to work around. I made manage and runtests script files in root directory.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
###runtests
#!/usr/bin/env bash
docker-compose -f local.yml run --rm django coverage run -m pytest "$@" && ruff .
###manage
#!/usr/bin/env bash
docker-compose -f local.yml run --rm --user="$(id -u):$(id -g)" django python -Wd manage.py "$@"
###runtests #!/usr/bin/env bash docker-compose -f local.yml run --rm django coverage run -m pytest "$@" && ruff . ###manage #!/usr/bin/env bash docker-compose -f local.yml run --rm --user="$(id -u):$(id -g)" django python -Wd manage.py "$@"
###runtests
#!/usr/bin/env bash

docker-compose -f local.yml run --rm django coverage run -m pytest "$@" && ruff .


###manage
#!/usr/bin/env bash

docker-compose -f local.yml run --rm --user="$(id -u):$(id -g)" django python -Wd manage.py "$@"

After create the script file, I tried to run them. and I got error permission denied. You can run ls -la on your bash and see if the script files have execute permission or not.

r = read, w = write, x = execute. I don’t have permission to execute the file. How do I change or add permission?

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
chmod +rwx <file_or_directory_name>
chmod +rwx <file_or_directory_name>
chmod +rwx <file_or_directory_name>

In this case, I already have read and write permission, so I could use just

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
chmod +x runtests
chmod +x manage
chmod +x runtests chmod +x manage
chmod +x runtests
chmod +x manage

pytest.ini

Create new file named pytest.ini in root directory. I won’t explain all the details, but just copy the file content here. But you can refer to the pytest documentation for configuration and options to see what they are doing.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
[pytest]
addopts = --ds=config.settings.test
--reuse-db
-s
python_files = tests.py test_*.py
norecursedirs =
.*
compose
dist
docs
htmlcov
locale
node_modules
requirements
static
staticfiles
templates
markers =
admin
core
template_tags
templates
users
urls
widgets
[pytest] addopts = --ds=config.settings.test --reuse-db -s python_files = tests.py test_*.py norecursedirs = .* compose dist docs htmlcov locale node_modules requirements static staticfiles templates markers = admin core template_tags templates users urls widgets
 [pytest]
addopts = --ds=config.settings.test
          --reuse-db
          -s

python_files = tests.py test_*.py
norecursedirs =
    .*
    compose
    dist
    docs
    htmlcov
    locale
    node_modules
    requirements
    static
    staticfiles
    templates

markers =
    admin
    core
    template_tags
    templates
    users
    urls
    widgets

VSCode Workspace Settings

You can configure the vscode settings globally. That’s what I used to do that. However, when working with teams, it is better to have the same workspace settings. Hit ctrl+shift+p and type workspace settings to open up the settings.json file.

Then, copy paste below settings.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
{
"files.associations": {
"*.html": "django-html",
"*.css": "tailwindcss"
},
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
},
"[html][django-html][handlebars][hbs][mustache][jinja][jinja-html][nj][njk][nunjucks][twig]": {
"editor.defaultFormatter": "monosans.djlint"
},
"editor.formatOnSave": true,
"djlint.formatCss": true,
"djlint.formatJs": false,
"djlint.closeVoidTags": true,
"djlint.formatAttributeTemplateTags": true,
"djlint.profile": "django",
"html.format.enable": true,
"html.format.templating": true,
"ruff.lint.args": ["\"args\": [\"--config=pyproject.toml\"]"],
}
{ "files.associations": { "*.html": "django-html", "*.css": "tailwindcss" }, "[python]": { "editor.defaultFormatter": "ms-python.black-formatter" }, "[html][django-html][handlebars][hbs][mustache][jinja][jinja-html][nj][njk][nunjucks][twig]": { "editor.defaultFormatter": "monosans.djlint" }, "editor.formatOnSave": true, "djlint.formatCss": true, "djlint.formatJs": false, "djlint.closeVoidTags": true, "djlint.formatAttributeTemplateTags": true, "djlint.profile": "django", "html.format.enable": true, "html.format.templating": true, "ruff.lint.args": ["\"args\": [\"--config=pyproject.toml\"]"], }
{
  "files.associations": {
    "*.html": "django-html",
    "*.css": "tailwindcss"
  },
  "[python]": {
    "editor.defaultFormatter": "ms-python.black-formatter"
  },
  "[html][django-html][handlebars][hbs][mustache][jinja][jinja-html][nj][njk][nunjucks][twig]": {
    "editor.defaultFormatter": "monosans.djlint"
  },
  "editor.formatOnSave": true,
  "djlint.formatCss": true,
  "djlint.formatJs": false,
  "djlint.closeVoidTags": true,
  "djlint.formatAttributeTemplateTags": true,
  "djlint.profile": "django",
  "html.format.enable": true,
  "html.format.templating": true,
  "ruff.lint.args": ["\"args\": [\"--config=pyproject.toml\"]"],
}

We haven’t covered the tailwindcss in this series, so you can remove tailwindcss settings if you don’t like to use tailwind.