diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..860adf6 --- /dev/null +++ b/.envrc @@ -0,0 +1,45 @@ +use flake +eval "$shellHook" +layout_postgres() { + export PGDATA="$(direnv_layout_dir)/postgres" + export PGHOST="$PGDATA" + if [[ ! -d "$PGDATA" ]]; then + initdb + echo -e "listen_addresses = 'localhost'\nunix_socket_directories = '$PGHOST'" >> "$PGDATA/postgresql.conf" + echo "CREATE DATABASE django;" | postgres --single -E postgres + fi +} +layout postgres + +layout_poetry() { + PYPROJECT_TOML="${PYPROJECT_TOML:-pyproject.toml}" + if [[ ! -f "$PYPROJECT_TOML" ]]; then + log_status "No pyproject.toml found. Executing \`poetry init\` to create a \`$PYPROJECT_TOML\` first." + poetry init + fi + + if [[ -d ".venv" ]]; then + VIRTUAL_ENV="$(pwd)/.venv" + poetry install + else + VIRTUAL_ENV=$(poetry env info --path 2>/dev/null ; true) + fi + + if [[ -z $VIRTUAL_ENV || ! -d $VIRTUAL_ENV ]]; then + log_status "No virtual environment exists. Executing \`poetry install\` to create one." + poetry install + VIRTUAL_ENV=$(poetry env info --path) + fi + + PATH_add "$VIRTUAL_ENV/bin" + export POETRY_ACTIVE=1 + export VIRTUAL_ENV +} +layout poetry +export PROJECT_DIR=$(pwd) +export WEBPORT=$(($RANDOM + 1100)) +export PGPORT=$(($WEBPORT + 100)) +watch_file "$PGDATA/postgresql.conf" +watch_file ./dev.sh +watch_file ./pyproject.toml + diff --git a/.gitignore b/.gitignore index eb09c92..946e9cf 100644 --- a/.gitignore +++ b/.gitignore @@ -251,3 +251,4 @@ flycheck_*.el result result-* +/.direnv/ diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..070d7cf --- /dev/null +++ b/Procfile @@ -0,0 +1,2 @@ +web: python ./src/manage.py runserver 0.0.0.0:$WEBPORT +db: postgres -p $PGPORT diff --git a/README.md b/README.md index e5a554f..fe72c72 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,46 @@ # django_htmx_examples -A collection of examples on how to do various tasks with HTMX and Django. \ No newline at end of file +A collection of examples on how to do various tasks with HTMX and Django. + +## Setup + +There is currently only one supported way to work with this repository. You +will need a Linux system (WSL might work) onto wich you install the Nix package +manager with Flakes enabled[^1] and direnv[^3]. Afterwards you can enter the +development environment with `direnv allow`. + +[^1]: https://nixos.org/download.html +[^3]: https://direnv.net/ + +After you've entered the development environment with either method you can +start the development server with `dev run`. This will start a PostgreSQL +database running and start the Django development server. + +In case you want a fresh start or remove the project you can just remove the +`.direnv` and `.venv` directory at the root of the project. All the data of the +PostgreSQL database is stored there together with the symlinks to the Nix +store. + +In case you want to tweak something these are the applications use do build the +development environment: + +- Nix package manager +- direnv +- overmind[^4] + +The `dev` command is a simple BASH script called `dev.sh` at the root of the +project. + +[^4]: https://github.com/DarthSim/overmind + +Run the `dev` command without an argument to see all options. + +**Manual way** + +The manual way you have to install poetry[^2] and then run `poetry shell` to +enter the virtual environment. You will then need a local PostgreSQL server or +modify the settings so that you can use your prefered database. + +Please note that I will only use and test the first method. + +[^2]: https://python-poetry.org diff --git a/dev.sh b/dev.sh new file mode 100755 index 0000000..15fc64f --- /dev/null +++ b/dev.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash + +# Helper functions not exposed to the user { + +# Setup the database +_setup () { + overmind start -l db -D + sleep 2 + if [ -f .direnv/first_run ]; then + python ./src/manage.py collectstatic --noinput + python ./src/manage.py makemigrations + python ./src/manage.py migrate + else + python ./src/manage.py collectstatic --noinput + python ./src/manage.py makemigrations + python ./src/manage.py migrate + python ./src/manage.py shell -c "from django.contrib.auth import get_user_model; User = get_user_model(); User.objects.create_superuser('admin', 'admin@example.com', 'password')" + touch .direnv/first_run + fi + overmind quit + sleep 2 +} +#} + +# Main tasks start +declare -A tasks +declare -A descriptions + +run () { + _setup + printf "\n---\n webserver: http://$(hostname -f):$WEBPORT\n---\n" + overmind start -D +} +descriptions["run"]="Start the webserver." +tasks["run"]=run +descriptions["start"]="Alias for run." +tasks["start"]=run + +stop () { + overmind quit +} +descriptions["stop"]="Stop the webserver and DB." +tasks["stop"]=stop + +clean () { + find . \( -name __pycache__ -o -name "*.pyc" \) -delete + rm -f .direnv/first_run + rm -f src/*/migrations/0*.py + rm -rf .direnv/postgres/ + rm -rf .venv/ +} +descriptions["clean"]="Reset the project to a fresh state including the database." +tasks["clean"]=clean + + +cleanall () { + git clean -xdf +} +descriptions["cleanall"]="Completly remove any files which are not checked into git." +tasks["cleanall"]=cleanall + +update (){ + poetry update --lock +} +descriptions["update"]="Update the dependencies." +tasks["update"]=update + +# only one task at a time +if [ $# != 1 ]; then + printf "usage: dev \n\n" + for task in "${!tasks[@]}" + do + echo "$task - ${descriptions[$task]}" + done + +else + # Check if task is available + if [[ -v "tasks[$1]" ]] ; then + ${tasks["$1"]} + else + echo "Task not found." + fi +fi diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..33e17d1 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1690031011, + "narHash": "sha256-kzK0P4Smt7CL53YCdZCBbt9uBFFhE0iNvCki20etAf4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "12303c652b881435065a98729eb7278313041e49", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..1eca9f5 --- /dev/null +++ b/flake.nix @@ -0,0 +1,31 @@ +{ + description = ""; + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + outputs = { self, nixpkgs, flake-utils }: (flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { + inherit system; + }; + in + rec { + devShells.default = pkgs.mkShell { + buildInputs = [ + pkgs.poetry + pkgs.python311 + pkgs.python311Packages.pip + pkgs.overmind + pkgs.postgresql_15 + (pkgs.writeScriptBin "dev" "${builtins.readFile ./dev.sh}") + ]; + PYTHON_KEYRING_BACKEND = "keyring.backends.fail.Keyring"; + LD_LIBRARY_PATH = "${pkgs.stdenv.cc.cc.lib}/lib"; + shellHook = '' + export DJANGO_SETTINGS_MODULE=network_inventory.settings.local + ''; + }; + })); +} +