Geliştirme ve Üretim için Ortamınızı Optimize Edin: Pydantic Eğitimi, 2. Bölüm

Yayınlanan: 2022-07-22

Geliş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ı Oluştur iletişim kutusundaki Genel sekmesini gösteren bir tarayıcı içindeki bir pgAdmin4 ekranı. Veritabanı metin alanı merhaba_visitor değerini içerir, sahip alanı postgres kullanıcısını görüntüler ve yorum alanı boştur.

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:

  1. Geliştirme ortamı ayarlarımızı tutmak için src/.env dosyasını oluşturun.
  2. Ayarları src/hello_visitor/settings.py kopyalayın ve src/ src/.env dizinine ekleyin.
  3. Kopyalanan satırları settings.py dosyasından kaldırın.
  4. 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:

Bir tarayıcıda "SELECT * FROM public.homepage_visitcounter SİPARİŞ BY id ASC" sorgusunu gösteren bir pgAdmin4 ekranı. Veri Çıkışı sekmesi, o tablo içinde bir satır olduğunu gösterir. Yedek anahtar kimliği alanı değeri 1'dir ve sayım alanı değeri 0'dır.

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:

Pydantic Django uygulamamızın ana ekranını gösteren ve "Merhaba, ziyaretçi!" yazan bir tarayıcı penceresi. bir satırda ve diğerinde "1".

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.