Статьи

Создание RESTful API с помощью Flask: подход DIY

REpresentational State Transfer (REST) — это стиль проектирования архитектуры веб-разработки, который подразумевает логическое разделение ресурсов API для обеспечения легкого доступа, манипулирования и масштабирования. Многократно используемые компоненты написаны таким образом, чтобы ими можно было легко управлять с помощью простых и интуитивно понятных HTTP-запросов, которые могут быть GET, POST, PUT, PATCH и DELETE (их может быть больше, но выше перечислены наиболее часто используемые).

Несмотря на то, как это выглядит, REST не управляет протоколом или стандартом. Он просто устанавливает программный архитектурный стиль для написания веб-приложений и API и приводит к упрощению интерфейсов внутри и вне приложения. API веб-служб, написанные так, чтобы следовать принципам REST, называются API RESTful.

В этой серии из трех частей я расскажу о различных способах создания API-интерфейсов RESTful с использованием Flask в качестве веб-инфраструктуры. В первой части я расскажу о том, как создавать REST API на основе классов, которые больше похожи на DIY (сделай сам), то есть реализовывать их все самостоятельно, без использования сторонних расширений. В последних частях этой серии я расскажу, как использовать различные расширения Flask для упрощения создания более эффективных API REST.

Я предполагаю, что у вас есть общее представление о лучших методах настройки Flask и среды, использующих virtualenv, которым необходимо следовать при разработке приложения Python.

Следующие пакеты должны быть установлены для приложения, которое мы будем разрабатывать.

1
2
$ pip install flask
$ pip install flask-sqlalchemy

Приведенные выше команды должны установить все необходимые пакеты, необходимые для работы этого приложения.

Для этого урока я создам небольшое приложение, в котором я создам тривиальную модель для Product . Затем я продемонстрирую, как мы можем написать RESTful API для того же самого. Ниже приведена структура приложения.

1
2
3
4
5
6
7
8
flask_app/
   my_app/
       — __init__.py
       product/
           — __init__.py // Empty file
           — models.py
           — views.py
   — run.py

Я не буду создавать интерфейс для этого приложения, поскольку конечные точки API RESTful можно тестировать напрямую, используя HTTP-вызовы, используя различные другие методы.

01
02
03
04
05
06
07
08
09
10
11
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
 
app = Flask(__name__)
app.config[‘SQLALCHEMY_DATABASE_URI’] = ‘sqlite:////tmp/test.db’
db = SQLAlchemy(app)
 
from my_app.catalog.views import catalog
app.register_blueprint(catalog)
 
db.create_all()

В приведенном выше файле приложение было настроено с инициализацией расширений и, наконец, созданием базы данных. Последний оператор создает новую базу данных в расположении, SQLALCHEMY_DATABASE_URI если база данных еще не существует в этом месте, в противном случае он загружает приложение с той же базой данных.

01
02
03
04
05
06
07
08
09
10
11
12
13
from my_app import db
 
