توسعه دهندگان می توانند بدترین دشمنان خود باشند. من نمونههای بیشماری از مهندسین را دیدهام که روی سیستمی توسعه میدهند که با محیط تولیدشان مطابقت ندارد. این ناهماهنگی منجر به کار اضافی و عدم شناسایی خطاهای سیستم تا بعداً در فرآیند توسعه می شود. تراز کردن این تنظیمات در نهایت استقرار مداوم را تسهیل می کند. با در نظر گرفتن این موضوع، ما یک نمونه برنامه کاربردی در محیط توسعه جنگو خود ایجاد خواهیم کرد که از طریق Docker، pydantic و conda ساده شده است.
یک محیط توسعه معمولی از موارد زیر استفاده می کند:
- یک مخزن محلی؛
- پایگاه داده PostgreSQL مبتنی بر Docker. و
- یک محیط conda (برای مدیریت وابستگی های پایتون).
Pydantic و Django برای پروژه های ساده و پیچیده مناسب هستند. مراحل زیر راه حل ساده ای را نشان می دهد که نحوه انعکاس محیط هایمان را برجسته می کند.
پیکربندی مخزن Git
قبل از شروع نوشتن کد یا نصب سیستم های توسعه، بیایید یک مخزن Git محلی ایجاد کنیم:
mkdir hello-visitor
cd hello-visitor
git init
ما با یک شروع می کنیم پایتون پایه .gitignore
فایل در ریشه مخزن در طول این آموزش، قبل از افزودن فایلهایی که نمیخواهیم Git آنها را ردیابی کند، به این فایل اضافه میکنیم.
پیکربندی Django PostgreSQL با استفاده از Docker
جنگو به یک پایگاه داده رابطه ای نیاز دارد و به طور پیش فرض از SQLite استفاده می کند. ما معمولاً از SQLite برای ذخیره سازی داده های حیاتی اجتناب می کنیم زیرا دسترسی همزمان کاربر را به خوبی مدیریت نمی کند. اکثر توسعه دهندگان یک پایگاه داده تولید معمولی مانند PostgreSQL را انتخاب می کنند. صرف نظر از این، ما باید از همان پایگاه داده برای توسعه و تولید استفاده کنیم. این دستور معماری بخشی از برنامه دوازده عاملی.
خوشبختانه، یک نمونه محلی PostgreSQL با داکر و Docker Compose یک نسیم است
برای جلوگیری از آلوده شدن دایرکتوری ریشه ما، فایل های مربوط به Docker را در زیر شاخه های جداگانه قرار می دهیم. ما با ایجاد یک فایل Docker Compose برای استقرار 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:
بعد، یک را ایجاد می کنیم docker-compose
فایل محیطی برای پیکربندی ظرف PostgreSQL:
# 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 در دستور قبلی ضروری است. مورد نیاز خواهد بود مگر اینکه مراحل خاص در محیط توسعه ما دنبال می شوند.
ایجاد پایگاه داده
بیایید با استفاده از یک مجموعه ابزار استاندارد به PostgreSQL متصل و پیکربندی کنیم. pgAdmin4. ما از همان اعتبار ورود به سیستم استفاده خواهیم کرد که قبلاً در متغیرهای محیطی پیکربندی شده بود.
حالا بیایید یک پایگاه داده جدید به نام ایجاد کنیم hello_visitor
:
با در اختیار داشتن پایگاه داده، ما آماده نصب محیط برنامه نویسی خود هستیم.
مدیریت محیط زیست پایتون از طریق Miniconda
اکنون باید یک محیط Python ایزوله و وابستگی های مورد نیاز را راه اندازی کنیم. برای سادگی راه اندازی و نگهداری، ما را انتخاب کردیم مینیکوندا.
بیایید محیط conda خود را ایجاد و فعال کنیم:
conda create --name hello-visitor python=3.9
conda activate hello-visitor
اکنون یک فایل ایجاد می کنیم، 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
در مرحله بعد، از پایتون می خواهیم این وابستگی ها را نصب کند:
cd hello-visitor
pip install -r requirements.txt
وابستگی های ما اکنون باید برای آماده سازی کار توسعه برنامه نصب شوند.
داربست جنگو
با اجرای اول پروژه و برنامه خود را داربست خواهیم داشت 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
بعد، ما باید جنگو را برای بارگذاری پروژه خود پیکربندی کنیم. را settings.py
فایل نیاز به تنظیم دارد INSTALLED_APPS
آرایه ای برای ثبت نام جدید ایجاد شده ما homepage
برنامه:
# src/hello_visitor/settings.py
# ...
INSTALLED_APPS = [
"homepage.apps.HomepageConfig",
"django.contrib.admin",
# ...
]
# ...
پیکربندی تنظیمات برنامه
با استفاده از رویکرد تنظیمات pydantic و جنگو که در قسمت اول نشان داده شد، باید یک فایل متغیرهای محیطی برای سیستم توسعه خود ایجاد کنیم. تنظیمات فعلی خود را به صورت زیر به این فایل منتقل می کنیم:
- فایل را ایجاد کنید
src/.env
برای نگه داشتن تنظیمات محیط توسعه ما. - تنظیمات را از
src/hello_visitor/settings.py
و آنها را به آن اضافه کنیدsrc/.env
. - آن خطوط کپی شده را از قسمت حذف کنید
settings.py
فایل. - اطمینان حاصل کنید که رشته اتصال پایگاه داده از همان اعتبارنامه هایی استفاده می کند که قبلاً پیکربندی کردیم.
فایل محیطی ما، 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، با این قطعه کد پیکربندی میکنیم:
# 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
فایل با نسخه در مخزن کد منبع ما.
ایجاد مدل
برنامه ما تعداد بازدیدکنندگان صفحه اصلی را ردیابی و نمایش می دهد. ما نیاز داریم a مدل برای نگه داشتن آن شمارش و سپس استفاده نقشهبردار شی رابطهای جنگو (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
جدول. بیایید یک فایل مهاجرت جداگانه برای انجام این کار با استفاده از داربست جنگو ایجاد کنیم:
# 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:
#
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
بیایید با مشاهده محتویات جدول بررسی کنیم که انتقال به درستی انجام شده است:
ما می بینیم که ما homepage_visitcounter
جدول وجود دارد و با تعداد بازدید اولیه 0 پر شده است. با مجذور شدن پایگاه داده، ما بر ایجاد رابط کاربری خود تمرکز خواهیم کرد.
دیدگاه های ما را ایجاد و پیکربندی کنید
ما باید دو بخش اصلی UI خود را پیاده سازی کنیم: یک view و یک الگو.
ما ایجاد می کنیم 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)
برنامه جنگو ما نیاز به گوش دادن به درخواست های هدف دارد 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=" 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=" 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 %}
آخرین مرحله در ایجاد رابط کاربری ما این است که به جنگو بگوییم این الگوها را کجا پیدا کند. بیایید یک را اضافه کنیم TEMPLATES['DIRS']
مورد فرهنگ لغت به ما settings.py
فایل:
# src/hello_visitor/settings.py
TEMPLATES = [
{
...
'DIRS': [BASE_DIR / 'templates'],
...
},
]
رابط کاربری ما اکنون پیاده سازی شده است و تقریباً آماده آزمایش عملکرد برنامه خود هستیم. قبل از اینکه آزمایش خود را انجام دهیم، باید آخرین قطعه محیط خود را در جای خود قرار دهیم: ذخیره محتوای ثابت.
پیکربندی محتوای ثابت ما
برای جلوگیری از استفاده از میانبرهای معماری در سیستم توسعه خود، ذخیره محتوای ثابت را برای انعکاس محیط تولید خود پیکربندی می کنیم.
ما تمام فایل های استاتیک پروژه خود را در یک دایرکتوری نگه می داریم، src/static
و به جنگو دستور دهید تا آن فایل ها را قبل از استقرار جمع آوری کند.
ما از آرم Toptal برای برنامه خود استفاده خواهیم کرد favicon
و آن را به عنوان ذخیره کنید src/static/favicon.ico
:
# from `src` folder
mkdir static
cd static
wget
mv 83b2f6e0d02cdb3d951a75bd07ee4058.png favicon.ico
در مرحله بعد، جنگو را برای جمع آوری فایل های استاتیک پیکربندی می کنیم:
# src/hello_visitor/settings.py
# Static files (CSS, JavaScript, images)
# a la
#
# 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 را به عنوان میان افزار با افزودن قطعه زیر به ما ثبت می کنیم src/hello_visitor/settings.py
فایل. سفارش ثبت به شدت تعریف شده است، و WhiteNoiseMiddleware
باید بلافاصله پس از آن ظاهر شود SecurityMiddleware
:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
# ...
]
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
کش فایل استاتیک اکنون باید در محیط توسعه ما پیکربندی شود و ما را قادر می سازد تا برنامه خود را اجرا کنیم.
اجرای سرور توسعه ما
ما یک برنامه کاملاً کدگذاری شده داریم و اکنون می توانیم وب سرور توسعه جاسازی شده جنگو خود را با این دستور راه اندازی کنیم:
# in the `src` folder
python manage.py runserver
وقتی به سمت http://localhost:8000
، هر بار که صفحه را بازخوانی می کنیم تعداد آن افزایش می یابد:
ما اکنون یک برنامه کاربردی داریم که با بازخوانی صفحه، تعداد بازدیدهایش را افزایش می دهد.
آماده استقرار
این آموزش تمام مراحل مورد نیاز برای ایجاد یک برنامه کاربردی را در یک محیط زیبای توسعه جنگو که با تولید مطابقت دارد را پوشش داده است. در قسمت 3، ما استقرار برنامه خود را در محیط تولید آن پوشش خواهیم داد. همچنین ارزش کاوش در تمرینات اضافی ما را دارد که مزایای جنگو و پیدانتیک را برجسته می کند: آنها در مخزن کد کامل برای این آموزش pydantic.
وبلاگ مهندسی Toptal از استفان دیویدسون برای بررسی و آزمایش بتا نمونه کد ارائه شده در این مقاله تشکر می کند.