開発と本番用に環境を最適化する:Pydanticチュートリアル、パート2

公開: 2022-07-22

開発者は彼ら自身の最悪の敵になることができます。 実稼働環境に適合しないシステムで開発しているエンジニアの例は数え切れないほどあります。 この不協和音は余分な作業につながり、開発プロセスの後半までシステムエラーをキャッチしません。 これらの設定を調整すると、最終的には継続的な展開が容易になります。 これを念頭に置いて、Django開発環境でサンプルアプリケーションを作成し、Docker、pydantic、およびcondaを使用して簡略化します。

一般的な開発環境では、次のものを使用します。

  • ローカルリポジトリ。
  • DockerベースのPostgreSQLデータベース。 と
  • conda環境(Pythonの依存関係を管理するため)。

PydanticとDjangoは、単純なプロジェクトと複雑なプロジェクトの両方に適しています。 次の手順は、環境をミラーリングする方法を強調する簡単なソリューションを示しています。

Gitリポジトリの構成

コードの記述や開発システムのインストールを開始する前に、ローカルのGitリポジトリを作成しましょう。

 mkdir hello-visitor cd hello-visitor git init

リポジトリルートにある基本的な.gitignoreファイルから始めます。 このチュートリアル全体を通して、Gitに追跡させたくないファイルを追加する前に、このファイルに追加します。

Dockerを使用したDjangoPostgreSQLの構成

Djangoにはリレーショナルデータベースが必要であり、デフォルトではSQLiteを使用します。 SQLiteは同時ユーザーアクセスを適切に処理しないため、通常、ミッションクリティカルなデータストレージにはSQLiteを使用しません。 ほとんどの開発者は、PostgreSQLなどのより一般的な本番データベースを選択します。 とにかく、開発と本番には同じデータベースを使用する必要があります。 このアーキテクチャ上の義務は、12要素アプリの一部です。

幸い、DockerとDockerComposeを使用してローカルPostgreSQLインスタンスを操作するのは簡単です。

ルートディレクトリの汚染を避けるために、Docker関連のファイルを別々のサブディレクトリに配置します。 まず、PostgreSQLをデプロイするためのDockerComposeファイルを作成します。

 # docker-services/docker-compose.yml version: "3.9" services: db: image: "postgres:13.4" env_file: .env volumes: - hello-visitor-postgres:/var/lib/postgresql/data ports: - ${POSTGRES_PORT}:5432 volumes: hello-visitor-postgres:

次に、PostgreSQLコンテナを構成するためのdocker-compose環境ファイルを作成します。

 # docker-services/.env POSTGRES_USER=postgres POSTGRES_PASSWORD=MyDBPassword123 # The 'maintenance' database POSTGRES_DB=postgres # The port exposed to localhost POSTGRES_PORT=5432

これで、データベースサーバーが定義および構成されました。 コンテナをバックグラウンドで開始しましょう:

 sudo docker compose --project-directory docker-services/ up -d

前のコマンドでのsudoの使用に注意することが重要です。 開発環境で特定の手順を実行しない限り、これが必要になります。

データベースの作成

標準のツールスイートpgAdmin4を使用してPostgreSQLに接続して構成しましょう。 以前に環境変数で構成したものと同じログイン資格情報を使用します。

それでは、 hello_visitorという名前の新しいデータベースを作成しましょう。

[データベースの作成]ダイアログの[全般]タブを表示するブラウザー内のpgAdmin4画面。データベースのテキストフィールドには値hello_visitorが含まれ、所有者フィールドにはpostgresユーザーが表示され、コメントフィールドは空白です。

データベースを配置したら、プログラミング環境をインストールする準備が整います。

MinicondaによるPython環境管理

次に、分離されたPython環境と必要な依存関係を設定する必要があります。 セットアップとメンテナンスを簡単にするために、Minicondaを選択しました。

conda環境を作成してアクティブ化しましょう:

 conda create --name hello-visitor python=3.9 conda activate hello-visitor