class Product(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    price = db.Column(db.Float(asdecimal=True))
 
    def __init__(self, name, price):
        self.name = name
        self.price = price
 
    def __repr__(self):
        return ‘<Product %d>’ % self.id

В приведенном выше файле я создал очень тривиальную модель для хранения названия и цены Product . Это создаст таблицу в SQLite соответствующую деталям, представленным в модели.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import json
from flask import request, jsonify, Blueprint, abort
from flask.views import MethodView
from my_app import db, app
from my_app.catalog.models import Product
 
catalog = Blueprint(‘catalog’, __name__)
 
@catalog.route(‘/’)
@catalog.route(‘/home’)
def home():
    return «Welcome to the Catalog Home.»
 
 
class ProductView(MethodView):
 
    def get(self, id=None, page=1):
        if not id:
            products = Product.query.paginate(page, 10).items
            res = {}
            for product in products:
                res[product.id] = {
                    ‘name’: product.name,
                    ‘price’: str(product.price),
                }
        else:
            product = Product.query.filter_by(id=id).first()
            if not product:
                abort(404)
            res = {
                ‘name’: product.name,
                ‘price’: str(product.price),
            }
        return jsonify(res)
 
    def post(self):
        name = request.form.get(‘name’)
        price = request.form.get(‘price’)
        product = Product(name, price)
        db.session.add(product)
        db.session.commit()
        return jsonify({product.id: {
            ‘name’: product.name,
            ‘price’: str(product.price),
        }})
 
    def put(self, id):
        # Update the record for the provided id
        # with the details provided.
        return
 
    def delete(self, id):
        # Delete the record for the provided id.
        return
 
 
product_view = ProductView.as_view(‘product_view’)
app.add_url_rule(
    ‘/product/’, view_func=product_view, methods=[‘GET’, ‘POST’]
)
app.add_url_rule(
    ‘/product/<int:id>’, view_func=product_view, methods=[‘GET’]
)

Основная суть этого урока рассматривается в файле выше. Flask предоставляет утилиту, называемую подключаемыми представлениями , которая позволяет вам создавать представления в форме классов, а не обычно как функции. Диспетчеризация на основе MethodView ( MethodView ) — это реализация подключаемых представлений, которая позволяет писать методы, соответствующие HTTP-методам, в нижнем регистре. В приведенном выше примере я написал методы get() и post() соответствующие HTTP GET и POST соответственно.

Маршрутизация также реализована другим способом, в нескольких последних строках вышеуказанного файла. Мы можем указать методы, которые будут поддерживаться любым конкретным правилом. Любой другой HTTP-вызов будет встречен Error 405 Method not allowed .

Для запуска приложения выполните скрипт run.py Содержимое этого скрипта:

1
2
from my_app import app
app.run(debug=True)

Теперь просто выполните из командной строки:

1
$ python run.py

Чтобы проверить, работает ли приложение, запустите http://127.0.0.1:5000/ в своем браузере, и вас должен приветствовать простой экран с приветственным сообщением.

Чтобы протестировать этот API, мы можем просто сделать HTTP-вызовы, используя любой из многих доступных методов. GET звонки могут быть сделаны непосредственно через браузер. Вызовы POST могут быть сделаны с использованием расширения Chrome, такого как Postman, или из командной строки с помощью curl , или мы можем использовать библиотеку requests Python, чтобы сделать эту работу за нас. Я буду использовать библиотеку запросов здесь для демонстрационных целей.

Давайте сначала сделаем GET вызов, чтобы убедиться, что у нас еще нет созданных продуктов. Согласно дизайну RESTful API, вызов get, который выглядит примерно как /product/ должен содержать список всех продуктов. Затем я создам пару продуктов, сделав POST вызовы /product/ с некоторыми данными. Затем вызов GET для /product/ должен перечислить все созданные продукты. Чтобы получить конкретный продукт, нужно выполнить вызов GET для /product/<product id> . Ниже приведен пример всех вызовов, которые можно сделать с помощью этого примера.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
$ pip install requests
$ python
>>> import requests
>>> r = requests.get(‘http://localhost:5000/product/’)
>>> r.json()
{}
>>> r = requests.post(‘http://localhost:5000/product/’, data={‘name’: ‘iPhone 6s’, ‘price’: 699})
>>> r.json()
{u’1′: {u’price’: u’699.0000000000′, u’name’: u’iPhone 6s’}}
>>> r = requests.post(‘http://localhost:5000/product/’, data={‘name’: ‘iPad Pro’, ‘price’: 999})
>>> r.json()
{u’2′: {u’price’: u’999.0000000000′, u’name’: u’iPad Pro’}}
>>> r = requests.get(‘http://localhost:5000/product/’)
>>> r.json()
{u’1′: {u’price’: u’699.0000000000′, u’name’: u’iPhone 6s’}, u’2′: {u’price’: u’999.0000000000′, u’name’: u’iPad Pro’}}
>>> r = requests.get(‘http://localhost:5000/product/1’)
>>> r.json()
{u’price’: u’699.0000000000′, u’name’: u’iPhone 6s’}

В этом уроке вы увидели, как самостоятельно создавать интерфейсы RESTful с помощью утилиты Flask с подключаемыми представлениями. Это наиболее гибкий подход при написании API REST, но он требует гораздо больше кода для написания.

Существуют расширения, которые немного облегчают жизнь и в значительной степени автоматизируют реализацию API RESTful. Я расскажу об этом в следующих нескольких частях этого урока.