- Home »

Создание веб-приложения с Flask на Python 3
Сейчас веб-разработка стала одним из самых востребованных направлений в IT. Каждый день появляются новые стартапы, компании переходят в онлайн, а пользователи требуют всё более сложные веб-сервисы. В этой статье мы разберем, как создать веб-приложение с Flask на Python 3 — от нуля до боевого развертывания. Если ты администратор, который хочет быстро поднять легковесный веб-сервис, или разработчик, ищущий альтернативу громоздким фреймворкам, то этот материал для тебя. Здесь будут конкретные команды, примеры кода и готовые решения для продакшена.
Что такое Flask и зачем он нужен
Flask — это микрофреймворк для создания веб-приложений на Python. В отличие от Django, который навязывает свою структуру проекта, Flask дает полную свободу в выборе архитектуры. Он идеально подходит для API, микросервисов, прототипов и небольших веб-приложений.
Основные преимущества Flask:
- Минималистичность — весит всего несколько мегабайт
- Гибкость — можешь подключить только нужные компоненты
- Простота изучения — базовое приложение пишется в 5 строк
- Активное сообщество — множество расширений и документации
- Подходит для RESTful API и микросервисной архитектуры
Сравнение с другими фреймворками
Фреймворк | Размер | Кривая обучения | Производительность | Лучше всего для |
---|---|---|---|---|
Flask | Легкий | Низкая | Высокая | API, микросервисы |
Django | Тяжелый | Высокая | Средняя | Крупные проекты |
FastAPI | Легкий | Средняя | Очень высокая | Современные API |
Tornado | Средний | Высокая | Высокая | Асинхронные приложения |
Установка и подготовка окружения
Для работы с Flask потребуется Python 3.7+ и виртуальное окружение. Если планируешь деплоить на продакшн-сервер, рекомендую сразу арендовать VPS или выделенный сервер.
# Создание виртуального окружения
python3 -m venv flask_env
source flask_env/bin/activate # Linux/Mac
# flask_env\Scripts\activate # Windows
# Установка Flask
pip install Flask
# Для продакшена также понадобятся:
pip install gunicorn
pip install python-dotenv
Создание базового приложения
Создадим простейшее Flask-приложение. Сохрани код в файл app.py
:
from flask import Flask, request, jsonify
import os
from datetime import datetime
app = Flask(__name__)
# Базовый маршрут
@app.route('/')
def hello():
return f'Flask работает!
Текущее время: {datetime.now()}
'
# API endpoint
@app.route('/api/status')
def api_status():
return jsonify({
'status': 'ok',
'timestamp': datetime.now().isoformat(),
'server': os.uname().nodename if hasattr(os, 'uname') else 'unknown'
})
# POST запрос
@app.route('/api/echo', methods=['POST'])
def echo():
data = request.get_json()
if not data:
return jsonify({'error': 'No JSON data provided'}), 400
return jsonify({
'received': data,
'timestamp': datetime.now().isoformat()
})
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)
Запуск приложения:
python app.py
Приложение будет доступно по адресу http://localhost:5000
. Попробуй открыть в браузере или протестировать API:
# Тестирование API
curl http://localhost:5000/api/status
# POST запрос
curl -X POST http://localhost:5000/api/echo \
-H "Content-Type: application/json" \
-d '{"message": "Hello Flask!"}'
Структура проекта для продакшена
Для серьезного проекта нужна правильная структура. Создадим каталоги:
mkdir flask_project
cd flask_project
# Структура проекта
flask_project/
├── app/
│ ├── __init__.py
│ ├── routes.py
│ ├── models.py
│ └── config.py
├── templates/
├── static/
├── requirements.txt
├── .env
└── run.py
Файл app/__init__.py
:
from flask import Flask
from app.config import Config
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
from app.routes import bp
app.register_blueprint(bp)
return app
Файл app/config.py
:
import os
from dotenv import load_dotenv
load_dotenv()
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key-change-me'
DEBUG = os.environ.get('FLASK_DEBUG', 'False').lower() == 'true'
HOST = os.environ.get('FLASK_HOST', '0.0.0.0')
PORT = int(os.environ.get('FLASK_PORT', 5000))
Файл app/routes.py
:
from flask import Blueprint, jsonify, request
from datetime import datetime
import psutil # pip install psutil
bp = Blueprint('main', __name__)
@bp.route('/')
def index():
return jsonify({'message': 'Flask API v1.0', 'timestamp': datetime.now().isoformat()})
@bp.route('/health')
def health_check():
return jsonify({
'status': 'healthy',
'uptime': datetime.now().isoformat(),
'memory_usage': psutil.virtual_memory().percent,
'cpu_usage': psutil.cpu_percent()
})
@bp.route('/api/v1/data', methods=['GET', 'POST'])
def handle_data():
if request.method == 'GET':
return jsonify({'data': 'Sample data', 'method': 'GET'})
elif request.method == 'POST':
json_data = request.get_json()
return jsonify({'received': json_data, 'method': 'POST'})
Файл run.py
:
from app import create_app
app = create_app()
if __name__ == '__main__':
app.run(
debug=app.config['DEBUG'],
host=app.config['HOST'],
port=app.config['PORT']
)
Конфигурация для продакшена
Создай файл .env
:
SECRET_KEY=your-super-secret-key-here
FLASK_DEBUG=false
FLASK_HOST=0.0.0.0
FLASK_PORT=5000
Файл requirements.txt
:
Flask==2.3.3
python-dotenv==1.0.0
gunicorn==21.2.0
psutil==5.9.5
Развертывание с Gunicorn
Для продакшена Flask нужно запускать через WSGI-сервер. Gunicorn — отличный выбор:
# Базовый запуск
gunicorn -w 4 -b 0.0.0.0:5000 run:app
# Продвинутая конфигурация
gunicorn -w 4 -b 0.0.0.0:5000 \
--access-logfile /var/log/flask/access.log \
--error-logfile /var/log/flask/error.log \
--log-level info \
--timeout 120 \
--keep-alive 5 \
--max-requests 1000 \
--max-requests-jitter 100 \
run:app
Создай systemd-сервис для автозапуска. Файл /etc/systemd/system/flask-app.service
:
[Unit]
Description=Flask App
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/flask_project
Environment=PATH=/var/www/flask_project/flask_env/bin
ExecStart=/var/www/flask_project/flask_env/bin/gunicorn -w 4 -b 0.0.0.0:5000 run:app
ExecReload=/bin/kill -s HUP $MAINPID
Restart=always
[Install]
WantedBy=multi-user.target
Активация сервиса:
sudo systemctl daemon-reload
sudo systemctl enable flask-app
sudo systemctl start flask-app
sudo systemctl status flask-app
Настройка Nginx как reverse proxy
Конфигурация Nginx для Flask-приложения. Файл /etc/nginx/sites-available/flask-app
:
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
location /static/ {
root /var/www/flask_project;
expires 30d;
add_header Cache-Control "public, immutable";
}
# Для API можно добавить CORS
location /api/ {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# CORS headers
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization, Content-Type";
if ($request_method = OPTIONS) {
return 204;
}
}
}
Активация конфигурации:
sudo ln -s /etc/nginx/sites-available/flask-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Мониторинг и логирование
Добавим систему логирования в Flask-приложение. Обнови app/__init__.py
:
import logging
from logging.handlers import RotatingFileHandler
from flask import Flask
from app.config import Config
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
# Настройка логирования
if not app.debug:
if not os.path.exists('logs'):
os.mkdir('logs')
file_handler = RotatingFileHandler('logs/flask_app.log', maxBytes=10240, backupCount=10)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
))
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
app.logger.info('Flask application startup')
from app.routes import bp
app.register_blueprint(bp)
return app
Скрипт для мониторинга состояния приложения:
#!/bin/bash
# monitoring.sh
check_app() {
response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:5000/health)
if [ "$response" = "200" ]; then
echo "$(date): App is healthy"
else
echo "$(date): App is down (HTTP $response)"
# Перезапуск сервиса
sudo systemctl restart flask-app
fi
}
# Добавь в crontab: */5 * * * * /path/to/monitoring.sh
check_app
Интеграция с базой данных
Для работы с БД используем SQLAlchemy. Добавь в requirements.txt:
Flask-SQLAlchemy==3.0.5
Flask-Migrate==4.0.5
Обнови app/models.py
:
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
db = SQLAlchemy()
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
def to_dict(self):
return {
'id': self.id,
'username': self.username,
'email': self.email,
'created_at': self.created_at.isoformat()
}
class ApiLog(db.Model):
id = db.Column(db.Integer, primary_key=True)
endpoint = db.Column(db.String(255), nullable=False)
method = db.Column(db.String(10), nullable=False)
ip_address = db.Column(db.String(45), nullable=False)
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
response_code = db.Column(db.Integer)
Обнови app/config.py
:
import os
from dotenv import load_dotenv
load_dotenv()
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key-change-me'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'sqlite:///app.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
DEBUG = os.environ.get('FLASK_DEBUG', 'False').lower() == 'true'
HOST = os.environ.get('FLASK_HOST', '0.0.0.0')
PORT = int(os.environ.get('FLASK_PORT', 5000))
Расширенный API с аутентификацией
Добавим JWT-аутентификацию:
pip install Flask-JWT-Extended
Обнови app/routes.py
:
from flask import Blueprint, jsonify, request
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity
from datetime import datetime, timedelta
from app.models import db, User, ApiLog
import hashlib
bp = Blueprint('main', __name__)
@bp.route('/auth/login', methods=['POST'])
def login():
data = request.get_json()
username = data.get('username')
password = data.get('password')
if not username or not password:
return jsonify({'error': 'Username and password required'}), 400
# Простая проверка (в реальном проекте используй proper hashing)
user = User.query.filter_by(username=username).first()
if user:
access_token = create_access_token(
identity=user.id,
expires_delta=timedelta(hours=24)
)
return jsonify({
'access_token': access_token,
'user': user.to_dict()
})
return jsonify({'error': 'Invalid credentials'}), 401
@bp.route('/auth/register', methods=['POST'])
def register():
data = request.get_json()
username = data.get('username')
email = data.get('email')
if User.query.filter_by(username=username).first():
return jsonify({'error': 'Username already exists'}), 400
user = User(username=username, email=email)
db.session.add(user)
db.session.commit()
return jsonify({'message': 'User created', 'user': user.to_dict()}), 201
@bp.route('/protected', methods=['GET'])
@jwt_required()
def protected():
current_user_id = get_jwt_identity()
user = User.query.get(current_user_id)
return jsonify({
'message': 'This is a protected endpoint',
'user': user.to_dict() if user else None
})
Полезные расширения для Flask
Список популярных расширений, которые стоит изучить:
- Flask-CORS — для работы с Cross-Origin Resource Sharing
- Flask-Limiter — ограничение частоты запросов
- Flask-Caching — кеширование ответов
- Flask-Mail — отправка email
- Flask-Admin — админка для модели данных
- Flask-SocketIO — WebSocket поддержка
- Flask-RESTful — для создания REST API
- Celery — асинхронные задачи
Пример использования Flask-Limiter:
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
limiter = Limiter(
app,
key_func=get_remote_address,
default_limits=["200 per day", "50 per hour"]
)
@bp.route('/api/limited')
@limiter.limit("10 per minute")
def limited_endpoint():
return jsonify({'message': 'Rate limited endpoint'})
Автоматизация и CI/CD
Скрипт для автоматического деплоя:
#!/bin/bash
# deploy.sh
set -e
echo "Starting deployment..."
# Переход в директорию проекта
cd /var/www/flask_project
# Обновление кода
git pull origin main
# Активация виртуального окружения
source flask_env/bin/activate
# Установка зависимостей
pip install -r requirements.txt
# Миграции базы данных
flask db upgrade
# Перезапуск сервиса
sudo systemctl restart flask-app
# Проверка статуса
sleep 5
if curl -f http://localhost:5000/health > /dev/null; then
echo "Deployment successful!"
else
echo "Deployment failed!"
exit 1
fi
Пример GitHub Actions workflow (.github/workflows/deploy.yml
):
name: Deploy Flask App
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pytest
- name: Run tests
run: pytest tests/
- name: Deploy to server
uses: appleboy/ssh-action@v0.1.5
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /var/www/flask_project
./deploy.sh
Тестирование Flask-приложения
Создай файл tests/test_app.py
:
import pytest
from app import create_app
from app.models import db, User
@pytest.fixture
def app():
app = create_app()
app.config['TESTING'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
with app.app_context():
db.create_all()
yield app
db.drop_all()
@pytest.fixture
def client(app):
return app.test_client()
def test_health_check(client):
response = client.get('/health')
assert response.status_code == 200
data = response.get_json()
assert data['status'] == 'healthy'
def test_user_registration(client):
response = client.post('/auth/register', json={
'username': 'testuser',
'email': 'test@example.com'
})
assert response.status_code == 201
data = response.get_json()
assert data['user']['username'] == 'testuser'
def test_protected_endpoint_without_token(client):
response = client.get('/protected')
assert response.status_code == 401
Запуск тестов:
pip install pytest
pytest tests/ -v
Оптимизация производительности
Несколько советов для повышения производительности Flask-приложения:
- Используй Redis для кеширования — Flask-Caching с Redis backend
- Оптимизируй запросы к БД — используй индексы и избегай N+1 проблем
- Настрой правильно Gunicorn — количество воркеров = (CPU cores * 2) + 1
- Используй CDN для статики — AWS CloudFront или Cloudflare
- Мониторинг — подключи Prometheus + Grafana
Пример конфигурации Redis cache:
from flask_caching import Cache
cache = Cache(app, config={
'CACHE_TYPE': 'redis',
'CACHE_REDIS_URL': 'redis://localhost:6379/0',
'CACHE_DEFAULT_TIMEOUT': 300
})
@bp.route('/api/cached-data')
@cache.cached(timeout=60)
def cached_endpoint():
# Дорогая операция
return jsonify({'data': 'This response is cached'})
Безопасность Flask-приложения
Обязательные меры безопасности:
- Используй HTTPS — настрой SSL через Let’s Encrypt
- Валидируй входные данные — используй Flask-WTF или marshmallow
- Настрой CORS правильно — не используй wildcards в продакшене
- Скрой информацию о сервере — отключи debug в продакшене
- Используй helmet для заголовков безопасности
Пример middleware для безопасности:
from flask import Flask
from flask_talisman import Talisman
app = Flask(__name__)
Talisman(app, force_https=True)
@app.after_request
def security_headers(response):
response.headers['X-Content-Type-Options'] = 'nosniff'
response.headers['X-Frame-Options'] = 'DENY'
response.headers['X-XSS-Protection'] = '1; mode=block'
return response
Интересные применения Flask
Flask отлично подходит для нестандартных задач:
- Webhook обработчики — для GitHub, GitLab, Stripe
- Микросервисы — небольшие специализированные сервисы
- API Gateway — прокси для других сервисов
- Админские панели — с Flask-Admin
- Интеграция с IoT — прием данных с датчиков
- Chatbot API — обработка сообщений от Telegram, Discord
Пример webhook обработчика:
import hmac
import hashlib
@bp.route('/webhook/github', methods=['POST'])
def github_webhook():
signature = request.headers.get('X-Hub-Signature-256')
if not verify_signature(request.data, signature):
return jsonify({'error': 'Invalid signature'}), 401
payload = request.get_json()
if payload.get('action') == 'opened':
# Обработка нового PR
handle_new_pr(payload)
return jsonify({'status': 'processed'})
def verify_signature(payload, signature):
secret = os.environ.get('GITHUB_WEBHOOK_SECRET')
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(f'sha256={expected}', signature)
Полезные ресурсы
Официальная документация и полезные ссылки:
Заключение и рекомендации
Flask — это мощный и гибкий инструмент для создания веб-приложений любой сложности. Он идеально подходит для быстрого прототипирования, создания API и микросервисов. Основные преимущества:
- Быстрый старт — можешь создать рабочий API за 10 минут
- Масштабируемость — легко добавлять новые компоненты
- Производительность — при правильной настройке показывает отличные результаты
- Сообщество — множество расширений и готовых решений
Рекомендую использовать Flask для:
- REST API и микросервисов
- Прототипов и MVP
- Интеграционных сервисов
- Админских панелей
- Webhook обработчиков
Для крупных проектов с множеством форм и сложной логикой лучше рассмотреть Django. Если нужна максимальная производительность для API — обрати внимание на FastAPI.
Помни про безопасность, мониторинг и правильное конфигурирование продакшен-окружения. Удачи в разработке!
В этой статье собрана информация и материалы из различных интернет-источников. Мы признаем и ценим работу всех оригинальных авторов, издателей и веб-сайтов. Несмотря на то, что были приложены все усилия для надлежащего указания исходного материала, любая непреднамеренная оплошность или упущение не являются нарушением авторских прав. Все упомянутые товарные знаки, логотипы и изображения являются собственностью соответствующих владельцев. Если вы считаете, что какой-либо контент, использованный в этой статье, нарушает ваши авторские права, немедленно свяжитесь с нами для рассмотрения и принятия оперативных мер.
Данная статья предназначена исключительно для ознакомительных и образовательных целей и не ущемляет права правообладателей. Если какой-либо материал, защищенный авторским правом, был использован без должного упоминания или с нарушением законов об авторском праве, это непреднамеренно, и мы исправим это незамедлительно после уведомления. Обратите внимание, что переиздание, распространение или воспроизведение части или всего содержимого в любой форме запрещено без письменного разрешения автора и владельца веб-сайта. Для получения разрешений или дополнительных запросов, пожалуйста, свяжитесь с нами.