Optimalkan Lingkungan Anda untuk Pengembangan dan Produksi: Tutorial Pydantic, Bagian 2

Diterbitkan: 2022-07-22

Pengembang bisa menjadi musuh terburuk mereka sendiri. Saya telah melihat banyak contoh insinyur yang mengembangkan sistem yang tidak sesuai dengan lingkungan produksi mereka. Disonansi ini menyebabkan kerja ekstra dan tidak menangkap kesalahan sistem sampai nanti dalam proses pengembangan. Menyelaraskan penyiapan ini pada akhirnya akan memudahkan penerapan berkelanjutan. Dengan mengingat hal ini, kami akan membuat aplikasi sampel pada lingkungan pengembangan Django kami, disederhanakan melalui Docker, pydantic, dan conda.

Lingkungan pengembangan yang khas menggunakan:

  • Repositori lokal;
  • Database PostgreSQL berbasis Docker; dan
  • Lingkungan conda (untuk mengelola dependensi Python).

Pydantic dan Django cocok untuk proyek baik yang sederhana maupun yang kompleks. Langkah-langkah berikut menunjukkan solusi sederhana yang menyoroti cara mencerminkan lingkungan kita.

Konfigurasi Repositori Git

Sebelum kita mulai menulis kode atau menginstal sistem pengembangan, mari buat repositori Git lokal:

 mkdir hello-visitor cd hello-visitor git init

Kita akan mulai dengan file dasar Python .gitignore di root repositori. Sepanjang tutorial ini, kita akan menambahkan file ini sebelum menambahkan file yang tidak ingin dilacak oleh Git.

Konfigurasi Django PostgreSQL Menggunakan Docker

Django membutuhkan database relasional dan, secara default, menggunakan SQLite. Kami biasanya menghindari SQLite untuk penyimpanan data mission-critical karena tidak menangani akses pengguna secara bersamaan dengan baik. Sebagian besar pengembang memilih database produksi yang lebih umum, seperti PostgreSQL. Apapun, kita harus menggunakan database yang sama untuk pengembangan dan produksi. Mandat arsitektur ini adalah bagian dari Aplikasi Dua Belas Faktor.

Untungnya, mengoperasikan instance PostgreSQL lokal dengan Docker dan Docker Compose sangat mudah.

Untuk menghindari polusi direktori root kami, kami akan menempatkan file terkait Docker di subdirektori terpisah. Kita akan mulai dengan membuat file Docker Compose untuk menyebarkan PostgreSQL:

 # 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:

Selanjutnya, kami akan membuat file lingkungan docker-compose untuk mengonfigurasi wadah PostgreSQL kami:

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

Server database sekarang didefinisikan dan dikonfigurasi. Mari kita mulai wadah kita di latar belakang:

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

Penting untuk diperhatikan penggunaan sudo pada perintah sebelumnya. Ini akan diperlukan kecuali langkah-langkah spesifik diikuti di lingkungan pengembangan kami.

Pembuatan Basis Data

Mari kita sambungkan dan konfigurasikan PostgreSQL menggunakan rangkaian alat standar, pgAdmin4. Kami akan menggunakan kredensial login yang sama seperti yang dikonfigurasi sebelumnya di variabel lingkungan.

Sekarang mari kita buat database baru bernama hello_visitor :

Layar pgAdmin4 dalam browser menampilkan tab Umum dalam dialog Buat Basis Data. Bidang teks database berisi nilai hello_visitor, bidang pemilik menampilkan pengguna postgres, dan bidang komentar kosong.

Dengan database kami di tempat, kami siap untuk menginstal lingkungan pemrograman kami.

Manajemen Lingkungan Python melalui Miniconda

Kita sekarang perlu menyiapkan lingkungan Python yang terisolasi dan dependensi yang diperlukan. Untuk kesederhanaan pengaturan dan pemeliharaan, kami memilih Miniconda.

Mari buat dan aktifkan lingkungan conda kita:

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

