Merampingkan Pengaturan Django Anda Dengan Petunjuk Jenis: Tutorial Pydantic

Diterbitkan: 2022-07-22

Proyek Django dulu membuat saya frustrasi karena saya tidak memiliki cara yang kuat dan skalabel untuk menambahkan lingkungan baru. Dengan menyatukan petunjuk tipe pydantic dan Python, saya membangun fondasi kuat yang saya butuhkan.

Seperti yang dijelaskan dalam PEP 484, petunjuk jenis mendukung analisis statis, tetapi anotasi yang sama ini juga tersedia saat runtime. Paket pihak ketiga seperti pydantic menawarkan pemeriksaan jenis runtime yang menggunakan metadata tambahan ini. Pydantic menggunakan petunjuk jenis Python untuk membantu mengelola metadata pengaturan dan melakukan validasi data runtime.

Tutorial pydantic ini akan menunjukkan jangkauan luas, efek positif dari penggunaan manajemen pengaturan pydantic dengan Django.

Konfigurasi kami mematuhi praktik terbaik yang dijelaskan di situs web Aplikasi Dua Belas Faktor:

  1. Tentukan konfigurasi nonkonstan dan rahasia sebagai variabel lingkungan.
  2. Di lingkungan pengembangan, tentukan variabel lingkungan dalam file .env dan tambahkan .env ke .gitignore .
  3. Gunakan mekanisme penyedia cloud untuk menentukan variabel lingkungan (rahasia) untuk lingkungan QA, staging, dan produksi.
  4. Gunakan satu file settings.py yang mengonfigurasi dirinya sendiri dari variabel lingkungan.
  5. Gunakan pydantic untuk membaca, memeriksa, memvalidasi, dan mengetik variabel lingkungan ke variabel Python yang mendefinisikan konfigurasi Django.

Atau, beberapa pengembang membuat beberapa file pengaturan, seperti settings_dev.py dan settings_prod.py . Sayangnya, pendekatan ini tidak berskala dengan baik. Ini mengarah pada duplikasi kode, kebingungan, bug yang sulit ditemukan, dan upaya pemeliharaan yang lebih tinggi.

Menggunakan praktik terbaik yang disebutkan di atas, menambahkan sejumlah lingkungan mudah, terdefinisi dengan baik, dan tahan kesalahan. Meskipun kami dapat menjelajahi konfigurasi lingkungan yang lebih rumit, kami akan fokus pada dua untuk kejelasan: pengembangan dan produksi.

Seperti apa ini dalam praktiknya?

Manajemen Pengaturan Pydantic dan Variabel Lingkungan

Kami sekarang fokus pada contoh dalam pengembangan dan produksi. Kami menunjukkan bagaimana setiap lingkungan mengonfigurasi pengaturannya secara berbeda dan bagaimana pydantic mendukung masing-masing.

Aplikasi contoh kita memerlukan database yang didukung Django, jadi kita perlu menyimpan string koneksi database. Kami memindahkan informasi konfigurasi koneksi database ke dalam variabel lingkungan, DATABASE_URL , menggunakan paket Python dj-database-url. Harap dicatat bahwa variabel ini bertipe str dan diformat sebagai berikut:

 postgres://{user}:{password}@{hostname}:{port}/{database-name} mysql://{user}:{password}@{hostname}:{port}/{database-name} oracle://{user}:{password}@{hostname}:{port}/{database-name} sqlite:///PATH

Di lingkungan pengembangan kami, kami dapat menggunakan instance PostgreSQL yang berisi Docker untuk kemudahan penggunaan, sementara di lingkungan produksi kami, kami akan menunjuk ke layanan basis data yang disediakan.

Variabel lain yang ingin kita definisikan adalah boolean, DEBUG . Bendera DEBUG di Django tidak boleh dihidupkan dalam penyebaran produksi. Hal ini dimaksudkan untuk mendapatkan umpan balik tambahan selama pengembangan. Misalnya, dalam mode debug, Django akan menampilkan halaman kesalahan terinci saat pengecualian terjadi.

Nilai yang berbeda untuk pengembangan dan produksi dapat didefinisikan sebagai berikut:

Nama Variabel Perkembangan Produksi
DATABASE_URL postgres://postgres:mypw@localhost:5432/mydb postgres://foo1:foo2@foo3:5432/foo4
DEBUG True False

Kami menggunakan modul manajemen pengaturan pydantic untuk mengelola kumpulan nilai variabel lingkungan yang berbeda ini tergantung pada lingkungan.

Langkah-Langkah Persiapan

Untuk mempraktikkannya, kami mulai mengonfigurasi lingkungan pengembangan kami dengan membuat file .env tunggal kami dengan konten ini:

 DATABASE_URL=postgres://postgres:mypw@localhost:5432/mydb DEBUG=True

Selanjutnya, kita tambahkan file .env ke file .gitignore proyek. File .gitignore menghindari penyimpanan informasi yang berpotensi sensitif di kontrol sumber.

Sementara pendekatan ini bekerja dengan baik di lingkungan pengembangan kami, spesifikasi lingkungan produksi kami menggunakan mekanisme yang berbeda. Praktik terbaik kami menentukan bahwa variabel lingkungan produksi menggunakan rahasia lingkungan. Misalnya, di Heroku, rahasia ini disebut Config Vars dan dikonfigurasi melalui Dasbor Heroku. Mereka tersedia untuk aplikasi yang digunakan sebagai variabel lingkungan:

Tangkapan layar antarmuka web Config Vars. Bilah sisi kiri memiliki deskripsi: "Konfigurasi vars mengubah cara aplikasi Anda berperilaku. Selain membuat sendiri, beberapa pengaya dilengkapi dengan pengaya sendiri." Bagian utama memiliki dua baris, masing-masing dengan dua bidang teks, ikon pensil, dan ikon X. Bidang teks memiliki data yang sama dengan kolom "Nama Variabel" dan "Produksi" di tabel sebelumnya. Sudut kanan atas memiliki tombol yang bertuliskan, "Sembunyikan Config Vars."

