- •ВВЕДЕНИЕ
- •1. ПОДГОТОВКА К РАЗРАБОТКЕ
- •1.1. НАЧАЛО РАБОТЫ
- •1.2. ИСПОЛЬЗОВАНИЕ КОНТЕЙНЕРОВ DOCKER
- •1.3. НАСТРОЙКА СУБД POSTGRESQL
- •1.4. НАСТРОЙКА ТЕСТОВ
- •1.6. СОЗДАНИЕ RESTFUL-МАРШРУТОВ
- •1.6.1. МАРШРУТ POST /USERS
- •1.6.3. МАРШРУТ GET /USERS
- •1.6.2. МАРШРУТ GET /USERS/:ID
- •1.7. ИСПОЛЬЗОВАНИЕ ШАБЛОНОВ JINJA
- •1.8. СТРУКТУРА ПРОЕКТА
- •2. ОТЛАДКА ПРИЛОЖЕНИЯ
- •2.1. ПОКРЫТИЕ ТЕСТАМИ И КАЧЕСТВО КОДА
- •2.1.1. ПОКРЫТИЕ КОДА ТЕСТАМИ
- •2.1.2. КАЧЕСТВО КОДА
- •2.2. НЕПРЕРЫВНАЯ ИНТЕГРАЦИЯ
- •2.3. ПАНЕЛЬ ИНСТРУМЕНТОВ ОТЛАДКИ FLASK
- •2.4. СТРУКТУРА ПРИЛОЖЕНИЯ
- •3. РАЗВИТИЕ ПРИЛОЖЕНИЯ
- •3.1. ПАКЕТ FLASK MIGRATE
- •3.1.1. ИЗМЕНЕНИЕ МОДЕЛИ
- •3.1.2. НАСТРОЙКА FLASK MIGRATE
- •3.2. РЕФАКТОРИНГ КОДА
- •3.3. ПАКЕТ FLASK BCRYPT
- •3.4. НАСТРОЙКА LOG ROUNDS
- •3.5. ИСПОЛЬЗОВАНИЕ ТОКЕНОВ JWT
- •3.5.1. ПАКЕТ PYJWT
- •3.5.2. РАСШИФРОВКА ТОКЕНА
- •3.6. МАРШРУТЫ ДЛЯ АУТЕНТИФИКАЦИИ
- •3.6.1. МАРШРУТ POST /AUTH/REGISTER
- •3.6.2. МАРШРУТ POST /AUTH/LOGIN
- •3.6.3. МАРШРУТ GET /AUTH/LOGOUT
- •3.6.4. МАРШРУТ GET /AUTH/STATUS
- •ЗАКЛЮЧЕНИЕ
- •СПИСОК ЛИТЕРАТУРЫ
response_object['message'] = 'Sorry. That email already ex-
ists.'
return jsonify(response_object), 400 except exc.IntegrityError as e:
db.session.rollback()
return jsonify(response_object), 400
Добавим импорт:
from sqlalchemy import exc
Необходимо убедиться, что тесты выполняются, а затем можно перейти к созданию следующего маршрута.
1.6.2. МАРШРУТ GET /USERS/:ID
Начнём создание маршрута с написания теста:
def test_single_user(self):
"""Проверка, что получение данных одного пользователя работает корректно."""
user = User(username='testname1', email='testname1@flaskapp.org')
db.session.add(user)
db.session.commit()
with self.client:
response = self.client.get(f'/users/{user.id}')
data = json.loads(response.data.decode())
self.assertEqual(response.status_code, 200)
self.assertIn('testname1', data['data']['username'])
self.assertIn('testname1@flaskapp.org', data['data']['email'])
self.assertIn('success', data['status'])
Добавим следующий импорт:
from project import db
from project.api.models import User
Перед созданием функции представления необходимо убедиться, что тес-
ты не выполняются. Создадим представление:
@users_blueprint.route('/users/<user_id>', methods=['GET'])
def get_single_user(user_id):
"""Получение данных для одного пользователя"""
user = User.query.filter_by(id=user_id).first()
response_object = {
'status': 'success',
'data': {
24
'id': user.id, 'username': user.username, 'email': user.email, 'active': user.active
}
}
return jsonify(response_object), 200
Тесты теперь должны завершиться успешно. А что насчёт обработки ошибок? Рассмотрим следующие варианты.
1.Идентификатор пользователя не указан.
2.Идентификатор пользователя не существует.
Напишем соответствующие тесты:
def test_single_user_no_id(self):
"""Проверка, что выдаётся ошибка, если идентификатор не ука-
зан."""
with self.client:
response = self.client.get('/users/blah')
data = json.loads(response.data.decode())
self.assertEqual(response.status_code, 404)
self.assertIn('User does not exist', data['message'])
self.assertIn('fail', data['status'])
def test_single_user_incorrect_id(self):
"""Проверка, что выдаётся ошибка, если идентификатор не существу-
ет."""
with self.client:
response = self.client.get('/users/999')
data = json.loads(response.data.decode())
self.assertEqual(response.status_code, 404)
self.assertIn('User does not exist', data['message'])
self.assertIn('fail', data['status'])
Обновлённый вид теперь будет выглядеть следующим образом:
@users_blueprint.route('/users/<user_id>', methods=['GET'])
def get_single_user(user_id):
"""Получение данных одного пользователя"""
response_object = {
'status': 'fail',
'message': 'User does not exist'
}
try:
25
user = User.query.filter_by(id=int(user_id)).first() if not user:
return jsonify(response_object), 404 else:
response_object = { 'status': 'success', 'data': {
'id': user.id, 'username': user.username, 'email': user.email, 'active': user.active
}
}
return jsonify(response_object), 200 except ValueError:
return jsonify(response_object), 404
1.6.3. МАРШРУТ GET /USERS
Снова начнём создание маршрута с написания теста. Поскольку сначала нужно добавить несколько пользователей в базу данных, добавим вспомога-
тельную функцию в начало файла project/tests/test_users.py перед классом
TestUserService ().
def add_user(username, email):
user = User(username=username, email=email)
db.session.add(user)
db.session.commit()
return user
Выполним рефакторинг теста test_single_user (), например следующим
образом:
def test_single_user(self):
"""Проверка, что get_single_user ведёт себя корректно.""" user = add_user('testname1', 'testname1@flaskapp.org') with self.client:
response = self.client.get(f'/users/{user.id}') data = json.loads(response.data.decode()) self.assertEqual(response.status_code, 200) self.assertIn('testname1', data['data']['username'])
self.assertIn('testname1@flaskapp.org', data['data']['email']) self.assertIn('success', data['status'])
26
Добавим новый тест:
def test_all_users(self):
"""Проверка, что get_all_users ведёт себя корректно.""" add_user('testname1', 'testname1@flaskapp.org') add_user('testname2', 'testname2@flaskapp.com')
with self.client:
response = self.client.get('/users')
data = json.loads(response.data.decode()) self.assertEqual(response.status_code, 200) self.assertEqual(len(data['data']['users']), 2) self.assertIn('testname1', data['data']['users'][0]['username']) self.assertIn(
'testname1@flaskapp.org', data['data']['users'][0]['email']) self.assertIn('testname2', data['data']['users'][1]['username']) self.assertIn(
'testname2@flaskapp.com', data['data']['users'][1]['email']) self.assertIn('success', data['status'])
Убедимся, что тесты не работают. Затем добавим следующее представле-
ние:
@users_blueprint.route('/users', methods=['GET']) def get_all_users():
"""Get all users""" response_object = {
'status': 'success', 'data': {
'users': [user.to_json() for user in User.query.all()]
}
}
return jsonify(response_object), 200
Добавим к моделям метод to_json:
def to_json(self):
return {
'id': self.id,
'username': self.username,
'email': self.email,
'active': self.active
}
Прежде чем двигаться дальше, протестируем маршрут в браузере – http://DOCKER_MACHINE_IP:5001/users. Результат обращения к маршруту должен быть следующим:
27
