Как создать 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 using virtualenv venv.
  • Активируйте виртуальную среду, используя source venv/bin/activateLinux/Mac или venv\Scripts\activateWindows.
  • Создайте файл с именем 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.