💻 Примеры: полный CRUD Community Pulse
⚡ Минимальный рабочий пример
# questions.py — основные эндпоинты
from flask import Blueprint, request, jsonify
from sqlalchemy import select
from app.models import db
from app.models.questions import Question
questions_bp = Blueprint('questions', __name__, url_prefix='/questions')
@questions_bp.route('/', methods=['GET'])
def get_questions():
questions = db.session.execute(select(Question)).scalars().all()
return jsonify([{'id': q.id, 'text': q.text} for q in questions])
@questions_bp.route('/', methods=['POST'])
def create_question():
data = request.get_json()
if not data or 'text' not in data:
return jsonify({'error': 'No text'}), 400
q = Question(text=data['text'])
db.session.add(q); db.session.commit()
return jsonify({'id': q.id}), 201
Пример 1: Полный файл questions.py (SQLAlchemy 2.x)
# app/routers/questions.py
from flask import Blueprint, request, jsonify
from sqlalchemy import select
from app.models import db
from app.models.questions import Question
questions_bp = Blueprint('questions', __name__, url_prefix='/questions')
@questions_bp.route('/', methods=['GET'])
def get_questions():
"""Получение списка всех вопросов."""
questions = db.session.execute(select(Question)).scalars().all()
questions_data = [{'id': q.id, 'text': q.text} for q in questions]
return jsonify(questions_data)
@questions_bp.route('/<int:id>', methods=['GET'])
def get_question(id):
"""Получение деталей конкретного вопроса по его ID."""
question = db.session.get(Question, id)
if question is None:
return jsonify({'message': 'Вопрос с таким ID не найден'}), 404
return jsonify({'id': question.id, 'text': question.text}), 200
@questions_bp.route('/', methods=['POST'])
def create_question():
"""Создание нового вопроса."""
data = request.get_json()
if not data or 'text' not in data:
return jsonify({'error': 'No question text provided'}), 400
question = Question(text=data['text'])
db.session.add(question)
db.session.commit()
return jsonify({'message': 'Вопрос создан', 'id': question.id}), 201
@questions_bp.route('/<int:id>', methods=['PUT'])
def update_question(id):
"""Обновление конкретного вопроса по его ID."""
question = db.session.get(Question, id)
if question is None:
return jsonify({'message': 'Вопрос с таким ID не найден'}), 404
data = request.get_json()
if not data or 'text' not in data:
return jsonify({'message': 'Текст вопроса не предоставлен'}), 400
question.text = data['text']
db.session.commit()
return jsonify({'message': f'Вопрос обновлён: {question.text}'}), 200
@questions_bp.route('/<int:id>', methods=['DELETE'])
def delete_question(id):
"""Удаление конкретного вопроса по его ID."""
question = db.session.get(Question, id)
if question is None:
return jsonify({'message': 'Вопрос с таким ID не найден'}), 404
db.session.delete(question)
db.session.commit()
return jsonify({'message': f'Вопрос с ID {id} удалён'}), 200
Пример 2: Полный файл response.py (статистика ответов)
# app/routers/response.py
from flask import Blueprint, request, jsonify
from sqlalchemy import select
from app.models import db
from app.models.questions import Question
from app.models.statistic import Statistic
from app.models.response import Response
response_bp = Blueprint('response', __name__, url_prefix='/responses')
@response_bp.route('/', methods=['GET'])
def get_responses():
"""Получение агрегированной статистики ответов."""
statistics = db.session.execute(select(Statistic)).scalars().all()
results = [
{
'question_id': stat.question_id,
'agree_count': stat.agree_count,
'disagree_count': stat.disagree_count
}
for stat in statistics
]
return jsonify(results), 200
@response_bp.route('/', methods=['POST'])
def add_response():
"""Добавление нового ответа на вопрос с обновлением статистики."""
data = request.get_json()
if not data or 'question_id' not in data or 'is_agree' not in data:
return jsonify({'message': 'Некорректные данные'}), 400
question = db.session.get(Question, data['question_id'])
if not question:
return jsonify({'message': 'Вопрос не найден'}), 404
# Создаём запись ответа
response = Response(
question_id=question.id,
is_agree=data['is_agree']
)
db.session.add(response)
# Обновляем или создаём статистику
statistic = db.session.execute(
select(Statistic).filter_by(question_id=question.id)
).scalar_one_or_none()
if not statistic:
statistic = Statistic(
question_id=question.id,
agree_count=0,
disagree_count=0
)
db.session.add(statistic)
if data['is_agree']:
statistic.agree_count += 1
else:
statistic.disagree_count += 1
db.session.commit()
return jsonify({'message': f'Ответ на вопрос {question.id} добавлен'}), 201
Пример 3: Регистрация blueprints в create_app()
# app/__init__.py
from flask import Flask
from app.models import db
def create_app(config=None):
app = Flask(__name__)
app.config.from_object(config or 'app.config.DevelopmentConfig')
db.init_app(app)
from app.routers.questions import questions_bp
from app.routers.response import response_bp
app.register_blueprint(questions_bp)
app.register_blueprint(response_bp)
return app
Пример 4: Тестирование в Postman
GET /questions — получить все вопросы
Method: GET
URL: http://localhost:5000/questions/
Headers: (не нужны)
Expected: 200 OK, JSON-массив
POST /questions — создать вопрос
Method: POST
URL: http://localhost:5000/questions/
Headers: Content-Type: application/json
Body (raw JSON):
{
"text": "Согласны ли вы с ограничением скорости 30 км/ч в городе?"
}
Expected: 201 Created
{
"message": "Вопрос создан",
"id": 1
}
PUT /questions/1 — обновить вопрос
Method: PUT
URL: http://localhost:5000/questions/1
Headers: Content-Type: application/json
Body (raw JSON):
{
"text": "Обновлённый текст вопроса"
}
Expected: 200 OK
DELETE /questions/1 — удалить вопрос
Method: DELETE
URL: http://localhost:5000/questions/1
Expected: 200 OK
{
"message": "Вопрос с ID 1 удалён"
}
POST /responses — добавить ответ
Method: POST
URL: http://localhost:5000/responses/
Headers: Content-Type: application/json
Body (raw JSON):
{
"question_id": 1,
"is_agree": true
}
Expected: 201 Created
{
"message": "Ответ на вопрос 1 добавлен"
}
Пример 5: Использование request.args для фильтрации
⚠️ Проверить по документации — расширенная фильтрация не входила в лекцию, показана как дополнительный паттерн.
@questions_bp.route('/', methods=['GET'])
def get_questions():
"""Получение вопросов с опциональным поиском."""
search = request.args.get('search', '') # /questions/?search=скорость
stmt = select(Question)
if search:
stmt = stmt.where(Question.text.ilike(f'%{search}%'))
questions = db.session.execute(stmt).scalars().all()
return jsonify([{'id': q.id, 'text': q.text} for q in questions])