次に、Pythonの依存関係を列挙するファイルhello-visitor/requirements.txtを作成します。

 django # PostgreSQL database adapter: psycopg2 # Pushes .env key-value pairs into environment variables: python-dotenv pydantic # Utility library to read database connection information: dj-database-url # Static file caching: whitenoise # Python WSGI HTTP Server: gunicorn

次に、Pythonにこれらの依存関係をインストールするように依頼します。

 cd hello-visitor pip install -r requirements.txt

これで、アプリケーション開発作業の準備として、依存関係をインストールする必要があります。

Django足場

最初にdjango-adminを実行し、次に生成されたファイルmanage.pyを実行して、プロジェクトとアプリの足場を作ります。

 # From the `hello-visitor` directory mkdir src cd src # Generate starter code for our Django project. django-admin startproject hello_visitor . # Generate starter code for our Django app. python manage.py startapp homepage

次に、プロジェクトをロードするようにDjangoを構成する必要があります。 settings.pyファイルでは、新しく作成したhomepageアプリを登録するために、 INSTALLED_APPS配列を調整する必要があります。

 # src/hello_visitor/settings.py # ... INSTALLED_APPS = [ "homepage.apps.HomepageConfig", "django.contrib.admin", # ... ] # ...

アプリケーション設定構成

最初の記事で示したpydanticおよびDjango設定アプローチを使用して、開発システム用の環境変数ファイルを作成する必要があります。 次のように、現在の設定をこのファイルに移動します。

  1. 開発環境の設定を保持するファイルsrc/.envを作成します。
  2. src/hello_visitor/settings.pyから設定をコピーし、src/ src/.envに追加します。
  3. それらのコピーされた行をsettings.pyファイルから削除します。
  4. データベース接続文字列が以前に構成したものと同じ資格情報を使用していることを確認してください。

環境ファイルsrc/.envは、次のようになります。

 DATABASE_URL=postgres://postgres:MyDBPassword123@localhost:5432/hello_visitor DATABASE_SSL=False SECRET_KEY="django-insecure-sackl&7(1hc3+%#*4e=)^q3qiw!hnnui*-^($o8t@2^^qqs=%i" DEBUG=True DEBUG_TEMPLATES=True USE_SSL=False ALLOWED_HOSTS='[ "localhost", "127.0.0.1", "0.0.0.0" ]'

次のコードスニペットを使用して、pydanticを使用して環境変数から設定を読み取るようにDjangoを構成します。

 # src/hello_visitor/settings.py import os from pathlib import Path from pydantic import ( BaseSettings, PostgresDsn, EmailStr, HttpUrl, ) import dj_database_url # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent class SettingsFromEnvironment(BaseSettings): """Defines environment variables with their types and optional defaults""" # PostgreSQL DATABASE_URL: PostgresDsn DATABASE_SSL: bool = True # Django SECRET_KEY: str DEBUG: bool = False DEBUG_TEMPLATES: bool = False USE_SSL: bool = False ALLOWED_HOSTS: list class Config: """Defines configuration for pydantic environment loading""" env_file = str(BASE_DIR / ".env") case_sensitive = True config = SettingsFromEnvironment() os.environ["DATABASE_URL"] = config.DATABASE_URL DATABASES = { "default": dj_database_url.config(conn_max_age=600, ssl_require=config.DATABASE_SSL) } SECRET_KEY = config.SECRET_KEY DEBUG = config.DEBUG DEBUG_TEMPLATES = config.DEBUG_TEMPLATES USE_SSL = config.USE_SSL ALLOWED_HOSTS = config.ALLOWED_HOSTS # ...

以前の編集を完了した後に問題が発生した場合は、細工したsettings.pyファイルをソースコードリポジトリのバージョンと比較してください。

モデルの作成

私たちのアプリケーションは、ホームページの訪問者数を追跡して表示します。 そのカウントを保持するモデルが必要です。次に、Djangoのオブジェクトリレーショナルマッパー(ORM)を使用して、データ移行を介して単一のデータベース行を初期化します。

