Hız, Esneklik ve
Sonsuz Ekosistem
Python, dünya genelinde en hızlı büyüyen backend dili konumuna gelmiştir. Asyncio altyapısı üzerine inşa edilen FastAPI, Node.js ile karşılaştırılabilir performans sunarken type-hint'ler ve Pydantic validasyonu ile derleme-zamanı güvenliği sağlar. Django ise on yıllık battle-tested mimarisini sunmaktadır.
Production-Ready
Python Kodu
from __future__ import annotations
from uuid import UUID
from typing import Annotated
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_db
from app.core.security import get_current_active_user
from app.models.user import User
from app.schemas.order import OrderCreate, OrderResponse, OrderListResponse
from app.services.order_service import OrderService
from app.tasks.notifications import send_order_confirmation
router = APIRouter(prefix="/orders", tags=["orders"])
# Dependency type aliases
DBSession = Annotated[AsyncSession, Depends(get_db)]
CurrentUser = Annotated[User, Depends(get_current_active_user)]
@router.post(
"/",
response_model=OrderResponse,
status_code=status.HTTP_201_CREATED,
summary="Yeni sipariş oluştur",
)
async def create_order(
payload: OrderCreate,
db: DBSession,
current_user: CurrentUser,
background_tasks: BackgroundTasks,
order_service: Annotated[OrderService, Depends()],
) -> OrderResponse:
"""
Yeni bir sipariş oluşturur ve onay e-postasını arka planda gönderir.
- **payload**: Sipariş satırları, teslimat adresi ve ödeme yöntemi
- **current_user**: JWT bearer token ile doğrulanmış kullanıcı
"""
# Stok kontrolü ve fiyat doğrulaması servis katmanında yapılır
try:
order = await order_service.create(
db=db,
user_id=current_user.id,
data=payload,
)
except ValueError as exc:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=str(exc),
) from exc
# E-posta gönderimiyle HTTP yanıtı arasında bağlantı kurma
background_tasks.add_task(
send_order_confirmation,
order_id=order.id,
email=current_user.email,
)
return OrderResponse.model_validate(order)
@router.get(
"/{order_id}",
response_model=OrderResponse,
summary="Sipariş detayı getir",
)
async def get_order(
order_id: UUID,
db: DBSession,
current_user: CurrentUser,
order_service: Annotated[OrderService, Depends()],
) -> OrderResponse:
order = await order_service.get_by_id(db=db, order_id=order_id)
if order is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Sipariş bulunamadı.",
)
# Kullanıcı yalnızca kendi siparişlerine erişebilir
if order.user_id != current_user.id and not current_user.is_admin:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Bu siparişe erişim yetkiniz yok.",
)
return OrderResponse.model_validate(order)
@router.get(
"/",
response_model=OrderListResponse,
summary="Kullanıcının siparişlerini listele",
)
async def list_orders(
db: DBSession,
current_user: CurrentUser,
order_service: Annotated[OrderService, Depends()],
page: int = 1,
page_size: int = 20,
) -> OrderListResponse:
orders, total = await order_service.list_for_user(
db=db,
user_id=current_user.id,
page=page,
page_size=page_size,
)
return OrderListResponse(
items=[OrderResponse.model_validate(o) for o in orders],
total=total,
page=page,
page_size=page_size,
)
from __future__ import annotations
import enum
from datetime import datetime
from decimal import Decimal
from uuid import UUID, uuid4
from sqlalchemy import (
ForeignKey, Numeric, String, Text,
Enum as SAEnum, func,
)
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import (
DeclarativeBase, Mapped,
mapped_column, relationship,
)
class Base(DeclarativeBase):
pass
class OrderStatus(str, enum.Enum):
PENDING = "pending"
CONFIRMED = "confirmed"
SHIPPED = "shipped"
DELIVERED = "delivered"
CANCELLED = "cancelled"
class Order(Base):
__tablename__ = "orders"
id: Mapped[UUID] = mapped_column(default=uuid4, primary_key=True)
user_id: Mapped[UUID] = mapped_column(ForeignKey("users.id"), index=True)
status: Mapped[OrderStatus] = mapped_column(
SAEnum(OrderStatus, name="order_status_enum"),
default=OrderStatus.PENDING,
nullable=False,
)
shipping_address: Mapped[str] = mapped_column(Text, nullable=False)
notes: Mapped[str | None] = mapped_column(Text)
created_at: Mapped[datetime] = mapped_column(server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(
server_default=func.now(), onupdate=func.now()
)
# İlişkiler
user: Mapped["User"] = relationship("User", back_populates="orders")
items: Mapped[list["OrderItem"]] = relationship(
"OrderItem", back_populates="order", cascade="all, delete-orphan"
)
@hybrid_property
def total_amount(self) -> Decimal:
"""Sipariş toplam tutarını satır bazında hesaplar."""
return sum(item.line_total for item in self.items)
class OrderItem(Base):
__tablename__ = "order_items"
id: Mapped[UUID] = mapped_column(default=uuid4, primary_key=True)
order_id: Mapped[UUID] = mapped_column(ForeignKey("orders.id"), index=True)
product_id: Mapped[UUID] = mapped_column(ForeignKey("products.id"))
sku: Mapped[str] = mapped_column(String(64), nullable=False)
quantity: Mapped[int] = mapped_column(nullable=False)
unit_price: Mapped[Decimal] = mapped_column(Numeric(12, 2), nullable=False)
order: Mapped["Order"] = relationship("Order", back_populates="items")
product: Mapped["Product"] = relationship("Product")
@hybrid_property
def line_total(self) -> Decimal:
return self.unit_price * self.quantity
# ─── Repository Pattern ────────────────────────────────────────────────────────
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
class OrderRepository:
"""Veritabanı erişimini soyutlayan repository sınıfı."""
def __init__(self, session: AsyncSession) -> None:
self._session = session
async def find_by_id(self, order_id: UUID) -> Order | None:
stmt = (
select(Order)
.where(Order.id == order_id)
.options(
selectinload(Order.items).selectinload(OrderItem.product),
selectinload(Order.user),
)
)
result = await self._session.execute(stmt)
return result.scalar_one_or_none()
async def find_by_user(
self,
user_id: UUID,
*,
offset: int = 0,
limit: int = 20,
) -> tuple[list[Order], int]:
count_stmt = (
select(func.count())
.select_from(Order)
.where(Order.user_id == user_id)
)
total = (await self._session.execute(count_stmt)).scalar_one()
stmt = (
select(Order)
.where(Order.user_id == user_id)
.options(selectinload(Order.items))
.order_by(Order.created_at.desc())
.offset(offset)
.limit(limit)
)
rows = (await self._session.execute(stmt)).scalars().all()
return list(rows), total
async def save(self, order: Order) -> Order:
self._session.add(order)
await self._session.flush()
await self._session.refresh(order)
return order
from __future__ import annotations
import logging
from uuid import UUID
from celery import chord, group, shared_task
from celery.signals import task_failure, task_success
from celery.utils.log import get_task_logger
from app.core.celery_app import celery_app
from app.core.database import SyncSessionLocal
from app.models.order import Order, OrderStatus
from app.services.inventory_service import InventoryService
from app.services.payment_service import PaymentService
from app.services.notification_service import NotificationService
logger = get_task_logger(__name__)
@celery_app.task(
bind=True,
name="orders.process",
max_retries=3,
default_retry_delay=60, # İlk yeniden deneme: 60 saniye
autoretry_for=(Exception,),
retry_backoff=True, # Üstel geri çekilme: 60, 120, 240 sn
retry_backoff_max=600,
acks_late=True, # Görev tamamlanana kadar mesajı koru
reject_on_worker_lost=True,
)
def process_order(self, order_id: str) -> dict:
"""
Siparişi paralel alt görevlerle işler:
1. Stok rezervasyonu ─╮
2. Ödeme tahsilatı ─┤─► Hepsi başarılıysa kargo bildirimi
3. Fatura oluşturma ─╯
"""
logger.info("Sipariş işleniyor: %s | deneme=%d", order_id, self.request.retries)
with SyncSessionLocal() as db:
order = db.get(Order, UUID(order_id))
if order is None:
logger.error("Sipariş bulunamadı: %s", order_id)
return {"status": "not_found", "order_id": order_id}
if order.status != OrderStatus.PENDING:
logger.warning("Beklenmeyen durum %s, atlanıyor.", order.status)
return {"status": "skipped", "order_id": order_id}
# chord: tüm header görevleri bitmeden callback çalışmaz
workflow = chord(
group(
reserve_stock.s(order_id),
charge_payment.s(order_id),
generate_invoice.s(order_id),
),
finalize_order.s(order_id=order_id),
)
result = workflow.delay()
return {"status": "dispatched", "chord_id": result.id}
@shared_task(name="orders.reserve_stock", acks_late=True)
def reserve_stock(order_id: str) -> dict:
"""Depoda stok rezervasyonu yapar."""
with SyncSessionLocal() as db:
service = InventoryService(db)
reserved = service.reserve_for_order(UUID(order_id))
logger.info("Stok rezerve edildi: %s items=%d", order_id, len(reserved))
return {"task": "reserve_stock", "reserved_count": len(reserved)}
@shared_task(name="orders.charge_payment", acks_late=True)
def charge_payment(order_id: str) -> dict:
"""Ödeme servisine tahsilat isteği gönderir."""
with SyncSessionLocal() as db:
service = PaymentService(db)
transaction_id = service.charge(UUID(order_id))
logger.info("Ödeme tahsil edildi: %s txn=%s", order_id, transaction_id)
return {"task": "charge_payment", "transaction_id": transaction_id}
@shared_task(name="orders.generate_invoice", acks_late=True)
def generate_invoice(order_id: str) -> dict:
"""PDF fatura oluşturur ve S3'e yükler."""
with SyncSessionLocal() as db:
service = NotificationService(db)
invoice_url = service.generate_invoice_pdf(UUID(order_id))
return {"task": "generate_invoice", "invoice_url": invoice_url}
@shared_task(name="orders.finalize")
def finalize_order(subtask_results: list[dict], *, order_id: str) -> dict:
"""Tüm alt görevler başarılıysa siparişi CONFIRMED'e alır."""
with SyncSessionLocal() as db:
order = db.get(Order, UUID(order_id))
order.status = OrderStatus.CONFIRMED
db.commit()
logger.info("Sipariş onaylandı: %s sonuçlar=%s", order_id, subtask_results)
return {"status": "confirmed", "order_id": order_id}
# ─── Sinyal Kancaları ──────────────────────────────────────────────────────────
@task_failure.connect(sender=process_order)
def on_order_processing_failure(
sender, task_id, exception, args, kwargs, traceback, einfo, **kw
):
logging.getLogger(__name__).critical(
"Sipariş işleme başarısız | task_id=%s hata=%s", task_id, exception
)
@task_success.connect(sender=process_order)
def on_order_processing_success(sender, result, **kw):
logging.getLogger(__name__).info(
"Sipariş başarıyla işlendi | sonuç=%s", result
)
from __future__ import annotations
from django.db.models import Prefetch
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters, serializers, status, viewsets
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
from shop.models import Product, ProductImage, Review, Category
from shop.serializers import (
ProductDetailSerializer,
ProductListSerializer,
ProductWriteSerializer,
ReviewSerializer,
)
from shop.permissions import IsOwnerOrAdmin
from shop.tasks import rebuild_search_index
# ─── Serializer ───────────────────────────────────────────────────────────────
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ["id", "name", "slug"]
class ProductImageSerializer(serializers.ModelSerializer):
class Meta:
model = ProductImage
fields = ["id", "url", "alt_text", "is_primary"]
class ProductDetailSerializer(serializers.ModelSerializer):
category = CategorySerializer(read_only=True)
images = ProductImageSerializer(many=True, read_only=True)
reviews_count = serializers.IntegerField(read_only=True)
average_rating = serializers.DecimalField(
max_digits=3, decimal_places=2, read_only=True
)
class Meta:
model = Product
fields = [
"id", "name", "slug", "description",
"price", "stock", "category", "images",
"reviews_count", "average_rating",
"is_active", "created_at",
]
# ─── ViewSet ──────────────────────────────────────────────────────────────────
class ProductViewSet(viewsets.ModelViewSet):
"""
Ürün CRUD işlemleri ve özel aksiyonlar.
list → herkes erişebilir
detail → herkes erişebilir
create/update/destroy → sadece admin
reviews → kimlik doğrulaması gerekli
bulk_activate → admin aksiyonu
"""
queryset = (
Product.objects
.select_related("category", "category__parent")
.prefetch_related(
Prefetch(
"images",
queryset=ProductImage.objects.filter(is_active=True).order_by("-is_primary"),
),
"reviews__user",
)
.filter(is_deleted=False)
)
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ["category__slug", "is_active", "price"]
search_fields = ["name", "description", "sku"]
ordering_fields = ["price", "created_at", "name"]
ordering = ["-created_at"]
throttle_classes = [UserRateThrottle]
def get_serializer_class(self):
if self.action == "list":
return ProductListSerializer
if self.action in ("create", "update", "partial_update"):
return ProductWriteSerializer
return ProductDetailSerializer
def get_permissions(self):
if self.action in ("create", "update", "partial_update", "destroy", "bulk_activate"):
return [IsAdminUser()]
if self.action in ("add_review", "reviews"):
return [IsAuthenticated()]
return []
def perform_create(self, serializer: ProductWriteSerializer) -> None:
product = serializer.save(created_by=self.request.user)
rebuild_search_index.delay(product_id=str(product.pk))
def perform_destroy(self, instance: Product) -> None:
# Fiziksel silme yerine soft-delete
instance.is_deleted = True
instance.save(update_fields=["is_deleted", "updated_at"])
# ─── Özel Aksiyonlar ──────────────────────────────────────────────────────
@action(detail=True, methods=["get", "post"], url_path="reviews")
def reviews(self, request: Request, pk: int | None = None) -> Response:
product = self.get_object()
if request.method == "GET":
qs = product.reviews.select_related("user").order_by("-created_at")
serializer = ReviewSerializer(qs, many=True, context={"request": request})
return Response(serializer.data)
# POST: Yeni yorum ekle
serializer = ReviewSerializer(
data=request.data,
context={"request": request, "product": product},
)
serializer.is_valid(raise_exception=True)
serializer.save(user=request.user, product=product)
return Response(serializer.data, status=status.HTTP_201_CREATED)
@action(detail=False, methods=["post"], url_path="bulk-activate")
def bulk_activate(self, request: Request) -> Response:
"""Birden fazla ürünü tek istekte aktifleştirir."""
ids = request.data.get("ids", [])
if not ids:
return Response(
{"detail": "Ürün ID listesi boş olamaz."},
status=status.HTTP_400_BAD_REQUEST,
)
updated = (
Product.objects
.filter(pk__in=ids, is_deleted=False)
.update(is_active=True)
)
return Response({"activated_count": updated})
Kullandığımız
Python Kütüphaneleri
ASGI tabanlı modern web framework. Otomatik OpenAPI dokümantasyonu, dependency injection ve yüksek performanslı async endpoint'ler.
Mapped[] ve mapped_column() ile tam tip güvenlikli async ORM. Repository pattern ve Unit of Work desteğiyle entegre edilebilir.
Rust tabanlı core ile 5–50x daha hızlı validasyon. Schema oluşturma, JSON serialization ve settings management için endüstri standardı.
Dağıtık görev kuyruğu; chord, group, chain primitifleriyle karmaşık iş akışları. Retry, backoff ve dead-letter queue desteği.
On yıllık battle-tested web framework. ORM, admin paneli, auth sistemi, ve Django REST Framework ile eksiksiz API geliştirme platformu.
SQLAlchemy için veritabanı migrasyon aracı. Otomatik diff oluşturma, downgrade desteği ve multi-branch migration yönetimi.
Async endpoint'ler için pytest desteği. httpx AsyncClient ile entegrasyon testleri, factory_boy ile test fixture yönetimi.
Async/sync HTTP istemcisi. Requests API uyumlu, HTTP/2 desteği, connection pooling ve retry stratejileriyle üçüncü taraf API entegrasyonu.
Python ile Neler
Geliştiriyoruz?
REST API Backend
FastAPI ile async-first, yüksek performanslı REST ve GraphQL API'leri. Pydantic doğrulaması, JWT auth ve otomatik OpenAPI dokümantasyonu.
Otomasyon Scriptleri
Periyodik raporlama, dosya işleme, e-posta/bildirim otomasyonu ve ERP/CRM sistemleriyle entegrasyon scriptleri.
Veri İşleme Pipeline
Pandas, Polars ve DuckDB ile büyük veri dönüşüm pipeline'ları. Celery ile paralel iş akışları ve zamanlanmış batch işlemler.
Web Scraping & ETL
Scrapy, Playwright ve Beautiful Soup ile web kazıma; extract, transform ve load süreçlerini otomatikleştiren ETL sistemleri.
Microservice Servisleri
FastAPI + Docker ile bağımsız ölçeklenebilir microservice'ler. gRPC, message queue ve event-driven mimari entegrasyonu.
ML Model Entegrasyonu
Eğitilmiş makine öğrenmesi modellerini production ortamına taşıma. FastAPI inference endpoint'leri, model versiyonlama ve A/B test altyapısı.
Python ile projenizi hayata geçirelim
FastAPI'den Django'ya, Celery'den ML entegrasyonuna kadar tüm Python ekosisteminde uzman ekibimizle çalışın.
Ücretsiz Danışmanlık Alın