Как создать API с помощью Flask
В этом уроке мы узнаем, как создать простой RESTful API с помощью Flask, облегченного веб-фреймворка для Python. Мы также будем использовать SQLAlchemy, ORM (Object-Relational Mapper), который позволяет нам взаимодействовать с базой данных с использованием объектов Python. В качестве базы данных мы будем использовать SQLite, но вы можете использовать любую другую базу данных, поддерживаемую SQLAlchemy.
Что такое RESTful API?
RESTful API (Representational State Transfer) — это способ разработки веб-сервисов, который следует некоторым принципам:
- Каждый ресурс (например, пользователь, продукт, сообщение и т. д.) идентифицируется уникальным URI (унифицированным идентификатором ресурса), например
/users/1
или/products/42
. - Клиент может выполнять различные операции с ресурсами, используя методы HTTP, такие как GET, POST, PUT, PATCH, DELETE и т. д. Например, чтобы создать нового пользователя, клиент может отправить запрос POST с данными пользователя
/users
в тело запроса. Чтобы обновить существующего пользователя, клиент может отправить запрос PUT или PATCH/users/1
с обновленными данными в теле запроса. Чтобы удалить пользователя, клиент может отправить запрос DELETE на адрес/users/1
. - Сервер отвечает соответствующим кодом состояния и данными в теле ответа, обычно в формате JSON (нотация объектов JavaScript). Например, если создание пользователя прошло успешно, сервер может ответить кодом состояния 201 (Создано) и данными созданного пользователя в теле ответа.
Если обновление пользователя прошло успешно, сервер может ответить кодом состояния 200 (ОК) и обновленными данными пользователя в теле ответа. Если удаление пользователя прошло успешно, сервер может ответить кодом состояния 204 (Нет контента) и без текста ответа.
Что такое SQLAlchemy?
SQLAlchemy — это ORM, который позволяет нам работать с базами данных, используя объекты Python. Он абстрагирует низкоуровневые детали SQL-запросов и предоставляет нам высокоуровневый интерфейс для управления данными.
SQLAlchemy поддерживает различные базы данных, такие как SQLite, PostgreSQL, MySQL, Oracle и т. д. SQLAlchemy также предоставляет нам декларативные модели, которые определяют схему нашей базы данных с использованием классов и атрибутов Python.
Затем мы можем использовать эти модели для выполнения операций CRUD (создание, чтение, обновление, удаление) над нашими данными.
Настройка нашего проекта:
Чтобы запустить наш проект, нам нужно установить некоторые зависимости:
- Python 3: вы можете загрузить его с https://www.python.org/downloads/ или использовать предпочитаемый вами менеджер пакетов.
- Pip: инструмент для установки пакетов Python. По умолчанию он должен поставляться с Python 3.
- Virtualenv: инструмент для создания изолированных сред Python. Вы можете установить его с помощью
pip install virtualenv
. - Flask: Наш веб-фреймворк. Вы можете установить его с помощью
pip install flask
. - SQLAlchemy: Наша ORM. Вы можете установить его с помощью
pip install sqlalchemy
. - Flask-SQLAlchemy : расширение, которое интегрирует SQLAlchemy с Flask. Вы можете установить его с помощью
pip install flask-sqlalchemy
.
Далее нам нужно создать каталог и файлы нашего проекта:
- Создайте каталог с именем
flask-api
и перейдите в него. - Создайте виртуальную среду под названием
venv
usingvirtualenv venv
. - Активируйте виртуальную среду, используя
source venv/bin/activate
Linux/Mac илиvenv\Scripts\activate
Windows. - Создайте файл с именем
app.py
, который будет содержать основной код нашего приложения. - Создайте файл с именем
models.py
, который будет содержать наши модели базы данных. - Создайте файл с именем
config.py
, который будет содержать наши настройки конфигурации.
Настройка нашего приложения
В нашем config.py
файле нам нужно определить некоторые параметры конфигурации для нашего приложения:
import os # Get the absolute path of the current directory basedir = os.path.abspath(os.path.dirname(__file__)) # Define the SQLALCHEMY_DATABASE_URI variable that tells SQLAlchemy where to find our database # We will use SQLite for simplicity and store it in a file called app.db in our project directory SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db') # Define the SQLALCHEMY_TRACK_MODIFICATIONS variable that tells SQLAlchemy whether to track changes to the database # We will set it to False to avoid unnecessary overhead SQLALCHEMY_TRACK_MODIFICATIONS = False
Создание нашего приложения
В нашем app.py
файле нам нужно создать приложение Flask и инициализировать его с нашими настройками конфигурации.
Чтобы взаимодействовать с базой данных с помощью SQLAlchemy, нам нужно создать объект db
и связать его с нашим приложением Flask. Этого можно добиться, импортировав SQLAlchemy
класс из flask_sqlalchemy
модуля и создав db
объект в отдельном файле с именем db.py
.
Вот пример фрагмента кода для создания db
объекта:
from flask_sqlalchemy import SQLAlchemy # Create a SQLAlchemy object db = SQLAlchemy()
Затем в основном файле приложения app.py
мы можем создать приложение Flask и связать его с db
объектом, вызвав init_app()
метод объекта db
. Вот пример фрагмента кода:
from flask import Flask from db import db # Create a Flask application with the name of the current module app = Flask(__name__) # Load the configuration settings from the config.py file app.config.from_pyfile('config.py') # Initialize the SQLAlchemy object with our application db.init_app(app)
Определение моделей наших баз данных
В нашем models.py
файле нам нужно определить модели базы данных, используя декларативный синтаксис SQLAlchemy. Модель — это класс Python, который представляет таблицу в нашей базе данных, а его атрибуты представляют столбцы. Нам также необходимо импортировать db
объект, который мы будем использовать для взаимодействия с нашей базой данных.
В этом уроке мы создадим простую модель под названием, User
которая имеет следующие атрибуты:
id
: целое число, которое является первичным ключом таблицы и уникально идентифицирует каждого пользователя.name
: строка, в которой хранится имя пользователя.email
: строка, в которой хранится адрес электронной почты пользователя.
Теперь мы можем определить нашу модель следующим образом:
from db import db # Define a User model that inherits from db.Model class User(db.Model): # Define the id attribute as an integer column that is the primary key id = db.Column(db.Integer, primary_key=True) # Define the name attribute as a string column that is not nullable name = db.Column(db.String(80), nullable=False) # Define the email attribute as a string column that is unique and not nullable email = db.Column(db.String(120), unique=True, nullable=False) # Define a __repr__ method that returns a string representation of the user object def __repr__(self): return f'<User {self.name}>'
Создание базы данных
После определения модели базы данных следующим шагом будет создание самой базы данных и ее таблиц. Это можно сделать с помощью метода SQLAlchemy create_all
. Для этого нам сначала нужно активировать нашу виртуальную среду (если это еще не сделано), а затем создать файл с именем create_db.py
.
Внутри create_db.py
мы добавим следующий фрагмент кода:
from app import app, db # Create and push an application context with app.app_context(): # Now you can use the db object db.create_all()
- Запустите файл в терминале.
Мы можем убедиться, что наша база данных и ее таблицы созданы, просмотрев файл app.db в каталоге нашего проекта. Мы также можем использовать такой инструмент, как DB Browser for SQLite ( https://sqlitebrowser.org/ ), для проверки нашей базы данных и управления ею.
Создание API
Теперь, когда мы создали нашу базу данных и ее модель, мы можем приступить к созданию нашего API. Мы будем использовать встроенную систему маршрутизации Flask для определения различных конечных точек для нашего API и обработки различных методов HTTP. Мы также будем использовать функции запроса и jsonify Flask для анализа и возврата данных JSON.
Мы реализуем следующие конечные точки для нашего API:
- GET
/users
: вернуть список всех пользователей в формате JSON. - GET
/users/<id>
: вернуть одного пользователя с заданным идентификатором в формате JSON. Если пользователя с таким идентификатором не существует, верните ошибку 404 (не найден). - POST
/users
: создайте нового пользователя с данными, указанными в теле запроса в формате JSON. Верните созданного пользователя в формате JSON с кодом состояния 201 (Создано). - PUT
/users/<id>
: обновить существующего пользователя с заданным идентификатором данными, предоставленными в теле запроса в формате JSON. Верните обновленного пользователя в формате JSON с кодом состояния 200 (ОК). Если пользователя с таким идентификатором не существует, верните ошибку 404 (не найден). - DELETE
/users/<id>
: удалить существующего пользователя с данным идентификатором. Верните код состояния 204 (Нет контента). Если пользователя с таким идентификатором не существует, верните ошибку 404 (не найден). - Мы можем добавить следующий код в наш файл app.py для реализации этих конечных точек:
from flask import Flask, jsonify, request from db import db # Create a Flask application with the name of the current module app = Flask(__name__) # Load the configuration settings from the config.py file app.config.from_pyfile('config.py') # Initialize the SQLAlchemy object with our application db.init_app(app) # Import the User model from models.py from models import User # Define a route for the GET /users endpoint @app.route('/users', methods=['GET']) def get_users(): # Query all users from the database users = User.query.all() # Convert each user object to a dictionary users_dict = [user.__dict__ for user in users] # Remove the _sa_instance_state attribute from each dictionary for user_dict in users_dict: user_dict.pop('_sa_instance_state') # Return a JSON response with the list of users return jsonify(users_dict) # Define a route for the GET /users/<id> endpoint @app.route('/users/<int:id>', methods=['GET']) def get_user(id): # Query a user by id from the database user = User.query.get(id) # Check if the user exists if user is None: # Return a 404 error if not found return jsonify({'message': 'User not found'}), 404 else: # Convert the user object to a dictionary user_dict = user.__dict__ # Remove the _sa_instance_state attribute from the dictionary user_dict.pop('_sa_instance_state') # Return a JSON response with the user data return jsonify(user_dict) # Define a route for the POST /users endpoint @app.route('/users', methods=['POST']) def create_user(): # Get the data from the request body as a dictionary data = request.get_json() # Check if the data is valid if 'name' not in data or 'email' not in data: # Return a 400 error if missing name or email return jsonify({'message': 'Name and email are required'}), 400 else: # Create a new user object with the data user = User(name=data['name'], email=data['email']) # Add and commit the user to the database db.session.add(user) db.session.commit() # Convert the user object to a dictionary user_dict = user.__dict__ # Remove the _sa_instance_state attribute from the dictionary user_dict.pop('_sa_instance_state') # Return a JSON response with the created user data and a 201 status code return jsonify(user_dict), 201 # Define a route for the PUT /users/<id> endpoint @app.route('/users/<int:id>', methods=['PUT']) def update_user(id): # Query a user by id from the database user = User.query.get(id) # Check if the user exists if user is None: # Return a 404 error if not found return jsonify({'message': 'User not found'}), 404 else: # Get the data from the request body as a dictionary data = request.get_json() # Check if the data is valid if 'name' not in data or 'email' not in data: # Return a 400 error if missing name or email return jsonify({'message': 'Name and email are required'}), 400 else: # Update the user object with the data user.name = data['name'] user.email = data['email'] # Commit the changes to the database db.session.commit() # Convert the user object to a dictionary user_dict = user.__dict__ # Remove the _sa_instance_state attribute from the dictionary user_dict.pop('_sa_instance_state') # Return a JSON response with the updated user data and a 200 status code return jsonify(user_dict), 200 # Define a route for the DELETE /users/<id> endpoint @app.route('/users/<int:id>', methods=['DELETE']) def delete_user(id): # Query a user by id from the database user = User.query.get(id) # Check if the user exists if user is None: # Return a 404 error if not found return jsonify({'message': 'User not found'}), 404 else: # Delete the user from the database db.session.delete(user) db.session.commit() # Return a 204 status code with no response body return '', 204 if __name__ == '__main__': app.run()
Тестирование API
Теперь, когда мы реализовали конечные точки API, мы можем протестировать их с помощью такого инструмента, как Postman ( https://www.postman.com/ ) или Curl ( https://curl.se/ ). Мы можем использовать эти инструменты для отправки различных HTTP-запросов к нашему API и проверки ответов.
Чтобы протестировать наш API, нам нужно выполнить следующие шаги:
- Запустите наше приложение Flask, используя
python app.py
. - Откройте Postman или Curl и отправьте различные запросы к конечным точкам нашего API.
- Проверьте коды состояния и тела ответов каждого запроса.
Сначала давайте создадим новый users
, используя метод POST:
- POST
/users
: создайте нового пользователя с данными, указанными в теле запроса в формате JSON. Верните созданного пользователя в формате JSON с кодом состояния 201 (Создано).
Запрос:
#User 1: curl -X POST -H "Content-Type: application/json" -d '{"name": "Alice","email":"alice@example.com"}' http://127.0.0.1:5000/users
Ответ:
{}
Пользователь 2:
curl -X POST -H "Content-Type: application/json" -d '{"name": "Bob", "email": "bob@example.com"}' http://127.0.0.1:5000/users
Пользователь 3:
curl -X POST -H "Content-Type: application/json" -d '{"name": "Charlie","email": "charlie@example.com"}' http://127.0.0.1:5000/users
Теперь мы будем использовать метод GET для возврата списка пользователей в формате JSON.
- GET
/users
:
Запрос:
curl http://127.0.0.1:5000/users
Ответ:
[ { "id": 1, "name": "Alice", "email": "alice@example.com" }, { "id": 2, "name": "Bob", "email": "bob@example.com" }, { "id": 3, "name": "Charlie", "email": "charlie@example.com" } ]
- GET
/users/<id>
: вернуть одного пользователя с заданным идентификатором в формате JSON. Если пользователя с таким идентификатором не существует, верните ошибку 404 (не найден).
Запрос:
curl http://localhost:5000/users/1
Ответ:
{ "id": 1, "name": "Alice", "email": "alice@example.com" }
Запрос:
curl http://localhost:5000/users/4
Ответ:
{ "message": "User not found" }
- PUT
/users/<id>
: обновить существующего пользователя с заданным идентификатором данными, предоставленными в теле запроса в формате JSON. Верните обновленного пользователя в формате JSON с кодом состояния 200 (ОК). Если пользователя с таким идентификатором не существует, верните ошибку 404 (не найден).
Запрос:
curl -X PUT -H "Content-Type: application/json" -d '{"name": "Alice", "email": "alice@new.com"}' http://localhost:5000/users/1
Ответ:
{}
Используйте GET, чтобы проверить обновленные данные пользователя.
curl http://localhost:5000/users/1
Ответ:
{ "id": 1, "name": "Alice", "email": "alice@new.com" }
Запрос:
curl -X PUT -H "Content-Type: application/json" -d '{"name": "Eve", "email": "eve@example.com"}' http://localhost:5000/users/4
Ответ:
{ "message": "User not found" }
- DELETE
/users/<id>
: удалить существующего пользователя с данным идентификатором. Верните код состояния 204 (Нет контента). Если пользователя с таким идентификатором не существует, верните ошибку 404 (не найден).
Запрос:
curl -X DELETE http://localhost:5000/users/2
Ответ:
Нет тела ответа.
Запрос:
curl -X DELETE http://localhost:5000/users/5
Ответ:
{ "message": "User not found" }
Вывод
Подводя итог, в этом уроке мы узнали, как создать простой RESTful API с использованием Flask и SQLAlchemy. Мы также узнали, как выполнять операции CRUD в нашей базе данных с использованием объектов Python и как тестировать наш API с помощью Curl.
Flask и Flask-RESTful предоставляют множество расширенных функций и опций для создания REST API, таких как подключение к базам данных с помощью flask_sqlalchemy
, сериализация и десериализация данных с помощью flask_marshmallow
, добавление аутентификации и авторизации с помощью flask_jwt_extended
, а также создание интерактивной документации для API с помощью flask_swagger_ui
.