まず、 VisitCounterモデルを作成します。

 # hello-visitor/src/homepage/models.py """Defines the models""" from django.db import models class VisitCounter(models.Model): """ORM for VisitCounter""" count = models.IntegerField() @staticmethod def insert_visit_counter(): """Populates database with one visit counter. Call from a data migration.""" visit_counter = VisitCounter(count=0) visit_counter.save() def __str__(self): return f"VisitCounter - number of visits: {self.count}"

次に、移行をトリガーしてデータベーステーブルを作成します。

 # in the `src` folder python manage.py makemigrations python manage.py migrate

homepage_visitcounterテーブルが存在することを確認するために、pgAdmin4でデータベースを表示できます。

次に、 homepage_visitcounterテーブルに初期値を入力する必要があります。 Djangoスキャフォールディングを使用してこれを実現するために、別の移行ファイルを作成しましょう。

 # from the 'src' directory python manage.py makemigrations --empty homepage

このセクションの冒頭で定義したVisitCounter.insert_visit_counterメソッドを使用するように、作成した移行ファイルを調整します。

 # src/homepage/migrations/0002_auto_-------_----.py # Note: The dashes are dependent on execution time. from django.db import migrations from ..models import VisitCounter def insert_default_items(apps, _schema_editor): """Populates database with one visit counter.""" # To learn about apps, see: # https://docs.djangoproject.com/en/3.2/topics/migrations/#data-migrations VisitCounter.insert_visit_counter() class Migration(migrations.Migration): """Runs a data migration.""" dependencies = [ ("homepage", "0001_initial"), ] operations = [ migrations.RunPython(insert_default_items), ]

これで、 homepageアプリに対してこの変更された移行を実行する準備が整いました。

 # from the 'src' directory python manage.py migrate homepage

テーブルの内容を見て、移行が正しく実行されたことを確認しましょう。

クエリ「SELECT*FROM public.homepage_visitcounter ORDERBYidASC」を表示するブラウザ内のpgAdmin4画面。 [データ出力]タブには、そのテーブル内に1つの行があることが示されています。サロゲートキーIDフィールドの値は1で、カウントフィールドの値は0です。

私たちのhomepage_visitcounterテーブルが存在し、最初の訪問数が0であることがわかります。データベースを二乗したので、UIの作成に焦点を合わせます。

ビューを作成して構成する

UIの2つの主要部分であるビューとテンプレートを実装する必要があります。

homepageビューを作成して、訪問者数を増やし、データベースに保存し、その数をテンプレートに渡して表示します。

 # src/homepage/views.py from django.shortcuts import get_object_or_404, render from .models import VisitCounter def index(request): """View for the main page of the app.""" visit_counter = get_object_or_404(VisitCounter, pk=1) visit_counter.count += 1 visit_counter.save() context = {"visit_counter": visit_counter} return render(request, "homepage/index.html", context)

私たちのDjangoアプリケーションは、 homepageを対象としたリクエストをリッスンする必要があります。 この設定を構成するには、次のファイルを追加します。

 # src/homepage/urls.py """Defines urls""" from django.urls import path from . import views # The namespace of the apps' URLconf app_name = "homepage" # pylint: disable=invalid-name urlpatterns = [ path("", views.index, name="index"), ]

homepageアプリケーションを提供するには、別のurls.pyファイルに登録する必要があります。

 # src/hello_visitor/urls.py from django.contrib import admin from django.urls import include, path urlpatterns = [ path("", include("homepage.urls")), path("admin/", admin.site.urls), ]

私たちのプロジェクトのベースHTMLテンプレートは、新しいファイルsrc/templates/layouts/base.htmlます。

 <!DOCTYPE html> {% load static %} <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Bootstrap CSS --> <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous"> <title>Hello, visitor!</title> <link rel="shortcut icon" type="image/png" href="{% static 'favicon.ico' %}"/> </head> <body> {% block main %}{% endblock %} <!-- Option 1: Bootstrap Bundle with Popper --> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4" crossorigin="anonymous"></script> </body> </html>

