diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..74bbcaa --- /dev/null +++ b/Dockerfile @@ -0,0 +1,42 @@ +ARG PYTHON_VERSION=3.11.6-slim-bookworm + +# define an alias for the specfic python version used in this file. +FROM python:${PYTHON_VERSION} as python + +FROM python as python-build-stage + +# Install apt packages +RUN apt-get update && apt-get install --no-install-recommends -y \ + # dependencies for building Python packages + build-essential + +# Requirements are installed here to ensure they will be cached. +COPY ./requirements.txt / + +# Create Python Dependency and Sub-Dependency Wheels +RUN pip wheel --wheel-dir /usr/src/app/wheels -r requirements.txt + +FROM python as python-run-stage + +ARG APP_HOME=/app + +ENV PYTHONUNBUFFERED 1 +ENV PYTHONDONTWRITEBYTECODE 1 + +WORKDIR ${APP_HOME} + +COPY --from=python-build-stage /usr/src/app/wheels /wheels/ + +# use wheels to install python dependencies +RUN pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* \ + && rm -rf /wheels/ + +COPY ./entrypoint /entrypoint +RUN sed -i 's/\r$//g' /entrypoint && chmod +x /entrypoint + +FROM python-run-stage AS backend + +# copy application code to WORKDIR +COPY . ${APP_HOME} + +ENTRYPOINT ["/entrypoint"] diff --git a/Pipfile b/Pipfile deleted file mode 100644 index b0f76f6..0000000 --- a/Pipfile +++ /dev/null @@ -1,13 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -django = ">=2.2" -django-ledger = ">=0.5.5.2" - -[dev-packages] - -[requires] -python_version = "3.11" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 017a4e7..0000000 --- a/Pipfile.lock +++ /dev/null @@ -1,168 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "e9e0853cadd1a6268ebc9e7530ad05b72e950f8ac2c0ed4e21e4802264d32721" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.11" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "asgiref": { - "hashes": [ - "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e", - "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed" - ], - "markers": "python_version >= '3.7'", - "version": "==3.7.2" - }, - "django": { - "hashes": [ - "sha256:8e0f1c2c2786b5c0e39fe1afce24c926040fad47c8ea8ad30aaf1188df29fc41", - "sha256:e1d37c51ad26186de355cbcec16613ebdabfa9689bbade9c538835205a8abbe9" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==4.2.7" - }, - "django-ledger": { - "hashes": [ - "sha256:1eb84d2aa41dced070245c7d64b75fd1aa9ed1f6de5cad68c3f9a7dbc25fc137" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==0.5.5.2" - }, - "django-treebeard": { - "hashes": [ - "sha256:787117995ff985d98e6c2b241ef6b9d37fe8ff7051cd7535c283616a0b5b2645", - "sha256:c751a3f924158c288fea89afc25a7151979faf01bf11fdc7be3b858099dfa56d" - ], - "markers": "python_version >= '3.8'", - "version": "==4.7" - }, - "faker": { - "hashes": [ - "sha256:171b27ba106cf69e30a91ac471407c2362bd6af27738e2461dc441aeff5eed91", - "sha256:df44b68b9d231e784f4bfe616d781576cfef9f0c5d9a17671bf84dc10d7b44d6" - ], - "markers": "python_version >= '3.8'", - "version": "==20.0.0" - }, - "markdown": { - "hashes": [ - "sha256:5874b47d4ee3f0b14d764324d2c94c03ea66bee56f2d929da9f2508d65e722dc", - "sha256:b65d7beb248dc22f2e8a31fb706d93798093c308dc1aba295aedeb9d41a813bd" - ], - "markers": "python_version >= '3.8'", - "version": "==3.5.1" - }, - "ofxtools": { - "hashes": [ - "sha256:682a516bfa5ccad0f9551c17cc2cf155422f9f5f85a341cfb4911b324de46045" - ], - "markers": "python_version >= '3.8'", - "version": "==0.9.5" - }, - "pillow": { - "hashes": [ - "sha256:00f438bb841382b15d7deb9a05cc946ee0f2c352653c7aa659e75e592f6fa17d", - "sha256:0248f86b3ea061e67817c47ecbe82c23f9dd5d5226200eb9090b3873d3ca32de", - "sha256:04f6f6149f266a100374ca3cc368b67fb27c4af9f1cc8cb6306d849dcdf12616", - "sha256:062a1610e3bc258bff2328ec43f34244fcec972ee0717200cb1425214fe5b839", - "sha256:0a026c188be3b443916179f5d04548092e253beb0c3e2ee0a4e2cdad72f66099", - "sha256:0f7c276c05a9767e877a0b4c5050c8bee6a6d960d7f0c11ebda6b99746068c2a", - "sha256:1a8413794b4ad9719346cd9306118450b7b00d9a15846451549314a58ac42219", - "sha256:1ab05f3db77e98f93964697c8efc49c7954b08dd61cff526b7f2531a22410106", - "sha256:1c3ac5423c8c1da5928aa12c6e258921956757d976405e9467c5f39d1d577a4b", - "sha256:1c41d960babf951e01a49c9746f92c5a7e0d939d1652d7ba30f6b3090f27e412", - "sha256:1fafabe50a6977ac70dfe829b2d5735fd54e190ab55259ec8aea4aaea412fa0b", - "sha256:1fb29c07478e6c06a46b867e43b0bcdb241b44cc52be9bc25ce5944eed4648e7", - "sha256:24fadc71218ad2b8ffe437b54876c9382b4a29e030a05a9879f615091f42ffc2", - "sha256:2cdc65a46e74514ce742c2013cd4a2d12e8553e3a2563c64879f7c7e4d28bce7", - "sha256:2ef6721c97894a7aa77723740a09547197533146fba8355e86d6d9a4a1056b14", - "sha256:3b834f4b16173e5b92ab6566f0473bfb09f939ba14b23b8da1f54fa63e4b623f", - "sha256:3d929a19f5469b3f4df33a3df2983db070ebb2088a1e145e18facbc28cae5b27", - "sha256:41f67248d92a5e0a2076d3517d8d4b1e41a97e2df10eb8f93106c89107f38b57", - "sha256:47e5bf85b80abc03be7455c95b6d6e4896a62f6541c1f2ce77a7d2bb832af262", - "sha256:4d0152565c6aa6ebbfb1e5d8624140a440f2b99bf7afaafbdbf6430426497f28", - "sha256:50d08cd0a2ecd2a8657bd3d82c71efd5a58edb04d9308185d66c3a5a5bed9610", - "sha256:61f1a9d247317fa08a308daaa8ee7b3f760ab1809ca2da14ecc88ae4257d6172", - "sha256:6932a7652464746fcb484f7fc3618e6503d2066d853f68a4bd97193a3996e273", - "sha256:7a7e3daa202beb61821c06d2517428e8e7c1aab08943e92ec9e5755c2fc9ba5e", - "sha256:7dbaa3c7de82ef37e7708521be41db5565004258ca76945ad74a8e998c30af8d", - "sha256:7df5608bc38bd37ef585ae9c38c9cd46d7c81498f086915b0f97255ea60c2818", - "sha256:806abdd8249ba3953c33742506fe414880bad78ac25cc9a9b1c6ae97bedd573f", - "sha256:883f216eac8712b83a63f41b76ddfb7b2afab1b74abbb413c5df6680f071a6b9", - "sha256:912e3812a1dbbc834da2b32299b124b5ddcb664ed354916fd1ed6f193f0e2d01", - "sha256:937bdc5a7f5343d1c97dc98149a0be7eb9704e937fe3dc7140e229ae4fc572a7", - "sha256:9882a7451c680c12f232a422730f986a1fcd808da0fd428f08b671237237d651", - "sha256:9a92109192b360634a4489c0c756364c0c3a2992906752165ecb50544c251312", - "sha256:9d7bc666bd8c5a4225e7ac71f2f9d12466ec555e89092728ea0f5c0c2422ea80", - "sha256:a5f63b5a68daedc54c7c3464508d8c12075e56dcfbd42f8c1bf40169061ae666", - "sha256:a646e48de237d860c36e0db37ecaecaa3619e6f3e9d5319e527ccbc8151df061", - "sha256:a89b8312d51715b510a4fe9fc13686283f376cfd5abca8cd1c65e4c76e21081b", - "sha256:a92386125e9ee90381c3369f57a2a50fa9e6aa8b1cf1d9c4b200d41a7dd8e992", - "sha256:ae88931f93214777c7a3aa0a8f92a683f83ecde27f65a45f95f22d289a69e593", - "sha256:afc8eef765d948543a4775f00b7b8c079b3321d6b675dde0d02afa2ee23000b4", - "sha256:b0eb01ca85b2361b09480784a7931fc648ed8b7836f01fb9241141b968feb1db", - "sha256:b1c25762197144e211efb5f4e8ad656f36c8d214d390585d1d21281f46d556ba", - "sha256:b4005fee46ed9be0b8fb42be0c20e79411533d1fd58edabebc0dd24626882cfd", - "sha256:b920e4d028f6442bea9a75b7491c063f0b9a3972520731ed26c83e254302eb1e", - "sha256:baada14941c83079bf84c037e2d8b7506ce201e92e3d2fa0d1303507a8538212", - "sha256:bb40c011447712d2e19cc261c82655f75f32cb724788df315ed992a4d65696bb", - "sha256:c0949b55eb607898e28eaccb525ab104b2d86542a85c74baf3a6dc24002edec2", - "sha256:c9aeea7b63edb7884b031a35305629a7593272b54f429a9869a4f63a1bf04c34", - "sha256:cfe96560c6ce2f4c07d6647af2d0f3c54cc33289894ebd88cfbb3bcd5391e256", - "sha256:d27b5997bdd2eb9fb199982bb7eb6164db0426904020dc38c10203187ae2ff2f", - "sha256:d921bc90b1defa55c9917ca6b6b71430e4286fc9e44c55ead78ca1a9f9eba5f2", - "sha256:e6bf8de6c36ed96c86ea3b6e1d5273c53f46ef518a062464cd7ef5dd2cf92e38", - "sha256:eaed6977fa73408b7b8a24e8b14e59e1668cfc0f4c40193ea7ced8e210adf996", - "sha256:fa1d323703cfdac2036af05191b969b910d8f115cf53093125e4058f62012c9a", - "sha256:fe1e26e1ffc38be097f0ba1d0d07fcade2bcfd1d023cda5b29935ae8052bd793" - ], - "markers": "python_version >= '3.8'", - "version": "==10.1.0" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "sqlparse": { - "hashes": [ - "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3", - "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c" - ], - "markers": "python_version >= '3.5'", - "version": "==0.4.4" - }, - "text-unidecode": { - "hashes": [ - "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", - "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93" - ], - "version": "==1.3" - } - }, - "develop": {} -} diff --git a/README.md b/README.md index 2bafce9..2cd1c5f 100644 --- a/README.md +++ b/README.md @@ -4,32 +4,37 @@ # Instructions Clone the django starter template & CD into it + ```shell git clone https://github.com/arrobalytics/django-ledger-starter.git && cd django-ledger-starter ``` -If pipenv is not installed on your system you may install it -```shell -pip install pipenv -``` +If docker and docker-compose are not installed on your system install them, for example on Debian/Ubuntu: ```shell -pipenv install && pipenv shell +sudo apt install docker.io docker-compose ``` -Run migrations -```shell -python manage.py migrate -``` +Start the services: -Create Django super user and follow the prompts ```shell -python manage.py createsuperuser +docker-compose up ``` -Run te server +Then navigate to: + +- http://localhost:8000/ledger for django-ledger (login as `root` with password `root`) + +- http://localhost:8090/?pgsql=postgres&username=ledger&db=ledger&ns=public (password: `ledger`) to browse the DB with [Adminer](https://www.adminer.org). + +You can open a shell into the django container with: + ```shell -python manage.py runserver +docker-compose exec django bash ``` -Navigate to http://127.0.0.1:8000/ledger \ No newline at end of file +or even execute Django admin commands with: + +```shell +docker-compose exec django python3 manage.py check +``` \ No newline at end of file diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..8492fa9 --- /dev/null +++ b/compose.yml @@ -0,0 +1,43 @@ +--- + +version: '3.7' + +services: + django: + build: + context: . + dockerfile: ./Dockerfile + image: django_ledger_django + container_name: django_ledger_django + restart: unless-stopped + ports: + - "8000:8000" + init: true + depends_on: + - postgres + environment: + - DATABASE_ENGINE=django.db.backends.postgresql + - DATABASE_HOST=postgres + - DATABASE_NAME=ledger + - DATABASE_USER=ledger + - DATABASE_PASSWORD=ledger + volumes: + - .:/app + postgres: + image: postgres:16.1-bookworm + container_name: django_ledger_postgres + restart: unless-stopped + environment: + - POSTGRES_DB=ledger + - POSTGRES_USER=ledger + - POSTGRES_PASSWORD=ledger + adminer: + image: adminer + container_name: weboll_local_adminer + restart: always + ports: + - 8090:8080 + links: + - postgres + depends_on: + - postgres diff --git a/django_ledger_starter/settings.py b/django_ledger_starter/settings.py index 5f1702a..021adba 100644 --- a/django_ledger_starter/settings.py +++ b/django_ledger_starter/settings.py @@ -10,8 +10,13 @@ https://docs.djangoproject.com/en/4.2/ref/settings/ """ +import environ + from pathlib import Path +env = environ.Env() + + # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -77,12 +82,18 @@ # https://docs.djangoproject.com/en/4.2/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', + "default": { + "ENGINE": env("DATABASE_ENGINE"), + "NAME": env("DATABASE_NAME"), + "USER": env("DATABASE_USER"), + "PASSWORD": env("DATABASE_PASSWORD"), + "TEST": {"NAME": env("DATABASE_NAME")}, } } - +if "DATABASE_HOST" in env: + DATABASES["default"]["HOST"] = env("DATABASE_HOST") +if "DATABASE_PORT" in env: + DATABASES["default"]["PORT"] = env("DATABASE_PORT") # Password validation # https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators diff --git a/entrypoint b/entrypoint new file mode 100644 index 0000000..aff4f7f --- /dev/null +++ b/entrypoint @@ -0,0 +1,16 @@ +#!/bin/bash + +set -o errexit +set -o pipefail +set -o nounset + +python3 manage.py migrate --noinput +python3 manage.py shell << END +from django.contrib.auth import get_user_model +UserModel = get_user_model() +if UserModel.objects.filter(is_superuser=True).count() == 0: + UserModel.objects.create_superuser('root', 'django-ledger@example.com', 'root') +END + +>&2 true +python3 manage.py runserver 0:8000 diff --git a/requirements.txt b/requirements.txt index faa97a7..74e18e8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,4 @@ Django~=4.2.7 -django-ledger>=0.5.5.2 \ No newline at end of file +django-ledger>=0.5.5.2 +django-environ>=0.11.2 +psycopg[binary]>=3.1.14 \ No newline at end of file