Geliştirme ve Üretim için Ortamınızı Optimize Edin: Pydantic Eğitimi, 2. Bölüm
Yayınlanan: 2022-07-22Geliştiriciler kendilerinin en kötü düşmanları olabilir. Kendi üretim ortamlarına uymayan bir sistem üzerinde gelişen sayısız mühendis örneği gördüm. Bu uyumsuzluk, fazladan çalışmaya ve geliştirme sürecinin ilerleyen safhalarına kadar sistem hatalarının yakalanmamasına yol açar. Bu kurulumları hizalamak, sonuçta sürekli dağıtımları kolaylaştıracaktır. Bunu akılda tutarak, Django geliştirme ortamımızda Docker, pydantic ve conda ile basitleştirilmiş örnek bir uygulama oluşturacağız.
Tipik bir geliştirme ortamı şunları kullanır:
- Yerel bir depo;
- Docker tabanlı bir PostgreSQL veritabanı; ve
- Bir conda ortamı (Python bağımlılıklarını yönetmek için).
Pydantic ve Django, hem basit hem de karmaşık projeler için uygundur. Aşağıdaki adımlar, ortamlarımızı nasıl yansıtacağımızı vurgulayan basit bir çözümü göstermektedir.
Git Deposu Yapılandırması
Kod yazmaya veya geliştirme sistemleri kurmaya başlamadan önce yerel bir Git deposu oluşturalım:
mkdir hello-visitor cd hello-visitor git init
Depo kökündeki temel bir Python .gitignore
dosyasıyla başlayacağız. Bu öğretici boyunca, Git'in izlemesini istemediğimiz dosyaları eklemeden önce bu dosyaya ekleyeceğiz.
Docker Kullanarak Django PostgreSQL Yapılandırması
Django, ilişkisel bir veritabanı gerektirir ve varsayılan olarak SQLite kullanır. Eşzamanlı kullanıcı erişimini iyi işlemediğinden, kritik görev veri depolaması için SQLite'tan genellikle kaçınırız. Çoğu geliştirici, PostgreSQL gibi daha tipik bir üretim veritabanını tercih eder. Ne olursa olsun, geliştirme ve üretim için aynı veritabanını kullanmalıyız. Bu mimari görev, On İki Faktör Uygulamasının bir parçasıdır.
Neyse ki, Docker ve Docker Compose ile yerel bir PostgreSQL örneğini çalıştırmak çok kolay.
Kök dizinimizi kirletmekten kaçınmak için Docker ile ilgili dosyaları ayrı alt dizinlere koyacağız. PostgreSQL'i dağıtmak için bir Docker Compose dosyası oluşturarak başlayacağız:
# 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:
Ardından, PostgreSQL kapsayıcımızı yapılandırmak için bir docker-compose
ortam dosyası oluşturacağız:
# docker-services/.env POSTGRES_USER=postgres POSTGRES_PASSWORD=MyDBPassword123 # The 'maintenance' database POSTGRES_DB=postgres # The port exposed to localhost POSTGRES_PORT=5432
Veritabanı sunucusu artık tanımlanmış ve yapılandırılmıştır. Konteynerimizi arka planda başlatalım:
sudo docker compose --project-directory docker-services/ up -d
Önceki komutta sudo kullanımına dikkat etmek önemlidir. Geliştirme ortamımızda belirli adımlar izlenmedikçe gerekli olacaktır.
Veritabanı Oluşturma
Standart bir araç paketi olan pgAdmin4'ü kullanarak PostgreSQL'e bağlanıp yapılandıralım. Ortam değişkenlerinde daha önce yapılandırılanla aynı oturum açma kimlik bilgilerini kullanacağız.
Şimdi hello_visitor
adında yeni bir veritabanı oluşturalım:
Veritabanımız yerindeyken, programlama ortamımızı kurmaya hazırız.
Miniconda ile Python Çevre Yönetimi
Şimdi izole bir Python ortamı ve gerekli bağımlılıklar kurmamız gerekiyor. Kurulum ve bakımın basitliği için Miniconda'yı seçtik.
Conda ortamımızı oluşturalım ve etkinleştirelim:
conda create --name hello-visitor python=3.9 conda activate hello-visitor
Şimdi, hello-visitor/requirements.txt
bir dosya oluşturacağız ve Python bağımlılıklarımızı sıralayacağız:
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
Ardından, Python'dan bu bağımlılıkları yüklemesini isteyeceğiz:
cd hello-visitor pip install -r requirements.txt
Bağımlılıklarımız, uygulama geliştirme çalışmasına hazırlık olarak şimdi kurulmalıdır.
Django İskele
Projemizi ve uygulamamızı önce django-admin
çalıştırarak, ardından oluşturduğu bir dosyayı ( manage.py
) çalıştırarak oluşturacağız:
# 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
Ardından, projemizi yüklemek için Django'yu yapılandırmamız gerekiyor. settings.py
dosyası, yeni oluşturulan homepage
uygulamamızı kaydetmek için INSTALLED_APPS
dizisinde bir ayarlama gerektirir:
# src/hello_visitor/settings.py # ... INSTALLED_APPS = [ "homepage.apps.HomepageConfig", "django.contrib.admin", # ... ] # ...
Uygulama Ayarı Yapılandırması
İlk taksitte gösterilen pydantic ve Django ayarları yaklaşımını kullanarak, geliştirme sistemimiz için bir ortam değişkenleri dosyası oluşturmamız gerekiyor. Mevcut ayarlarımızı bu dosyaya şu şekilde taşıyacağız:
- Geliştirme ortamı ayarlarımızı tutmak için
src/.env
dosyasını oluşturun. - Ayarları
src/hello_visitor/settings.py
kopyalayın ve src/src/.env
dizinine ekleyin. - Kopyalanan satırları
settings.py
dosyasından kaldırın. - Veritabanı bağlantı dizesinin daha önce yapılandırdığımız kimlik bilgilerini kullandığından emin olun.
Ortam dosyamız src/.env
görünmelidir:
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" ]'
Bu kod parçacığıyla, Django'yu pydantic kullanarak ortam değişkenlerimizden ayarları okuyacak şekilde yapılandıracağız:
# 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 # ...
Önceki düzenlemeleri tamamladıktan sonra herhangi bir sorunla karşılaşırsanız, hazırlanmış settings.py
dosyamızı kaynak kod havuzumuzdaki sürümle karşılaştırın.
Model Oluşturma
Uygulamamız, ana sayfa ziyaretçi sayısını izler ve görüntüler. Bu sayıyı tutacak bir modele ihtiyacımız var ve ardından bir veri geçişi yoluyla tek bir veritabanı satırını başlatmak için Django'nun nesne-ilişkisel eşleyicisini (ORM) kullanıyoruz.
İlk olarak VisitCounter
modelimizi oluşturacağız:
# 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}"
Ardından, veritabanı tablolarımızı oluşturmak için bir geçişi tetikleyeceğiz:
# in the `src` folder python manage.py makemigrations python manage.py migrate
homepage_visitcounter
tablosunun var olduğunu doğrulamak için veritabanını pgAdmin4'te görüntüleyebiliriz.
Ardından, homepage_visitcounter
tablomuza bir başlangıç değeri koymamız gerekiyor. Bunu Django iskelesini kullanarak gerçekleştirmek için ayrı bir geçiş dosyası oluşturalım:
# from the 'src' directory python manage.py makemigrations --empty homepage
Oluşturulan taşıma dosyasını bu bölümün başında tanımladığımız VisitCounter.insert_visit_counter
yöntemini kullanacak şekilde ayarlayacağız:
# 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), ]
Artık homepage
uygulaması için bu değiştirilmiş taşıma işlemini gerçekleştirmeye hazırız:
# from the 'src' directory python manage.py migrate homepage
Tablomuzun içeriğine bakarak geçişin doğru şekilde yürütüldüğünü doğrulayalım:
homepage_visitcounter
tablomuzun var olduğunu ve ilk ziyaret sayısı 0 ile doldurulduğunu görüyoruz. Veritabanımızın karesini aldıktan sonra, UI'mizi oluşturmaya odaklanacağız.
Görünümlerimizi Oluşturun ve Yapılandırın
Arayüzümüzün iki ana bölümünü uygulamamız gerekiyor: bir görünüm ve bir şablon.
Ziyaretçi sayısını artırmak için homepage
görünümünü oluşturuyoruz, veritabanına kaydediyoruz ve bu sayımı görüntülemek için şablona aktarıyoruz:
# 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 uygulamamızın homepage
yönelik istekleri dinlemesi gerekiyor. Bu ayarı yapılandırmak için şu dosyayı ekleyeceğiz:
# 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
uygulamamızın hizmete girmesi için onu farklı bir urls.py
dosyasına kaydetmeliyiz:
# 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), ]
Projemizin temel HTML şablonu, src/templates/layouts/base.html
adlı yeni bir dosyada yer alacaktır:
<!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
uygulamamız için temel şablonu yeni bir dosyada genişleteceğiz, 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 %}
Arayüzümüzü oluşturmanın son adımı, Django'ya bu şablonları nerede bulacağını söylemektir. settings.py
dosyamıza bir TEMPLATES['DIRS']
sözlük öğesi ekleyelim:
# src/hello_visitor/settings.py TEMPLATES = [ { ... 'DIRS': [BASE_DIR / 'templates'], ... }, ]
Kullanıcı arayüzümüz artık uygulandı ve uygulamamızın işlevselliğini test etmeye neredeyse hazırız. Testimizi yapmadan önce, ortamımızın son parçasını yerleştirmemiz gerekiyor: statik içerik önbelleğe alma.
Statik İçerik Yapılandırmamız
Geliştirme sistemimizde mimari kısayollar kullanmaktan kaçınmak için, üretim ortamımızı yansıtmak için statik içerik önbelleğe almayı yapılandıracağız.
Projemizin tüm statik dosyalarını tek bir dizinde tutacağız, src/static
ve dağıtımdan önce Django'ya bu dosyaları toplamasını söyleyeceğiz.
Uygulamamızın favicon
için Toptal'ın logosunu kullanacağız ve onu src/static/favicon.ico
olarak saklayacağız:
# from `src` folder mkdir static cd static wget https://frontier-assets.toptal.com/83b2f6e0d02cdb3d951a75bd07ee4058.png mv 83b2f6e0d02cdb3d951a75bd07ee4058.png favicon.ico
Ardından, statik dosyaları toplamak için Django'yu yapılandıracağız:
# 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"
Yalnızca orijinal statik dosyalarımızı kaynak kod deposunda saklamak istiyoruz; üretim için optimize edilmiş sürümleri saklamak istemiyoruz. Bu basit satırla ikincisini .gitignore
:
staticfiles
Gerekli dosyaları doğru bir şekilde depolayan kaynak kod depomuzla, şimdi önbellekleme sistemimizi bu statik dosyalarla çalışacak şekilde yapılandırmamız gerekiyor.
Statik Dosya Önbelleğe Alma
Üretimde ve dolayısıyla geliştirme ortamımızda da Django uygulamamızın statik dosyalarını daha verimli bir şekilde sunmak için WhiteNoise kullanacağız.
src/hello_visitor/settings.py
dosyamıza aşağıdaki snippet'i ekleyerek WhiteNoise'ı ara katman yazılımı olarak kaydediyoruz. Kayıt sırası kesin olarak tanımlanmıştır ve WhiteNoiseMiddleware
, SecurityMiddleware
hemen sonra görünmelidir:
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', # ... ] STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
Statik dosya önbelleğe alma, uygulamamızı çalıştırmamızı sağlayan geliştirme ortamımızda yapılandırılmalıdır.
Geliştirme Sunucumuzu Çalıştırma
Tamamen kodlanmış bir uygulamamız var ve artık bu komutla Django'nun gömülü geliştirme web sunucusunu başlatabiliriz:
# in the `src` folder python manage.py runserver
http://localhost:8000
gittiğimizde, sayfayı her yenilediğimizde sayı artacaktır:
Artık, sayfayı yeniledikçe ziyaret sayısını artıracak çalışan bir uygulamamız var.
Dağıtıma Hazır
Bu öğretici, üretimle eşleşen güzel bir Django geliştirme ortamında çalışan bir uygulama oluşturmak için gereken tüm adımları kapsar. Bölüm 3'te, uygulamamızı üretim ortamına dağıtmayı ele alacağız. Ayrıca, Django ve pydantic'in faydalarını vurgulayan ek alıştırmalarımızı keşfetmeye değer: Bunlar, bu pydantic öğreticisi için eksiksiz kod deposuna dahil edilmiştir.
Toptal Engineering Blog, bu makalede sunulan kod örneklerini gözden geçirdiği ve beta testi için Stephen Davidson'a şükranlarını sunar.