Как создать 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 (Нет контента) и без тела ответа.

Что такое Фласк?

Flask — это микровеб-фреймворк для Python, который позволяет нам быстро и легко создавать веб-приложения.

Он имеет минимальные зависимости и предоставляет нам необходимые инструменты для создания веб-сервисов, такие как маршрутизация, обработка запросов и ответов, шаблоны и т. д.

Flask также является расширяемым и поддерживает различные расширения, которые расширяют функциональность наших приложений, такие как SQLAlchemy для интеграции с базой данных, Flask-RESTful для создания RESTful API, Flask-JWT для аутентификации и авторизации и т. д.

Что такое 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, используя virtualenv 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}>'

Создание нашей базы данных

После определения нашей модели базы данных следующим шагом будет создание фактической базы данных и ее таблиц. Это можно сделать с помощью метода create_all SQLAlchemy. Для этого нам сначала нужно активировать нашу виртуальную среду (если она еще не активирована), а затем создать файл с именем 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 для 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 (не найдено).
  • УДАЛИТЬ /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":"[email protected]"}' http://127.0.0.1:5000/users

Ответ:

{}

Пользователь 2:

curl -X POST -H "Content-Type: application/json" -d '{"name": "Bob", "email": "[email protected]"}' http://127.0.0.1:5000/users

Пользователь 3:

curl -X POST -H "Content-Type: application/json" -d '{"name": "Charlie","email": "[email protected]"}' http://127.0.0.1:5000/users

Теперь мы будем использовать метод GET, чтобы вернуть список пользователей в формате JSON.

  • ПОЛУЧИТЬ /users:

Запрос:

curl http://127.0.0.1:5000/users

Ответ:

[
  {
    "id": 1,
    "name": "Alice",
    "email": "[email protected]"
  },
  {
    "id": 2,
    "name": "Bob",
    "email": "[email protected]"
  },
  {
    "id": 3,
    "name": "Charlie",
    "email": "[email protected]"
  }
]
  • GET /users/<id>: вернуть одного пользователя с заданным идентификатором в формате JSON. Если пользователя с таким идентификатором не существует, вернуть ошибку 404 (не найдено).

Запрос:

curl http://localhost:5000/users/1

Ответ:

{
  "id": 1,
  "name": "Alice",
  "email": "[email protected]"
}

Запрос:

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": "[email protected]"}' http://localhost:5000/users/1

Ответ:

{}

Используйте GET для проверки обновленных сведений о пользователе

curl http://localhost:5000/users/1

Ответ:

{
  "id": 1,
  "name": "Alice",
  "email": "[email protected]"
}

Запрос:

curl -X PUT -H "Content-Type: application/json" -d '{"name": "Eve", "email": "[email protected]"}' 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.

Для получения дополнительной информации и примеров вы можете обратиться к официальной документации Flask и Flask-RESTful, доступной по адресу:

Надеюсь, вам понравился этот урок и вы сочли его полезным. Подписывайтесь на меня [Кришнаншу Ратхор] и получайте больше подобных статей. Удачного кодирования!