← Назад к курсу
Пособие по созданию веб-приложений и веб-сервисов на Python
Оглавление
- Проектирование веб-приложений
- Выбор фреймворков и библиотек
- Архитектурные подходы
- Примеры приложений
- Деплой и DevOps
1. Проектирование веб-приложений
1.1. Этапы проектирования
-
Анализ требований
- Определение целевой аудитории
- Функциональные и нефункциональные требования
- Ограничения и ресурсы
-
Проектирование архитектуры
# Пример структуры проекта my_project/ ├── app/ │ ├── __init__.py │ ├── models.py # Модели данных │ ├── views.py # Логика представлений │ ├── routes.py # Маршруты │ └── services.py # Бизнес-логика ├── config.py ├── requirements.txt └── run.py
-
Выбор базы данных
- PostgreSQL - для сложных данных
- MySQL - проверенное решение
- SQLite - для прототипов
- MongoDB - для документоориентированных данных
- Redis - для кэширования
-
Проектирование API
# Пример RESTful дизайна # GET /api/users - список пользователей # POST /api/users - создание пользователя # GET /api/users/{id} - получение пользователя # PUT /api/users/{id} - обновление пользователя # DELETE /api/users/{id} - удаление пользователя
2. Выбор фреймворков и библиотек
2.1. Основные фреймворки
| Фреймворк | Назначение | Преимущества |
|---|---|---|
| Django | Полноценные веб-приложения | Батарейки в комплекте, ORM, админка |
| Flask | Микросервисы, API, прототипы | Легковесный, гибкий |
| FastAPI | Современные API | Высокая скорость, автодокументация |
| Tornado | Асинхронные приложения | Высокая производительность |
2.2. Библиотеки для Django
# requirements.txt для Django-проекта Django>=4.0 djangorestframework # REST API django-crispy-forms # Формы django-filter # Фильтрация django-debug-toolbar # Отладка psycopg2-binary # PostgreSQL драйвер celery # Асинхронные задачи redis # Кэширование
2.3. Библиотеки для Flask/FastAPI
# requirements.txt для API-проекта Flask # или FastAPI Flask-SQLAlchemy # ORM Flask-Migrate # Миграции Flask-JWT-Extended # Аутентификация Flask-CORS # CORS поддержка Pydantic # Валидация данных (для FastAPI) uvicorn # ASGI сервер
2.4. Универсальные библиотеки
# Общие зависимости SQLAlchemy # ORM Alembic # Миграции базы данных Pillow # Работа с изображениями python-dotenv # Переменные окружения requests # HTTP-запросы python-jose # JWT токены passlib # Хэширование паролей
3. Архитектурные подходы
3.1. MVC (Model-View-Controller)
# Django пример MVC
# models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
# views.py
from django.views.generic import ListView
from .models import Post
class PostListView(ListView):
model = Post
template_name = 'blog/post_list.html'
# post_list.html (Template)
{% for post in object_list %}
<h2>{{ post.title }}</h2>
<p>{{ post.content }}</p>
{% endfor %}
3.2. Микросервисная архитектура
# service_a/api.py (FastAPI)
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/{user_id}")
async def get_user(user_id: int):
return {"user_id": user_id}
# service_b/api.py (Flask)
from flask import Flask, jsonify
import requests
app = Flask(__name__)
@app.route('/profile/<int:user_id>')
def get_profile(user_id):
# Запрос к service_a
user_data = requests.get(f'http://service_a:8000/users/{user_id}')
return jsonify(user_data.json())
3.3. Репозиторий + Сервисный слой
# repository.py
class UserRepository:
def __init__(self, session):
self.session = session
def get_by_id(self, user_id):
return self.session.query(User).filter_by(id=user_id).first()
# service.py
class UserService:
def __init__(self, repository):
self.repository = repository
def get_user_profile(self, user_id):
user = self.repository.get_by_id(user_id)
if not user:
raise ValueError("User not found")
return {
"id": user.id,
"name": user.name,
"email": user.email
}
4. Примеры приложений
4.1. Блог на Django
# models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=100)
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# views.py
from django.views.generic import ListView, DetailView, CreateView
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import Post
class PostListView(ListView):
model = Post
paginate_by = 10
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'content', 'category']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
4.2. REST API сервис на FastAPI
# main.py
from fastapi import FastAPI, HTTPException, Depends
from sqlalchemy.orm import Session
from typing import List
import models, schemas, crud
from database import SessionLocal, engine
models.Base.metadata.create_all(bind=engine)
app = FastAPI()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
db_user = crud.get_user_by_email(db, email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return crud.create_user(db=db, user=user)
@app.get("/users/", response_model=List[schemas.User])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
users = crud.get_users(db, skip=skip, limit=limit)
return users
# schemas.py
from pydantic import BaseModel, EmailStr
class UserBase(BaseModel):
email: EmailStr
name: str
class UserCreate(UserBase):
password: str
class User(UserBase):
id: int
class Config:
orm_mode = True
4.3. CRM система на Flask
# app.py
from flask import Flask, render_template, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, login_required, current_user
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///crm.db'
db = SQLAlchemy(app)
login_manager = LoginManager(app)
# Модели
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), unique=True)
password = db.Column(db.String(200))
class Client(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100))
email = db.Column(db.String(120))
phone = db.Column(db.String(20))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
# Маршруты
@app.route('/')
@login_required
def dashboard():
return render_template('dashboard.html')
@app.route('/api/clients', methods=['GET', 'POST'])
@login_required
def clients():
if request.method == 'POST':
data = request.json
client = Client(
name=data['name'],
email=data['email'],
phone=data['phone'],
user_id=current_user.id
)
db.session.add(client)
db.session.commit()
return jsonify({'message': 'Client created'}), 201
clients = Client.query.filter_by(user_id=current_user.id).all()
return jsonify([{
'id': c.id,
'name': c.name,
'email': c.email,
'phone': c.phone
} for c in clients])
# templates/dashboard.html
"""
<!DOCTYPE html>
<html>
<head>
<title>CRM Dashboard</title>
</head>
<body>
<h1>Welcome, {{ current_user.email }}</h1>
<div id="clients-list"></div>
<script>
fetch('/api/clients')
.then(response => response.json())
.then(data => {
const container = document.getElementById('clients-list');
data.forEach(client => {
container.innerHTML += `<p>${client.name} - ${client.email}</p>`;
});
});
</script>
</body>
</html>
"""
4.4. Real-time приложение с WebSockets
# websocket_app.py
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from typing import List
app = FastAPI()
class ConnectionManager:
def __init__(self):
self.active_connections: List[WebSocket] = []
async def connect(self, websocket: WebSocket):
await websocket.accept()
self.active_connections.append(websocket)
def disconnect(self, websocket: WebSocket):
self.active_connections.remove(websocket)
async def send_personal_message(self, message: str, websocket: WebSocket):
await websocket.send_text(message)
async def broadcast(self, message: str):
for connection in self.active_connections:
await connection.send_text(message)
manager = ConnectionManager()
@app.websocket("/ws/{client_id}")
async def websocket_endpoint(websocket: WebSocket, client_id: int):
await manager.connect(websocket)
try:
while True:
data = await websocket.receive_text()
await manager.broadcast(f"Client #{client_id}: {data}")
except WebSocketDisconnect:
manager.disconnect(websocket)
await manager.broadcast(f"Client #{client_id} left")
5. Деплой и DevOps
5.1. Конфигурация для продакшена
# config/production.py
import os
class ProductionConfig:
SECRET_KEY = os.environ.get('SECRET_KEY')
DEBUG = False
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
SQLALCHEMY_TRACK_MODIFICATIONS = False
# Настройки безопасности
SESSION_COOKIE_SECURE = True
REMEMBER_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
# Логирование
LOG_LEVEL = 'WARNING'
5.2. Docker конфигурация
# Dockerfile FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD ["gunicorn", "--workers", "4", "--bind", "0.0.0.0:8000", "app:app"]
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:password@db:5432/app_db
depends_on:
- db
- redis
db:
image: postgres:15
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: app_db
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- web
volumes:
postgres_data:
5.3. CI/CD конфигурация (GitHub Actions)
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run tests
run: |
pytest
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- name: Deploy to server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /var/www/myapp
git pull origin main
docker-compose down
docker-compose build
docker-compose up -d
5.4. Мониторинг и логирование
# logging_config.py
import logging
from logging.handlers import RotatingFileHandler
def setup_logging(app):
# Файловый обработчик
file_handler = RotatingFileHandler(
'app.log',
maxBytes=10000,
backupCount=3
)
file_handler.setLevel(logging.WARNING)
# Форматтер
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
file_handler.setFormatter(formatter)
app.logger.addHandler(file_handler)
# Sentry для продакшена
if app.config['ENV'] == 'production':
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration
sentry_sdk.init(
dsn=app.config['SENTRY_DSN'],
integrations=[FlaskIntegration()]
)
6. Рекомендации и лучшие практики
6.1. Безопасность
- Всегда валидируйте входные данные
- Используйте параметризованные запросы для SQL
- Хэшируйте пароли (bcrypt, argon2)
- Реализуйте rate limiting
- Используйте HTTPS в продакшене
- Регулярно обновляйте зависимости
6.2. Производительность
- Используйте кэширование (Redis, Memcached)
- Оптимизируйте запросы к базе данных
- Реализуйте пагинацию для больших наборов данных
- Используйте асинхронные задачи для долгих операций
- Минимифицируйте статические файлы
6.3. Масштабирование
- Разделяйте приложение на микросервисы при необходимости
- Используйте очереди сообщений (RabbitMQ, Kafka)
- Реализуйте горизонтальное масштабирование
- Используйте балансировщики нагрузки
6.4. Тестирование
# tests/test_api.py
import pytest
from app import create_app
@pytest.fixture
def client():
app = create_app({'TESTING': True})
with app.test_client() as client:
yield client
def test_create_user(client):
response = client.post('/api/users', json={
'email': 'test@example.com',
'password': 'secret'
})
assert response.status_code == 201
assert 'id' in response.json
Заключение
Это пособие охватывает основные аспекты разработки веб-приложений на Python. Ключевые моменты:
- Выбирайте фреймворк под задачу: Django для монолитов, FastAPI/Flask для API
- Проектируйте архитектуру заранее: учитывайте масштабируемость и поддержку
- Следуйте лучшим практикам: безопасность, тестирование, документация
- Автоматизируйте процессы: CI/CD, деплой, мониторинг
- Не изобретайте велосипед: используйте проверенные библиотеки и решения
Для углубленного изучения каждой темы рекомендуется:
- Официальная документация фреймворков
- Книга "Two Scoops of Django"
- Курсы на Coursera/Stepik по веб-разработке
- Изучение исходного кода популярных проектов на GitHub
Удачной разработки!