Python, Django և FastAPI վեբ ծրագրավորողների համար
Վեբի համար անհրաժեշտ Python-ը, Django-ի «մարտկոցները» և երբ է FastAPI-ն ավելի հարմար։
30 վայրկյանում
Python-ը ընթեռնելի, ընդհանուր նշանակության լեզու է, որի վրա աշխատում են վեբ բեքենդների, տվյալների և AI աշխատանքների մեծ մասը։ Django-ն նրա «batteries included» վեբ ֆրեյմվորքն է՝ ORM, միգրացիաներ, ադմին վահանակ, auth, ձևեր և անվտանգության լռելյայն կարգավորումներ մեկ փաթեթում։ FastAPI-ն ավելի թեթև, async-first ֆրեյմվորք է տիպավորված JSON API-ների համար։ Եթե գիտեք PHP, Node.js կամ այլ բեքենդ ստեկ, այս դասը ձեր իմացածը կհամադրի Python-ի աշխարհի հետ։
Իմ ամենօրյա stack-ում չէ․ գիտեմ հիմնական գաղափարները և մանրամասները կսովորեմ, երբ նախագիծը դա պահանջի։
Ինչու է սա կարևոր
- Python-ը data science-ի և AI-ի լռելյայն լեզուն է, ուստի շատ թիմեր ուզում են, որ վեբ բեքենդը լինի նույն լեզվով, ինչ մոդելներն ու սկրիպտները։
- Django-ն «տուփից» տալիս է production մակարդակի ադմին, auth և ORM, ինչը փոքր թիմին թույլ է տալիս շատ արագ կառուցել տվյալներով հարուստ պրոդուկտ կամ back office։
- FastAPI-ն դարձել է տարածված ընտրություն տիպավորված, async JSON API-ների և machine learning մոդելները HTTP-ի հետևում մատուցելու համար։
- Laravel-ից կամ NestJS-ից եկող ծրագրավորողի համար Django-ն ու FastAPI-ն շատ մոտ են ծանոթ գաղափարներին, ուստի Python պահանջող նախագիծը կարճ ուսուցում է, ոչ թե զրոյից սկիզբ։
Ինչպես է աշխատում
01Python-ը մեկ էջում վեբ ծրագրավորողների համար
Python-ը ձևավոր փակագծերի փոխարեն օգտագործում է ներքաշում (indentation), չունի կետ-ստորակետեր և կարդացվում է անգլերենին մոտ։ Փոփոխականները դինամիկ տիպավորված են, բայց ժամանակակից կոդը ավելացնում է type hints (name: str, -> list[int]), որոնք mypy կամ Pyright գործիքները ստուգում են մինչև գործարկումը։ Հիմնական տիպերը, որոնք կօգտագործեք ամեն օր՝ str, int, float, bool, None, list, dict, tuple և set։ Ֆունկցիաները սահմանվում են def-ով, կլասները՝ class-ով, իսկ մոդուլները պարզապես .py ֆայլեր են, որոնք import եք անում։ Python 3.14-ը ներկայիս կայուն տարբերակն է (3.15-ը սպասվում է 2026-ի հոկտեմբերի ցիկլում), և միշտ պետք է աշխատել virtual environment-ի ներսում, որպեսզի յուրաքանչյուր նախագիծ ունենա իր կախվածությունները։ Python-ի տարբերակները, միջավայրերը և փաթեթները կառավարելու արագ, ժամանակակից եղանակը uv-ն է. pip-ը venv-ի հետ դեռ աշխատում է ամենուր։
- Truthy և falsy. դատարկ list-ը, դատարկ string-ը, 0-ն և None-ը բոլորը falsy են։
- List և dict comprehension-ները փոխարինում են map և filter կանչերի մեծ մասին։
- async def և await կան, բայց Python կոդի մեծ մասը դեռ սինքրոն է։
- PEP 8-ը ոճի ուղեցույցն է. Ruff-ը միլիվայրկյաններում ֆորմատավորում և lint է անում ըստ դրա։
from dataclasses import dataclass
@dataclass
class Product:
name: str
price: float
tags: list[str]
def cheap(items: list[Product], limit: float = 10.0) -> list[str]:
return [p.name for p in items if p.price < limit]
items = [Product("Mug", 8.5, ["home"]), Product("Lamp", 40.0, [])]
print(cheap(items)) # ['Mug']
02Django նախագծի կարգավորում
Django-ն կազմակերպված է project-ի (settings, արմատային URL-ներ, WSGI/ASGI մուտքի կետեր) և app-երի (ինքնուրույն ֆունկցիոնալ մասեր, օրինակ՝ orders կամ blog) տեսքով։ Յուրաքանչյուր app ունի մոդելներ, view-եր, URL-ներ, ադմինում գրանցում և միգրացիաներ։ Django 6.1-ը ներկայիս feature release-ն է, իսկ 5.2-ը long-term support տարբերակն է. հաջորդ LTS-ը՝ 6.2-ը, նախատեսված է 2027-ի ապրիլին։ Django 6.0-ն ավելացրեց ներկառուցված background tasks API, template partials և Content Security Policy-ի աջակցություն։ Հարցման հոսքը պարզ է. URL resolver-ը գտնում է ուղին, կանչում view-ը, view-ը մոդելներով կարդում կամ գրում է տվյալները և վերադարձնում պատասխան, հաճախ template-ով ռենդեր արված կամ JSON-ի սերիալիզացված։
uv init shop && cd shop
uv add django
uv run django-admin startproject config .
uv run python manage.py startapp catalog
# add "catalog" to INSTALLED_APPS in config/settings.py
uv run python manage.py migrate
uv run python manage.py createsuperuser
uv run python manage.py runserver
03Մոդելներ և ORM
Մոդելը Python կլաս է, որը համապատասխանում է աղյուսակին։ Յուրաքանչյուր ատրիբուտ դաշտ է՝ տիպով և ընտրանքներով, և Django-ն այդ կլասների փոփոխություններից գեներացնում է միգրացիաներ։ ORM-ը տալիս է QuerySet-ներ՝ ծույլ (lazy), շղթայվող query builder-ներ, որոնք դիմում են բազային միայն այն ժամանակ, երբ իտերացնում, կտրում (slice) կամ հաշվարկում եք դրանք։ Կապերը սահմանվում են ForeignKey, OneToOneField և ManyToManyField դաշտերով։ Արտադրողականության համար ամենակարևորը երկու մեթոդ է. select_related-ը միայնակ կապերը միացնում է մեկ հարցման մեջ (JOIN), իսկ prefetch_related-ը բազմակի կապերը բեռնում է երկրորդ հարցմամբ։ Առանց դրանց product.category-ին դիմող ցիկլը յուրաքանչյուր տողի համար մեկ հարցում է կատարում՝ դասական N+1 խնդիրը։
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=100, unique=True)
class Product(models.Model):
name = models.CharField(max_length=200)
price = models.DecimalField(max_digits=10, decimal_places=2)
category = models.ForeignKey(Category, on_delete=models.PROTECT, related_name="products")
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
indexes = [models.Index(fields=["category", "-created_at"])]
# One query with a JOIN instead of N+1:
recent = Product.objects.select_related("category").filter(price__lt=50)[:20]
04Ադմինը՝ back office տասը տողում
Django-ի ադմինը կարդում է ձեր մոդելները և կառուցում լիարժեք CRUD ինտերֆեյս՝ որոնումով, ֆիլտրերով, էջավորմամբ և թույլտվությունների ստուգումով։ Գրանցեք մոդելը, նշեք, որ սյունակները ցույց տալ և որ դաշտերով ֆիլտրել, և աշխատակիցները կարող են անմիջապես կառավարել տվյալները։ Սա այն հիմնական պատճառներից է, որոնց համար թիմերը Django են ընտրում ներքին գործիքների և բովանդակությամբ հարուստ պրոդուկտների համար։ Ադմինին վերաբերվեք որպես վստահելի աշխատակիցների գործիքի, ոչ թե հաճախորդների համար UI-ի. տեղադրեք այն ոչ ակնհայտ ուղու վրա, պահանջեք ուժեղ գաղտնաբառեր կամ SSO, և օգտագործեք list_select_related, որպեսզի մեծ աղյուսակները N+1 հարցումներ չառաջացնեն։
from django.contrib import admin
from .models import Product
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ("name", "category", "price", "created_at")
list_filter = ("category",)
search_fields = ("name",)
list_select_related = ("category",)
ordering = ("-created_at",)
05JSON API-ներ Django REST Framework-ով
Django-ն լռելյայն HTML է ռենդեր անում։ JSON API-ների համար թիմերի մեծ մասն ավելացնում է Django REST Framework (DRF)։ Դրա հիմնական բլոկներն են serializer-ները (մոդելները վերածում են JSON-ի և վալիդացնում մուտքը՝ ինչպես Laravel-ի Form Request-ները գումարած API Resource-ները), view-երը կամ viewset-ները (մշակում են HTTP մեթոդները) և router-ները (գեներացնում են URL-ները)։ DRF-ը տալիս է նաև authentication կլասներ, թույլտվություններ, throttling, էջավորում և զարգացման համար browsable API։ Django Ninja-ն ավելի նոր այլընտրանք է, որը FastAPI-ի ոճով օգտագործում է type hints և Pydantic, միաժամանակ պահպանելով Django-ի ORM-ն ու ադմինը։
from rest_framework import serializers, viewsets, permissions
from .models import Product
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ["id", "name", "price", "category"]
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.select_related("category").order_by("id")
serializer_class = ProductSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
# urls.py
# router = routers.DefaultRouter(); router.register("products", ProductViewSet)
06FastAPI՝ async, տիպավորված այլընտրանքը
FastAPI-ն միկրո-ֆրեյմվորք է՝ կառուցված Starlette-ի (ASGI գործիքակազմ) և Pydantic-ի (վալիդացիա) վրա։ Հարցման և պատասխանի կառուցվածքները հայտարարում եք որպես Pydantic մոդելներ type hints-ով, և FastAPI-ն ավտոմատ վալիդացնում է մուտքը, սերիալիզացնում ելքը և /docs հասցեում գեներացնում OpenAPI փաստաթղթեր։ Այն async-first է, ուստի լավ է մշակում շատ զուգահեռ, I/O-ով սահմանափակված հարցումներ. դրա համար էլ այն տարածված է ML մոդելներ մատուցելու և API gateway-երի համար։ Փոխզիջումը. չունի ներկառուցված ORM, ադմին, միգրացիաներ կամ auth համակարգ։ Դուք ինքներդ եք ընտրում SQLAlchemy կամ SQLModel, Alembic միգրացիաների համար և ձեր սեփական auth-ը։ Ընտրեք Django, երբ ուզում եք ամբողջական, կարծիք ունեցող (opinionated) պրոդուկտային ֆրեյմվորք. ընտրեք FastAPI, երբ ուզում եք փոքր, արագ, տիպավորված API ծառայություն։
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
app = FastAPI()
class ProductIn(BaseModel):
name: str = Field(min_length=1, max_length=200)
price: float = Field(gt=0)
@app.post("/products", status_code=201)
async def create_product(body: ProductIn) -> dict:
if body.name.lower() == "test":
raise HTTPException(status_code=422, detail="Reserved name")
return {"id": 1, **body.model_dump()}
# run: uv add "fastapi[standard]" && uv run fastapi dev main.py
07Python վեբ հավելվածների գործարկումը production-ում
Զարգացման ժամանակ օգտագործում եք runserver կամ fastapi dev, բայց երբեք production-ում։ Սինքրոն Django հավելվածի համար դասական կարգավորումը Gunicorn-ն է մի քանի worker պրոցեսներով nginx-ի հետևում։ Async կոդի համար (FastAPI, Django async view-եր, WebSocket-ներ Channels-ի միջոցով) գործարկում եք ASGI սերվեր, օրինակ՝ Uvicorn, հաճախ Gunicorn-ի կառավարմամբ կամ որպես մի քանի Uvicorn worker։ Ստատիկ ֆայլերը հավաքվում են collectstatic-ով և մատուցվում nginx-ով, CDN-ով կամ WhiteNoise-ով։ Կարգավորումները գալիս են միջավայրի փոփոխականներից, DEBUG-ը պետք է լինի False, ALLOWED_HOSTS-ը պետք է սահմանված լինի, իսկ manage.py check --deploy-ը ցույց է տալիս բացակայող անվտանգության կարգավորումները։ Ֆոնային աշխատանքը գնում է Celery-ին, RQ-ին, Dramatiq-ին կամ Django-ի նոր tasks API-ին՝ worker backend-ով։
# Django (WSGI) with 4 workers
gunicorn config.wsgi:application --workers 4 --bind 127.0.0.1:8000
# FastAPI or Django ASGI
uvicorn main:app --host 127.0.0.1 --port 8000 --workers 4
# Before each deploy
python manage.py check --deploy
python manage.py migrate --noinput
python manage.py collectstatic --noinput
Օգտագործեք, երբ
- Տվյալներով հարուստ պրոդուկտներ, CMS-անման կայքեր և ներքին գործիքներ, որտեղ Django-ի ադմինը շաբաթների աշխատանք է խնայում։
- Թիմեր, որոնք արդեն Python են օգտագործում data science-ի, ML-ի կամ ավտոմատացման համար և ուզում են մեկ լեզու ամբողջ ստեկում։
- Տիպավորված JSON API-ներ կամ ML մոդելների endpoint-ներ, որոնց պետք է բարձր զուգահեռություն և ավտոմատ OpenAPI փաստաթղթեր (FastAPI)։
- Նախագծեր, որոնք գնահատում են կայուն, լավ փաստաթղթավորված, պահպանողական ֆրեյմվորքներ long-term support թողարկումներով։
Մի՛ օգտագործեք, երբ
- Real-time, խիստ զուգահեռ համակարգեր, որտեղ ամբողջ թիմն արդեն ուժեղ է Node.js-ում կամ Go-ում և ունի պատրաստի գործիքներ։
- Երբ թիմն ունի խորը Laravel կամ NestJS փորձ, իսկ Python-ի պահանջ չկա. ստեկ փոխելն իրական գին ունի։
- CPU-ով ծանր հարցումների մշակում մաքուր Python-ով՝ առանց աշխատանքը հերթերին կամ native գրադարաններին փոխանցելու։
Այլընտրանքներ
- Laravel (PHP)
Շատ նման «batteries included» փիլիսոփայություն՝ Eloquent, միգրացիաներ, հերթեր, auth, մեծ էկոհամակարգով։
- NestJS (Node.js)
Կառուցվածքային TypeScript բեքենդ մոդուլներով և DI-ով. մեկ լեզու React կամ Next.js ֆրոնտենդի հետ։
- Flask
Մինիմալ Python միկրո-ֆրեյմվորք. ճկուն է, բայց ամեն ինչ ինքներդ եք հավաքում։
- Django Ninja / Litestar
Տիպավորված, Pydantic ոճի API-ներ. Ninja-ն պահում է Django-ի ORM-ն ու ադմինը, Litestar-ը FastAPI-ին նման այլընտրանք է։
Լավագույն պրակտիկաներ
- Ամրագրեք Python-ի տարբերակն ու կախվածությունները յուրաքանչյուր նախագծի համար uv-ով (pyproject.toml գումարած uv.lock) կամ pip-tools-ով. երբեք մի տեղադրեք համակարգային Python-ի մեջ։
- Յուրաքանչյուր list view-ի համար օգտագործեք select_related և prefetch_related, և զարգացման ժամանակ ստուգեք հարցումների քանակը Django Debug Toolbar-ով։
- Պահեք կարգավորումները միջավայրի փոփոխականներում, CI-ում գործարկեք manage.py check --deploy, և ամեն ոչ լոկալ միջավայրում պահեք DEBUG=False։
- Ավելացրեք type hints և CI-ում գործարկեք Ruff գումարած mypy կամ Pyright. դրանք բռնում են բազմաթիվ բագեր, որոնք դինամիկ տիպավորումը թաքցնում է։
- Առաջին օրվանից օգտագործեք custom user մոդել (AUTH_USER_MODEL). հետագայում այն փոխելը ցավոտ է։
- FastAPI-ում երբեք մի կանչեք արգելափակող գրադարաններ async def handler-ների ներսում. փոխարենը օգտագործեք def handler-ներ կամ async դրայվերներ։
- Թեստերը գրեք pytest-ով և pytest-django-ով. օգտագործեք factory-ներ մեծ fixture-ների փոխարեն։
Տարածված սխալներ
- N+1 հարցումներ՝ ցիկլերում կամ template-ներում կապված օբյեկտներին դիմելիս առանց select_related կամ prefetch_related։
- runserver կամ fastapi dev գործարկելը production-ում կամ DEBUG=True թողնելը, ինչը բացահայտում է կարգավորումներն ու stack trace-երը։
- Սինքրոն բազայի դրայվեր կամ requests.get կանչելը async FastAPI handler-ի ներսում, ինչը սառեցնում է event loop-ը բոլոր օգտատերերի համար։
- Production-ում արդեն կիրառված հին միգրացիաները խմբագրելը՝ նորերն ավելացնելու փոխարեն։
- Դանդաղ աշխատանք (email-ներ, PDF-ներ, արտաքին API-ներ) կատարելը հարցման ներսում՝ ֆոնային worker-ի փոխարեն։
Խորը վերլուծություն
08Ինչպես են QuerySet-ներն իրականում կատարվում
QuerySet-ը ծույլ է. filter-ը, exclude-ը և order_by-ը պարզապես կառուցում են ներքին հարցման ծառ։ SQL-ը կատարվում է, երբ իտերացնում եք, կանչում list(), len(), bool(), կտրում եք քայլով (step) կամ դիմում ինդեքսով։ Առաջին հաշվարկից հետո արդյունքները քեշավորվում են այդ QuerySet օբյեկտի վրա, ուստի այն երկու անգամ իտերացնելը բազային դիմում է մեկ անգամ, բայց .all()-ը կրկին կանչելը ստեղծում է նոր QuerySet և նոր հարցում։ exists()-ը և count()-ը կատարում են էժան, նպատակային հարցումներ, ուստի օգտագործեք դրանք len()-ի փոխարեն, երբ ձեզ պետք է միայն թիվ։ Հսկայական աղյուսակների համար .iterator(chunk_size=2000)-ը հոսքով բերում է տողերը՝ առանց բոլորը հիշողությունում քեշավորելու։ F() արտահայտությունները թվաբանությունը տեղափոխում են SQL և խուսափում race condition-ներից, իսկ annotate-ը Subquery-ով կամ ագրեգատներով հաշվետվական տրամաբանությունը պահում է բազայում։
from django.db.models import F, Count, Q
# Atomic increment in SQL, no read-modify-write race
Product.objects.filter(pk=42).update(stock=F("stock") - 1)
# Aggregate in the database, not in Python
Category.objects.annotate(
active=Count("products", filter=Q(products__price__gt=0))
).order_by("-active")
# Stream a large export
for p in Product.objects.only("id", "name").iterator(chunk_size=2000):
...
09Տրանզակցիաներ, կողպումներ և հետևողականություն
Լռելյայն Django-ն աշխատում է autocommit ռեժիմում. յուրաքանչյուր հարցում commit է լինում ինքնուրույն։ Բազմաքայլ փոփոխությունները փաթեթավորեք transaction.atomic()-ի մեջ, որպեսզի դրանք միասին հաջողվեն կամ ձախողվեն։ Տողային կողպումների համար atomic-ի ներսում select_for_update()-ը կատարում է SELECT ... FOR UPDATE, և այսպես եք անվտանգ նվազեցնում պահեստի քանակը կամ գումար տեղափոխում հաշիվների միջև։ Կողմնակի ազդեցությունները, օրինակ՝ email ուղարկելը կամ task հերթագրելը, պետք է կատարվեն միայն commit-ից հետո. transaction.on_commit()-ը հենց դա է անում, ուստի worker-ը երբեք չի մշակի rollback արված տող։ ATOMIC_REQUESTS-ը յուրաքանչյուր հարցում փաթեթավորում է տրանզակցիայի մեջ, ինչը պարզ է, բայց դանդաղ view-երի ժամանակ տրանզակցիաները բաց է պահում, ուստի շատ թիմեր նախընտրում են բացահայտ atomic բլոկներ։
from django.db import transaction
def reserve(product_id: int, qty: int) -> None:
with transaction.atomic():
p = Product.objects.select_for_update().get(pk=product_id)
if p.stock < qty:
raise ValueError("Out of stock")
p.stock -= qty
p.save(update_fields=["stock"])
transaction.on_commit(lambda: notify_warehouse.enqueue(product_id))
10Async Python-ում. ինչն է իրականում զուգահեռ աշխատում
Python-ի asyncio-ն coroutine-ները գործարկում է մեկ thread-ով event loop-ի վրա։ Զուգահեռությունը գալիս է I/O-ին await անելուց. մինչ մի հարցումը սպասում է ցանցին, մյուսն աշխատում է։ Արգելափակող կանչը (time.sleep, սինքրոն DB դրայվեր, requests) կանգնեցնում է ամբողջ loop-ը։ FastAPI-ն սա լավ է լուծում. սովորական def endpoint-ներն աշխատում են thread pool-ում, async def endpoint-ները՝ loop-ի վրա։ Django-ի async view-երն աշխատում են, և ORM-ը տրամադրում է async մեթոդներ (aget, acreate, async for), բայց բազայի շերտը դեռ հիմնականում աշխատում է thread-ում sync_to_async-ի միջոցով, ուստի async Django-ն հիմնականում օգուտ է տալիս I/O fan-out-ին և երկարատև կապերին, ոչ թե հարցումների մաքուր throughput-ին։ CPU-ով սահմանափակված աշխատանքին պետք են պրոցեսներ կամ native կոդ. Python 3.13-ը և 3.14-ը առաջարկում են ընտրովի free-threaded build, որը հեռացնում է GIL-ը, բայց գրադարանների աջակցությունը դեռ հասունանում է։
11Django deployment-ի մասշտաբավորում և ամրապնդում
Django-ն մասշտաբավորվում է հորիզոնական. հավելվածը stateless է, եթե session-ներն ու քեշը ապրում են Redis-ում կամ բազայում, ուստի load balancer-ի հետևում ավելացնում եք Gunicorn պրոցեսներ և սերվերներ։ Առաջին նեղ տեղը դառնում է կապերի քանակը. սահմանեք CONN_MAX_AGE մշտական կապերի համար, կամ օգտագործեք Django 5.1+-ի native PostgreSQL connection pooling-ը, կամ PgBouncer բազայի առջև։ Թանկ ֆրագմենտներն ու view-երը քեշավորեք Redis-ի վրա հիմնված cache framework-ով։ Read replica-ները աջակցվում են database router-ների միջոցով։ Անվտանգության առումով միացրեք SECURE_SSL_REDIRECT, SESSION_COOKIE_SECURE, CSRF_COOKIE_SECURE և HSTS, SECURE_PROXY_SSL_HEADER-ը կարգավորեք միայն այն դեպքում, երբ ձեր proxy-ն սահմանում է այդ header-ը, և ադմինը պահեք անսովոր URL-ի հետևում MFA-ով։
# settings/production.py (excerpt)
DEBUG = False
ALLOWED_HOSTS = ["example.com"]
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
DATABASES["default"]["CONN_MAX_AGE"] = 60
CACHES = {"default": {"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1"}}
Իրական իրավիճակներ
Թողարկումից հետո ապրանքների ցուցակի էջը դանդաղեց
Խնդիր
Django-ի ցուցակի էջը, որը բեռնվում էր 200 ms-ում, հիմա տևում է երեք վայրկյան։ Ենթակառուցվածքում ոչինչ չի փոխվել, բայց template-ն այժմ ցույց է տալիս յուրաքանչյուր ապրանքի կատեգորիան և մատակարարին։
Ինչպես լուծել
Լոկալ տեղադրեք Django Debug Toolbar և բացեք էջը. այն ցույց կտա հարյուրավոր գրեթե նույնական հարցումներ՝ N+1 օրինաչափությունը։ View-ի QuerySet-ին ավելացրեք select_related('category', 'supplier'), իսկ many-to-many tag-երի համար՝ prefetch_related։ Օգտագործեք only() կամ defer(), եթե աղյուսակն ունի մեծ տեքստային սյունակներ, որոնք ցուցակին պետք չեն։ Ավելացրեք թեստ assertNumQueries-ով, որպեսզի քանակը չկարողանա ռեգրեսիա տալ։ Վերջում ստուգեք, որ ֆիլտրի և դասավորման սյունակներն ունեն կոմպոզիտ ինդեքս՝ հաստատված PostgreSQL-ում EXPLAIN ANALYZE-ով։ Էջը պետք է վերադառնա մի քանի հարցման։
ML մոդելի մատուցում գոյություն ունեցող Django հավելվածի կողքին
Խնդիր
Data թիմն ունի Python մոդել, որը պետք է HTTP-ով կանխատեսումներ տա ցածր latency-ով։ Հիմնական Django հավելվածը սինքրոն է և արդեն ծանրաբեռնված։
Ինչպես լուծել
Մոդելը տեղադրեք փոքր FastAPI ծառայության մեջ, ոչ թե Django-ի ներսում։ Բեռնեք մոդելը մեկ անգամ գործարկման ժամանակ lifespan handler-ով, սահմանեք Pydantic մուտքի և ելքի սխեմաներ և գործարկեք այն Uvicorn-ով՝ CPU-ին ու հիշողությանը համապատասխան worker-ներով։ Եթե inference-ը CPU-ով ծանր է, օգտագործեք def endpoint-ներ կամ process pool, որպեսզի event loop-ն ազատ մնա։ Django-ն կանչում է ծառայությունը HTTP-ով՝ կարճ timeout-ով և fallback-ով, կամ batch կանխատեսումները հերթագրում է Celery-ի միջոցով։ Այսպես մոդելի մասշտաբավորումը, deploy-երը և ձախողումները մնում են անկախ հիմնական պրոդուկտից։
Աշխատակիցներին back office է պետք մինչև հաջորդ շաբաթ
Խնդիր
Հաճախորդն ունի պատվերների, հաճախորդների և ապրանքների բազա, և աշխատակիցներին պետք է որոնել, խմբագրել և արտահանել տվյալները։ Custom ադմին UI-ի համար բյուջե չկա։
Ինչպես լուծել
Մոդելավորեք աղյուսակները Django-ում (կամ գոյություն ունեցող սխեմայի վրա օգտագործեք inspectdb), հետո յուրաքանչյուր մոդել գրանցեք ադմինում list_display, list_filter, search_fields և list_select_related-ով։ Ավելացրեք custom admin action-ներ արտահանման և կարգավիճակի փոփոխության համար, readonly_fields աուդիտի սյունակների համար և խմբեր մանրակրկիտ թույլտվություններով, որպեսզի support աշխատակիցները չկարողանան պատվերներ ջնջել։ Պաշտպանեք ադմինը HTTPS-ով, ոչ լռելյայն ուղիով և MFA-ով, օրինակ՝ django-otp փաթեթի միջոցով։ Սա օրերի ընթացքում տալիս է օգտագործելի, թույլտվություններին հետևող back office, իսկ custom UI-ը կարող է գալ ավելի ուշ, եթե պետք լինի։
Արագ ուղեցույց
- Նոր նախագիծ uv-ով
uv init app && uv add django && uv run django-admin startproject config .- Միգրացիաներ ստեղծել և կիրառել
python manage.py makemigrations && python manage.py migrate- Ցույց տալ միգրացիայի SQL-ը
python manage.py sqlmigrate catalog 0002- Ինտերակտիվ ORM shell
python manage.py shell- Խուսափել N+1-ից (FK / M2M)
Model.objects.select_related("fk").prefetch_related("m2m")- Production-ի պատրաստվածության ստուգում
python manage.py check --deploy- Django-ն Gunicorn-ով գործարկել
gunicorn config.wsgi:application --workers 4 --bind 127.0.0.1:8000- FastAPI-ն dev ռեժիմում գործարկել
uv add "fastapi[standard]" && uv run fastapi dev main.py- Python-ի lint և ֆորմատավորում
ruff check . --fix && ruff format .
Հարցեր և պատասխաններ
Q1Django՞, թե՞ FastAPI. ինչպե՞ս եք ընտրում։
Սկսում եմ նրանից, թե ինչ է պետք պրոդուկտին։ Եթե դա ամբողջական հավելված է՝ օգտատերերով, թույլտվություններով, back office-ով և բազմաթիվ ռելյացիոն տվյալներով, Django-ն հաղթում է, որովհետև ORM-ը, միգրացիաները, ադմինն ու auth-ը բոլորը տեղում են և լավ ինտեգրված։ Եթե դա կենտրոնացված JSON ծառայություն է, օրինակ՝ ML inference endpoint կամ բարձր զուգահեռությամբ ներքին API, FastAPI-ն ավելի հարմար է՝ լռելյայն async, Pydantic վալիդացիա և գեներացված OpenAPI փաստաթղթեր։ FastAPI-ի գինն այն է, որ մնացածը ինքներդ եք հավաքում։ Django Ninja-ն լավ միջին տարբերակ է, երբ ուզում եք FastAPI ոճի տիպավորում Django-ի վրա։
Q2Ի՞նչ է N+1 խնդիրը Django-ում և ինչպե՞ս եք այն լուծում։
Այն առաջանում է, երբ օբյեկտների ցուցակը բեռնում եք մեկ հարցմամբ, հետո դիմում յուրաքանչյուրի կապին, ինչը յուրաքանչյուր տողի համար լրացուցիչ հարցում է առաջացնում։ Հիսուն ապրանք template-ում product.category-ով դառնում է հիսունմեկ հարցում։ Լուծումը select_related-ն է ForeignKey և OneToOne կապերի համար, որն ավելացնում է SQL JOIN, և prefetch_related-ը many-to-many և հակադարձ կապերի համար, որը կատարում է մեկ լրացուցիչ հարցում և միացնում Python-ում։ Ստուգում եմ Django Debug Toolbar-ով կամ թեստերում assertNumQueries-ով, որպեսզի ռեգրեսիաները բռնվեն մինչև production։
Q3Ի՞նչ են միգրացիաները և ինչպե՞ս դրանք անվտանգ deploy անել։
Միգրացիաները տարբերակավորված Python ֆայլեր են, որոնք նկարագրում են սխեմայի փոփոխությունները. makemigrations-ը գեներացնում է դրանք մոդելների փոփոխություններից, իսկ migrate-ը կիրառում։ Դրանք ապրում են Git-ում և կատարվում հերթականությամբ։ Անվտանգ deploy-ի համար փոփոխությունները պահում եմ հետադարձ համատեղելի. սկզբում ավելացնում եմ nullable սյունակներ, լրացնում տվյալները, հետո հաջորդ թողարկումում դարձնում պարտադիր, և երբեք չեմ վերանվանում կամ ջնջում սյունակ, որը գործող կոդը դեռ կարդում է։ Մեծ աղյուսակները զգուշություն են պահանջում, որովհետև որոշ գործողություններ կողպում են աղյուսակը. PostgreSQL-ում ինդեքսներն ավելացնում եմ concurrently։ Միգրացիաները կատարվում են որպես deploy-ի առանձին քայլ՝ նախքան նոր կոդը տրաֆիկ ստանալը։
Q4Ո՞րն է տարբերությունը WSGI-ի և ASGI-ի միջև։
Երկուսն էլ Python ստանդարտներ են, թե ինչպես է վեբ սերվերը խոսում հավելվածի հետ։ WSGI-ն ավելի հին, սինքրոն ստանդարտն է. յուրաքանչյուր հարցում զբաղեցնում է worker-ը իր ամբողջ տևողությամբ, ինչը նորմալ է Gunicorn-ով մատուցվող դասական request-response հավելվածների համար։ ASGI-ն async հաջորդն է. մեկ պրոցեսը կարող է մշակել բազմաթիվ զուգահեռ կապեր I/O-ին սպասելիս, և այն աջակցում է WebSocket-ներ ու երկարատև կապեր։ FastAPI-ն միայն ASGI է. Django-ն աջակցում է երկուսն էլ և ունի async view-եր ու async ORM ինտերֆեյս, թեև ORM-ի մեծ մասը ներքուստ դեռ սինքրոն է աշխատում thread-ում։
Q5Ինչպե՞ս է Django-ն պաշտպանում տարածված վեբ հարձակումներից։
Մի քանի պաշտպանություն միացված է լռելյայն։ Template engine-ը escape է անում ելքը, ինչը արգելափակում է XSS-ի մեծ մասը։ CSRF middleware-ը պահանջում է token ոչ անվտանգ ձևերի հարցումներում։ ORM-ը պարամետրացնում է հարցումները, ուստի SQL injection լինում է միայն, եթե raw SQL եք կառուցում string ֆորմատավորմամբ։ Clickjacking-ից պաշտպանությունը սահմանում է X-Frame-Options, գաղտնաբառերի հեշավորումն օգտագործում է ուժեղ ալգորիթմներ, իսկ Django 6.0-ն ավելացրեց ներկառուցված Content Security Policy աջակցություն։ Ձեր գործն է DEBUG-ն անջատված պահել, սահմանել ALLOWED_HOSTS, միացնել secure cookie-ներ և HSTS HTTPS-ի հետևում, և գործարկել manage.py check --deploy՝ տեսնելու, թե ինչ է պակասում։
Q6Ինչպե՞ս եք գործարկում ֆոնային job-եր Python վեբ հավելվածներում։
Ամեն դանդաղ կամ անհուսալի բան, օրինակ՝ email-ներ, նկարների մշակում կամ երրորդ կողմի կանչեր, պետք է դուրս գա հարցումից։ Վաղեմի ընտրությունը Celery-ն է Redis-ով կամ RabbitMQ-ով որպես broker, որն աջակցում է կրկնակի փորձեր, ժամանակացույց և տարբեր հերթերի ուղղորդում։ RQ-ն և Dramatiq-ը ավելի պարզ այլընտրանքներ են։ Django 6.0-ն ավելացրեց ներկառուցված tasks framework, որը սահմանում է ստանդարտ API աշխատանք հերթագրելու համար, իսկ backend-ներն ապահովում են իրական worker-ը։ Գործիքից անկախ՝ task-երը պետք է լինեն idempotent, ստանան ID-ներ ամբողջական օբյեկտների փոխարեն և ունենան timeout-ներ ու կրկնակի փորձերի սահմաններ։
Սենիոր մակարդակի հարցեր
S1Ե՞րբ կբաժանեիք Django մոնոլիտը և ինչպե՞ս։
Միայն երբ կա կոնկրետ ճնշում. բաղադրիչ՝ խիստ տարբեր մասշտաբավորման կարիքներով, առանձին թիմ, որը արգելափակված է ընդհանուր թողարկումներից, կամ մեկուսացման խիստ պահանջ։ Բաժանելուց առաջ սահմաններ եմ ամրագրում մոնոլիտի ներսում. յուրաքանչյուր Django app տիրապետում է իր մոդելներին, մյուս app-երը կանչում են փոքր service մոդուլ՝ մոդելներն ուղղակի import անելու փոխարեն, և app-երի միջև foreign key-երը նվազագույնի են հասցվում։ Դա հետագայում առանձնացումը էժան է դարձնում։ Երբ մի մաս առանձնացվում է, այն ստանում է իր սեփական բազան և մոնոլիտի հետ խոսում է API-ի կամ իրադարձությունների միջոցով, ոչ թե ընդհանուր աղյուսակներով։ Հաճախ ճիշտ առաջին քայլը պարզապես ֆոնային աշխատանքը Celery worker-ներին հանելն է, իսկ թեժ ընթերցման ուղին՝ քեշին, ինչը հանում է ճնշման մեծ մասը՝ առանց բաշխված համակարգերի ծախսերի։
S2Ինչպե՞ս եք կատարում zero-downtime սխեմայի փոփոխություններ մեծ PostgreSQL աղյուսակի վրա Django-ում։
Հետևում եմ expand and contract մոտեցմանը։ Առաջին թողարկում. ավելացնում եմ նոր սյունակը որպես nullable՝ առանց աղյուսակը վերագրող default-ի, և deploy եմ անում կոդ, որը գրում է և՛ հին, և՛ նոր սյունակներում։ Հետո լրացնում եմ (backfill) փաթեթներով՝ management command-ով կամ task-ով, դանդաղեցված, որպեսզի replication lag չառաջանա։ Հաջորդ թողարկում. ընթերցումները տեղափոխում եմ նոր սյունակ, հետո ավելացնում NOT NULL՝ վալիդացված check constraint մոտեցմամբ։ Վերջնական թողարկում. ջնջում եմ հին սյունակը, երբ այլևս ոչ մի գործող կոդ այն չի կարդում։ Ինդեքսների համար օգտագործում եմ AddIndexConcurrently-ը django.contrib.postgres-ից՝ atomic = False միգրացիայի ներսում։ Յուրաքանչյուր ռիսկային միգրացիայի համար վերանայում եմ sqlmigrate-ի ելքը և սահմանում lock_timeout, որպեսզի արգելափակված ALTER-ը արագ ձախողվի՝ ամբողջ տրաֆիկը իր հետևում հերթագրելու փոխարեն։
S3Արժե՞ արդյոք async Django-ն, թե՞ բարձր զուգահեռությամբ աշխատանքը պետք է գնա FastAPI-ին։
Կախված է նրանից, թե ուր է գնում ժամանակը։ Async Django-ն օգտակար է մի քանի արտաքին API-ների fan-out-ի, streaming պատասխանների և Channels-ի միջոցով WebSocket-ների համար՝ պահպանելով ադմինը, ORM-ն ու auth-ը։ Բայց ORM-ի async API-ն հիմնականում սինքրոն դրայվերները փաթեթավորում է thread-երում, իսկ sync middleware խառնելը context switch-եր է պարտադրում, ուստի հիմնականում բազայի վրա հիմնված workload-ը քիչ բան է շահում լավ կարգավորված Gunicorn worker-ների համեմատ։ FastAPI-ն async դրայվերով, օրինակ՝ asyncpg, տալիս է իսկապես չարգելափակող բազայի I/O և հարցման ավելի ցածր overhead, բայց auth-ը, ադմինն ու միգրացիաները ինքներդ եք կառուցում։ Իմ կանոնը. պրոդուկտը պահել Django-ում, և կոնկրետ բարձր զուգահեռությամբ endpoint-ները FastAPI ծառայության մեջ հանել միայն այն ժամանակ, երբ չափումները ցույց են տալիս, որ նեղ տեղը sync worker-ներն են։
S4Ինչպե՞ս կապահովեիք մոբայլ հավելվածի և SPA-ի կողմից օգտագործվող DRF API-ի անվտանգությունը։
Նույն կայքում գտնվող first-party SPA-ի համար ամենապարզը session authentication-ն է CSRF պաշտպանությամբ և secure, HttpOnly, SameSite cookie-ներով, ինչը թույլ է տալիս token-ները JavaScript-ում չպահել։ Մոբայլ հավելվածի համար ավելի լավ են աշխատում կարճատև access token-ները refresh token-ներով (օրինակ՝ djangorestframework-simplejwt-ով կամ OAuth2 provider-ով)՝ ռոտացիայով և logout-ի ժամանակ չեղարկմամբ։ Յուրաքանչյուր viewset ստանում է բացահայտ permission կլասներ և օբյեկտի մակարդակի ստուգումներ, իդեալում՝ սահմանափակված queryset-ներ, ինչպես Order.objects.filter(owner=request.user), որպեսզի այլ օգտատերերի տվյալներն անհասանելի լինեն ըստ դիզայնի։ Ավելացրեք throttling ըստ օգտատիրոջ և ըստ IP-ի, serializer-ի խիստ վալիդացիա, էջավորման սահմաններ, CORS՝ սահմանափակված հայտնի origin-ներով, և auth ձախողումների լոգավորում։ Վերջում թեստավորեք թույլտվության ուղիները, ոչ միայն happy path-ը։
Բառարան
- Virtual environment
- Մեկուսացված թղթապանակ՝ իր Python ինտերպրետատորով և փաթեթներով, որպեսզի նախագծերը չկիսեն և չկոտրեն միմյանց կախվածությունները։
- ORM
- Object-relational mapper. թույլ է տալիս բազայի տողերը հարցնել և պահել որպես Python օբյեկտներ՝ SQL ձեռքով գրելու փոխարեն։
- QuerySet
- Django-ի ծույլ, շղթայվող ներկայացումը բազայի հարցման. այն կատարվում է միայն այն ժամանակ, երբ արդյունքները պետք են։
- Միգրացիա
- Տարբերակավորված ֆայլ, որը նկարագրում է սխեմայի փոփոխությունը և կիրառվում հերթականությամբ, որպեսզի ամեն բազա համաժամեցված մնա մոդելների հետ։
- Serializer (DRF)
- Մոդելի նմուշները վերածում է JSON-ի և վալիդացնում մուտքային տվյալները՝ նախքան բազա հասնելը։
- Pydantic
- Գրադարան, որը Python type hints-ով վալիդացնում և վերլուծում է տվյալները. FastAPI-ի հարցումների մշակման միջուկն է։
- WSGI / ASGI
- Python հավելվածների և վեբ սերվերների միջև ստանդարտ ինտերֆեյսներ. WSGI-ն սինքրոն է, ASGI-ն աջակցում է async և WebSocket-ներ։
- GIL
- Global Interpreter Lock. ստանդարտ CPython build-երում թույլ է տալիս միաժամանակ միայն մեկ thread-ի կատարել Python bytecode։
Շարունակեք սովորել
Պրոդուկտի գաղափար ունե՞ք։Եկեք ստեղծենք այն։
Պատմեք ձեր գաղափարի, պրոդուկտի կամ թիմի մասին։ Սովորաբար պատասխանում եմ մեկ աշխատանքային օրվա ընթացքում։