2021-12-11 / Bartłomiej Kurek
Django - virtual mail manager (#2 - admin)

W pierwszej części omówiliśmy krótki setup projektu obejmujący środowisko Python oraz repozytorium kodu.
W tej części zajmiemy się wstępną konfiguracją aplikacji Django oraz uruchomieniem panelu administracyjnego.

Repozytorium kodu dostępne jest na platformie GitHub.

Konfiguracja ustawień aplikacji (django settings)

Defaults

Tworzę pakiet "settings":

$ mkdir -v vmapp/vmapp/settings

Następnie przenoszę domyślnie wygenerowany plik settings.py do tego pakietu, jako moduł "defaults.py".

$ mv vmapp/vmapp/settings.py vmapp/vmapp/settings/defaults.py

Konfigurację bazy danych ustawiam na:

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": os.environ.get("SQLITE_DB_PATH", ":memory:")
    }
}

Domyślnie baza danych umieszczona jest w pamięci, zatem uruchomienie aplikacji w innym trybie wymagać będzie poprawnej konfiguracji, którą przekażę za pomocą zmiennych środowiskowych.

Development / Testing

Tworzę pliki vmapp/vmapp/settings/development.py i vmapp/vmapp/settings/testing.py o identycznej zawartości:

import os
from .defaults import *  # noqa: 401


DEBUG = True

Production

Tworzę plik vmapp/vmapp/settings/production.py.

import os
from .defaults import *  # noqa: 401


DEBUG = False
ALLOWED_HOSTS = ["127.0.0.1"]
SECRET_KEY = os.environ.get("SECRET_KEY", "change-this")

W środowisku produkcyjnym DEBUG ustawiam na False, a zmienna SQLITE_DB_PATH będzie zawsze musiała zostać podana przy uruchomieniu aplikacji. Dodatkowo ustawiam ALLOWED_HOSTS, w tej chwili na localhost.

Próba standardowego uruchomienia aplikacji zakończy się w tej chwili niepowodzeniem, gdyż Django w defaults.py ma ustawione DEBUG = False, a ALLOWED_HOSTS na pustą listę.

$ python vmapp/manage.py runserver
CommandError: You must set settings.ALLOWED_HOSTS if DEBUG is False.

Konfiguracja środowisk (.env/*)

Tworzę katalog .env, w którym umieszczę pliki ze zmiennymi środowiskowymi poszczególnych konfiguracji.

$ mkdir .env

W każdym z tych plików umieszczam odpowiednią konfigurację (DJANGO_SETTINGS_MODULE, SQLITE_DB_PATH).

.env/development

DJANGO_SETTINGS_MODULE='vmapp.settings.development'
SQLITE_DB_PATH='.ignore/development.sqlite3'

.env/testing

DJANGO_SETTINGS_MODULE='vmapp.settings.testing'
SQLITE_DB_PATH=':memory:'

.env/production

DJANGO_SETTINGS_MODULE='vmapp.settings.production'
SQLITE_DB_PATH='/srv/vmapp/db/vmapp.db'

Temat zmiennych środowiskowych i ich bezpieczeństwa w systemach operacyjnych, dockerze (compose, secrets w docker swarm, itd.) zostawię na inną okazję.

Skrypt uruchamiający aplikację

Tworzę teraz prosty skrypt shell, którego używał będę do uruchamiania aplikacji z ustawieniami żądanego środowiska.
Skrypt znajduje się w głównym katalogu projektu, ładuje dane środowisko, a następnie uruchamia skrypt manage.py z Django.

#!/bin/sh

set -e
set -u

ENV=${1:-development}
test $# -gt 0 && shift || true 

. .env/$ENV
DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE} \
SQLITE_DB_PATH=${SQLITE_DB_PATH} \
    python3 vmapp/manage.py $@

Nadaję mu uprawnienia do wykonywania:

$ chmod a+x manage

Uruchomienie aplikacji

Skrypt manage domyślnie ustawia środowisko development. Jako pierwszy (w tej chwili jedyny) argument możemy przekazać nazwę środowiska.

$ ./manage
$ ./manage development
$ ./manage testing
$ ./manage production

Pozostałe argumenty trafiają bezpośrednio do skryptu manage.py z django.

Do uruchomienia aplikacji w trybie development muszę jeszcze utworzyć katalog ".ignore", w którym ma się znaleźć plik bazy danych (wskazany zmienną środowiskowa SQLITE_DB_PATH):

$ mkdir .ignore/

Wykonuję teraz migrację:

$ ./manage development migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK

Tworzę konto administratora:

$ ./manage development createsuperuser --username admin --email admin@localhost 
Password: 
Password (again): 
Superuser created successfully.

Uruchamiam serwer deweloperski:

$ ./manage development runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
December 11, 2021 - 11:21:43
Django version 4.0, using settings 'vmapp.settings.development'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

I loguję się do panelu administracyjnego:

$ firefox http://127.0.0.1:8000/admin/

img-border

W następnej części zajmiemy się tworzeniem modeli dla systemu pocztowego oraz testami jednostkowymi.