homepageアプリの基本テンプレートを新しいファイルsrc/templates/homepage/index.htmlに拡張します。

 {% extends "layouts/base.html" %} {% block main %} <main> <div class="container py-4"> <div class="p-5 mb-4 bg-dark text-white text-center rounded-3"> <div class="container-fluid py-5"> <h1 class="display-5 fw-bold">Hello, visitor {{ visit_counter.count }}!</h1> </div> </div> </div> </main> {% endblock %}

UIを作成する最後のステップは、これらのテンプレートの場所をDjangoに指示することです。 TEMPLATES['DIRS']ディクショナリアイテムをsettings.pyファイルに追加しましょう。

 # src/hello_visitor/settings.py TEMPLATES = [ { ... 'DIRS': [BASE_DIR / 'templates'], ... }, ]

これでユーザーインターフェイスが実装され、アプリケーションの機能をテストする準備がほぼ整いました。 テストを行う前に、環境の最後の部分である静的コンテンツキャッシュを配置する必要があります。

静的コンテンツ構成

開発システムでアーキテクチャのショートカットを使用しないようにするために、本番環境をミラーリングするように静的コンテンツキャッシュを構成します。

プロジェクトのすべての静的ファイルを単一のディレクトリsrc/staticに保持し、デプロイ前にそれらのファイルを収集するようにDjangoに指示します。

アプリケーションのfaviconにToptalのロゴを使用し、 src/static/favicon.icoとして保存します。

 # from `src` folder mkdir static cd static wget https://frontier-assets.toptal.com/83b2f6e0d02cdb3d951a75bd07ee4058.png mv 83b2f6e0d02cdb3d951a75bd07ee4058.png favicon.ico

次に、静的ファイルを収集するようにDjangoを構成します。

 # src/hello_visitor/settings.py # Static files (CSS, JavaScript, images) # a la https://docs.djangoproject.com/en/3.2/howto/static-files/ # # Source location where we'll store our static files STATICFILES_DIRS = [BASE_DIR / "static"] # Build output location where Django collects all static files STATIC_ROOT = BASE_DIR / "staticfiles" STATIC_ROOT.mkdir(exist_ok=True) # URL to use when referring to static files located in STATIC_ROOT. STATIC_URL = "/static/" STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"

元の静的ファイルをソースコードリポジトリに保存するだけです。 本番環境に最適化されたバージョンを保存する必要はありません。 この単純な行を使用して、後者を.gitignoreに追加しましょう。

 staticfiles

ソースコードリポジトリに必要なファイルが正しく保存されているので、これらの静的ファイルを処理するようにキャッシュシステムを構成する必要があります。

静的ファイルキャッシング

本番環境、つまり開発環境でも、WhiteNoiseを使用して、Djangoアプリケーションの静的ファイルをより効率的に提供します。

次のスニペットをsrc/hello_visitor/settings.pyファイルに追加して、WhiteNoiseをミドルウェアとして登録します。 登録順序は厳密に定義されており、 WhiteNoiseMiddlewareSecurityMiddlewareの直後に表示される必要があります。

 MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', # ... ] STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

これで、静的ファイルキャッシュが開発環境で構成され、アプリケーションを実行できるようになります。

開発サーバーの実行

完全にコード化されたアプリケーションがあり、次のコマンドでDjangoの組み込み開発Webサーバーを起動できます。

 # in the `src` folder python manage.py runserver

http://localhost:8000に移動すると、ページを更新するたびにカウントが増加します。

pydantic Djangoアプリケーションのメイン画面を表示するブラウザウィンドウ。「こんにちは、ビジター!」と表示されます。 1行に「1」、次の行に「1」。

これで、ページを更新すると訪問数が増えるアプリケーションが機能するようになりました。

展開する準備ができました

このチュートリアルでは、本番環境に一致する美しいDjango開発環境で動作するアプリを作成するために必要なすべての手順について説明しました。 パート3では、アプリケーションを本番環境にデプロイする方法について説明します。 Djangoとpydanticの利点を強調する追加の演習も検討する価値があります。これらは、このpydanticチュートリアルのコード完全リポジトリに含まれています。


Toptal Engineering Blogは、この記事に示されているコードサンプルのレビューとベータテストを行ってくれたStephenDavidsonに感謝の意を表します。