Setelah itu, kita perlu menyesuaikan konfigurasi aplikasi untuk membaca nilai-nilai ini dari kedua lingkungan secara otomatis.

Mengonfigurasi settings.py Django

Mari kita mulai dengan proyek Django baru untuk menyediakan struktur penting untuk contoh kita. Kami membuat perancah proyek Django baru dengan perintah terminal berikut:

 $ django-admin startproject mysite

Kami sekarang memiliki struktur proyek mysite untuk proyek situs saya. Struktur file proyek adalah sebagai berikut:

 mysite/ manage.py mysite/ __init__.py settings.py urls.py asgi.py wsgi.py

File settings.py berisi kode boilerplate yang memungkinkan kita mengelola konfigurasi aplikasi. Ini memiliki banyak pengaturan default yang telah ditentukan yang harus kita sesuaikan dengan lingkungan yang relevan.

Untuk mengelola pengaturan aplikasi ini menggunakan variabel lingkungan dan pydantic, tambahkan kode ini ke bagian atas file 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""" DATABASE_URL: PostgresDsn DEBUG: bool = False 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=True) } DEBUG = config.DEBUG

Kode ini melakukan hal berikut:

  • Mendefinisikan kelas SettingsFromEnvironment , mewarisi dari kelas BaseSettings .
  • Mendefinisikan DATABASE_URL dan DEBUG , menyetel tipenya dan default opsional menggunakan petunjuk tipe Python.
  • Mendefinisikan kelas Config yang memberi tahu pydantic untuk mencari variabel dalam file .env jika tidak ada dalam variabel lingkungan sistem.
  • Membuat instance kelas Config ke dalam objek config ; variabel yang diinginkan tersedia sebagai config.DATABASE_URL dan config.DEBUG .
  • Mendefinisikan variabel Django reguler DATABASES dan DEBUG dari anggota config ini.

Kode yang sama berjalan di semua lingkungan, dan pydantic menangani hal berikut:

  • Itu mencari variabel lingkungan DATABASE_URL dan DEBUG .
    • Jika didefinisikan sebagai variabel lingkungan, seperti dalam produksi, ia akan menggunakannya.
    • Jika tidak, itu menarik nilai-nilai itu dari file .env .
    • Jika tidak menemukan nilai, itu akan melakukan hal berikut:
      • Untuk DATABASE_URL , itu menimbulkan kesalahan.
      • Untuk DEBUG , ini memberikan nilai default False .
  • Jika menemukan variabel lingkungan, ia akan memeriksa jenis bidang dan memberikan kesalahan jika salah satu dari mereka salah:
    • Untuk DATABASE_URL , itu memverifikasi bahwa jenis bidangnya adalah URL gaya PostgresDsn .
    • Untuk DEBUG , ia memverifikasi bahwa jenis bidangnya adalah boolean pydantic yang valid dan tidak ketat.

Harap perhatikan pengaturan eksplisit variabel lingkungan sistem operasi dari nilai konfigurasi untuk DATABASE_URL . Tampaknya berlebihan untuk menetapkan os.environ["DATABASE_URL"] = config.DATABASE_URL karena DATABASE_URL sudah didefinisikan sebagai variabel lingkungan eksternal. Namun, ini memungkinkan pydantic untuk mengurai, memeriksa, dan memvalidasi variabel ini. Jika variabel lingkungan DATABASE_URL hilang atau salah format, pydantic akan memberikan pesan kesalahan yang jelas. Pemeriksaan kesalahan ini sangat berharga saat aplikasi berpindah dari pengembangan ke lingkungan berikutnya.

Jika variabel tidak didefinisikan, baik default akan ditetapkan atau kesalahan akan meminta untuk didefinisikan. Setiap prompt yang dihasilkan juga merinci jenis variabel yang diinginkan. Manfaat sampingan dari pemeriksaan ini adalah bahwa anggota tim baru dan insinyur DevOps lebih mudah menemukan variabel mana yang perlu didefinisikan. Ini menghindari masalah yang sulit ditemukan yang terjadi saat aplikasi berjalan tanpa semua variabel yang ditentukan.

Itu dia. Aplikasi sekarang memiliki implementasi manajemen pengaturan yang dapat dipelihara menggunakan satu versi settings.py . Keindahan dari pendekatan ini adalah memungkinkan kita untuk menentukan variabel lingkungan yang benar dalam file .env atau cara lain yang diinginkan yang tersedia melalui lingkungan hosting kita.

Jalur yang Dapat Diukur

Saya telah menggunakan manajemen pengaturan Django dengan pengetikan runtime pydantic di semua proyek Django saya. Saya telah menemukan itu mengarah ke basis kode yang lebih kecil dan lebih dapat dipelihara. Ini juga menyediakan pendekatan yang terstruktur dengan baik, mendokumentasikan diri sendiri, dan skalabel untuk menambahkan lingkungan baru.

Artikel berikutnya dalam seri ini adalah tutorial langkah demi langkah dalam membangun aplikasi Django dari awal, dengan manajemen pengaturan pydantic, dan menyebarkannya ke Heroku.


Blog Toptal Engineering mengucapkan terima kasih kepada Stephen Davidson karena telah meninjau contoh kode yang disajikan dalam artikel ini.

Versi sebelumnya dari artikel ini menekankan rilis Python tertentu. Karena Python telah mendukung petunjuk jenis selama beberapa tahun, kami telah menggeneralisasi teks pengantar dengan menghapus referensi versi ini.