Ответ
В Django для разделения бизнес-логики от представлений (views) и моделей (models) принято использовать паттерн Service Layer (слой сервисов).
Это помогает избежать анти-паттернов Fat Models (толстые модели) и Fat Views (толстые представления), делая код более тестируемым, переиспользуемым и поддерживаемым. Логику выносят в отдельные файлы, например, services.py
внутри Django-приложения.
Пример
1. Файл с бизнес-логикой (services.py
):
# myapp/services.py
from typing import List, Dict
from django.db import transaction
from .models import Order, OrderItem, Product
class OrderCreationError(Exception):
pass
@transaction.atomic
def create_order(user, items_data: List[Dict]) -> Order:
"""
Создает заказ и связанные с ним позиции в одной транзакции.
Инкапсулирует логику валидации, расчета и сохранения.
"""
order = Order.objects.create(user=user, status=Order.Status.PENDING)
for item_data in items_data:
product_id = item_data.get('product_id')
quantity = item_data.get('quantity')
if not Product.objects.filter(id=product_id, is_active=True).exists():
raise OrderCreationError(f"Товар с ID {product_id} не найден или неактивен.")
OrderItem.objects.create(order=order, product_id=product_id, quantity=quantity)
# Здесь может быть дополнительная логика: расчет итоговой суммы, отправка уведомлений и т.д.
order.calculate_total_price()
order.save()
return order
2. Представление, которое вызывает сервис (views.py
):
# myapp/views.py
from django.http import JsonResponse
from . import services
def order_create_api(request):
# Данные обычно приходят из request.body в формате JSON
items_data = [{'product_id': 1, 'quantity': 2}]
user = request.user
try:
new_order = services.create_order(user=user, items_data=items_data)
return JsonResponse({"status": "ok", "order_id": new_order.id}, status=201)
except services.OrderCreationError as e:
return JsonResponse({"status": "error", "message": str(e)}, status=400)
Преимущества подхода:
- Тестируемость: Бизнес-логику в
services.py
можно тестировать изолированно, без HTTP-запросов. - Переиспользование: Сервисную функцию
create_order
можно вызвать из view, Celery-задачи или management-команды. - Принцип единственной ответственности (SRP): Views отвечают за HTTP, модели — за данные, а сервисы — за бизнес-процессы.
- Читаемость: Код представлений становится лаконичным и понятным.