Sekarang, kita akan membuat sebuah file, hello-visitor/requirements.txt , yang menyebutkan dependensi Python kita:

 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

Selanjutnya, kami akan meminta Python untuk menginstal dependensi ini:

 cd hello-visitor pip install -r requirements.txt

Dependensi kita sekarang harus diinstal sebagai persiapan untuk pekerjaan pengembangan aplikasi.

Perancah Django

Kami akan membuat perancah proyek dan aplikasi kami dengan terlebih dahulu menjalankan django-admin , kemudian menjalankan file yang dihasilkannya, 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

Selanjutnya, kita perlu mengkonfigurasi Django untuk memuat proyek kita. File settings.py memerlukan penyesuaian pada larik INSTALLED_APPS untuk mendaftarkan aplikasi homepage kami yang baru dibuat:

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

Konfigurasi Pengaturan Aplikasi

Menggunakan pendekatan pengaturan pydantic dan Django yang ditunjukkan dalam angsuran pertama, kita perlu membuat file variabel lingkungan untuk sistem pengembangan kita. Kami akan memindahkan pengaturan kami saat ini ke dalam file ini sebagai berikut:

  1. Buat file src/.env untuk menyimpan pengaturan lingkungan pengembangan kita.
  2. Salin pengaturan dari src/hello_visitor/settings.py dan tambahkan ke src/.env .
  3. Hapus baris yang disalin dari file settings.py .
  4. Pastikan string koneksi database menggunakan kredensial yang sama yang kita konfigurasikan sebelumnya.

File lingkungan kita, src/.env , akan terlihat seperti ini:

 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" ]'

Kami akan mengonfigurasi Django untuk membaca pengaturan dari variabel lingkungan kami menggunakan pydantic, dengan potongan kode ini:

 # 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 # ...

Jika Anda mengalami masalah setelah menyelesaikan pengeditan sebelumnya, bandingkan file settings.py buatan kami dengan versi di repositori kode sumber kami.

Pembuatan Model

Aplikasi kami melacak dan menampilkan jumlah pengunjung beranda. Kita membutuhkan model untuk menahan hitungan itu dan kemudian menggunakan object-relational mapper (ORM) Django untuk menginisialisasi baris database tunggal melalui migrasi data.

Pertama, kami akan membuat model VisitCounter kami:

 # 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}"

Selanjutnya, kami akan memicu migrasi untuk membuat tabel database kami:

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

Untuk memverifikasi bahwa tabel homepage_visitcounter ada, kita dapat melihat database di pgAdmin4.

Selanjutnya, kita perlu meletakkan nilai awal di tabel homepage_visitcounter kita. Mari buat file migrasi terpisah untuk mencapai ini menggunakan perancah Django:

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

Kami akan menyesuaikan file migrasi yang dibuat untuk menggunakan metode VisitCounter.insert_visit_counter yang kami definisikan di awal bagian ini:

 # 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), ]

Sekarang kami siap untuk menjalankan migrasi yang dimodifikasi ini untuk aplikasi homepage :

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

Mari kita verifikasi bahwa migrasi dijalankan dengan benar dengan melihat isi tabel kita:

Layar pgAdmin4 dalam browser menampilkan kueri "SELECT * FROM public.homepage_visitcounter ORDER BY id ASC". Tab Data Output menunjukkan bahwa ada satu baris di dalam tabel itu. Nilai bidang id kunci pengganti adalah 1, dan nilai bidang hitungan adalah 0.

Kami melihat bahwa tabel homepage_visitcounter kami ada dan telah diisi dengan jumlah kunjungan awal 0. Dengan database kami dikuadratkan, kami akan fokus pada pembuatan UI kami.

Buat dan Konfigurasikan Tampilan Kami

Kita perlu mengimplementasikan dua bagian utama dari UI kita: tampilan dan template.

Kami membuat tampilan homepage untuk menambah jumlah pengunjung, menyimpannya ke database, dan meneruskan hitungan itu ke template untuk ditampilkan:

 # 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)

Aplikasi Django kami perlu mendengarkan permintaan yang ditujukan ke homepage . Untuk mengonfigurasi pengaturan ini, kami akan menambahkan file ini:

 # 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"), ]

Agar aplikasi homepage kami disajikan, kami harus mendaftarkannya di file urls.py yang berbeda:

 # 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), ]

Template HTML dasar proyek kami akan hidup dalam file baru, 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>

Kami akan memperluas template dasar untuk aplikasi homepage kami di file baru, 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 %}

Langkah terakhir dalam membuat UI kita adalah memberi tahu Django di mana menemukan templat-templat ini. Mari tambahkan item kamus TEMPLATES['DIRS'] ke file settings.py kita:

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

Antarmuka pengguna kami sekarang diimplementasikan dan kami hampir siap untuk menguji fungsionalitas aplikasi kami. Sebelum melakukan pengujian, kita perlu menempatkan bagian terakhir dari lingkungan kita: cache konten statis.

Konfigurasi Konten Statis Kami

Untuk menghindari mengambil pintasan arsitektur pada sistem pengembangan kami, kami akan mengonfigurasi cache konten statis untuk mencerminkan lingkungan produksi kami.

Kami akan menyimpan semua file statis proyek kami dalam satu direktori, src/static , dan menginstruksikan Django untuk mengumpulkan file-file itu sebelum penerapan.

Kami akan menggunakan logo Toptal untuk favicon aplikasi kami dan menyimpannya sebagai src/static/favicon.ico :

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

Selanjutnya, kita akan mengonfigurasi Django untuk mengumpulkan file statis:

 # 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"

Kami hanya ingin menyimpan file statis asli kami di repositori kode sumber; kami tidak ingin menyimpan versi yang dioptimalkan untuk produksi. Mari tambahkan yang terakhir ke .gitignore kita dengan baris sederhana ini:

 staticfiles

Dengan repositori kode sumber kami menyimpan file yang diperlukan dengan benar, kami sekarang perlu mengonfigurasi sistem caching kami untuk bekerja dengan file statis ini.

Caching File Statis

Dalam produksi—dan dengan demikian, juga dalam lingkungan pengembangan kita—kita akan menggunakan WhiteNoise untuk melayani file statis aplikasi Django kita dengan lebih efisien.

Kami mendaftarkan WhiteNoise sebagai middleware dengan menambahkan cuplikan berikut ke file src/hello_visitor/settings.py kami. Urutan pendaftaran ditentukan secara ketat, dan WhiteNoiseMiddleware harus segera muncul setelah SecurityMiddleware :

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

Caching file statis sekarang harus dikonfigurasi di lingkungan pengembangan kami, memungkinkan kami untuk menjalankan aplikasi kami.

Menjalankan Server Pengembangan Kami

Kami memiliki aplikasi yang sepenuhnya dikodekan dan sekarang dapat meluncurkan server web pengembangan tertanam Django kami dengan perintah ini:

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

Saat kita menavigasi ke http://localhost:8000 , hitungan akan bertambah setiap kali kita me-refresh halaman:

Sebuah jendela browser menampilkan layar utama dari aplikasi Django pydantic kita, yang mengatakan, "Halo, pengunjung!" pada satu baris dan "1" pada baris berikutnya.

Kami sekarang memiliki aplikasi yang berfungsi yang akan menambah jumlah kunjungannya saat kami menyegarkan halaman.

Siap Disebarkan

Tutorial ini telah mencakup semua langkah yang diperlukan untuk membuat aplikasi yang berfungsi dalam lingkungan pengembangan Django yang indah yang cocok dengan produksi. Di Bagian 3, kita akan membahas penerapan aplikasi kita ke lingkungan produksinya. Perlu juga menjelajahi latihan tambahan kami yang menyoroti manfaat Django dan pydantic: Mereka disertakan dalam repositori kode lengkap untuk tutorial pydantic ini.


Blog Toptal Engineering mengucapkan terima kasih kepada Stephen Davidson karena telah meninjau dan menguji beta sampel kode yang disajikan dalam artikel ini.