Создание приложения Todo с помощью Flask на Python

Введение В этом руководстве мы собираемся создать API или веб-службу для приложения todo. Служба API будет реализована с использованием архитектуры на основе REST. Наше приложение будет иметь следующие основные функции: * Создание элемента в списке задач * Прочитать полный список задач * Обновить элементы со статусом «Не начато», «Выполняется» или «Завершено» * Удалить элементы из list Что такое REST? REST или REpresentational State Transfer - это архитектурный стиль для создания веб-сервисов.

Вступление

В этом руководстве мы собираемся создать API или веб-службу для приложения todo. Служба API будет реализована с использованием архитектуры на основе REST.

Наше приложение будет иметь следующие основные функции:

  • Создать элемент в списке дел
  • Прочтите полный список задач
  • Обновите элементы со статусом «Не начато», «Выполняется» или «Завершено».
  • Удалить элементы из списка

Что такое ОТДЫХ?

REST или REpresentational State Transfer - это архитектурный стиль для создания веб-сервисов и API. Это требует, чтобы системы, реализующие REST, не имели состояния. Клиент отправляет серверу запрос на извлечение или изменение ресурсов, не зная, в каком состоянии находится сервер. Серверы отправляют ответ клиенту, не имея необходимости знать, какой был предыдущий обмен данными с клиентом.

Каждый запрос к системе RESTful обычно использует эти 4 HTTP-команды:

  • GET : получить определенный ресурс или набор ресурсов.
  • POST : создать новый ресурс
  • PUT : обновить определенный ресурс
  • УДАЛИТЬ : удалить определенный ресурс.

Хотя другие разрешены и иногда используются, например PATCH , HEAD и OPTIONS .

Что такое Flask?

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

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

Настройка Flask

Во-первых, давайте продолжим и установим Flask с помощью pip:

 $ pip install Flask 

Давайте быстро настроим Flask и развернем веб-сервер на нашей локальной машине. Создайте файл main.py в каталоге todo_service_flask

 from flask import Flask 
 app = Flask(__name__) 
 
 @app.route('/') 
 def hello_world(): 
 return 'Hello World!' 

После импорта Flask мы настроили маршрут . Маршрут определяется шаблоном URL, методом HTTP и функцией, которая получает и обрабатывает HTTP-запрос. Мы связали этот маршрут с функцией Python, которая будет вызываться каждый раз, когда URL-адрес запрашивается через HTTP. В этом случае мы настроили корневой (/) маршрут так, чтобы к нему можно было получить доступ по шаблону URL http://[IP-OR-DOMAIN]:[PORT]/ .

Запуск приложения Flask

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

К счастью, все это можно сделать с помощью одной простой команды:

 $ FLASK_APP=main.py flask run 

Вы должны увидеть сообщение в консоли:

 Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 

Мы можем использовать cURL для запуска запроса GET. Если вы используете Mac, cURL уже должен быть установлен в вашей системе:

 $ curl -X GET http://127.0.0.1:5000/ 

Нас должен встретить ответ:

 Hello World! 

На этом история не заканчивается. Давайте продолжим и структурируем наше приложение Todo.

Структурирование приложения Todo

Наше приложение Todo будет иметь несколько основных функций:

  • Добавление элементов в список
  • Получение всех предметов из списка
  • Обновление элемента в списке
  • Удаление элемента из списка

Их часто называют операциями CRUD для создания, чтения, обновления и удаления .

Мы будем использовать базу данных SQLite для хранения данных, которая представляет собой очень легкую файловую базу данных. Вы можете установить Браузер БД для SQLite, чтобы легко создавать базу данных.

Назовем эту базу данных todo.db и поместим ее в каталог todo_service_flask . Теперь, чтобы создать таблицу, мы запускаем простой запрос:

 CREATE TABLE "items" ( 
 "item" TEXT NOT NULL, 
 "status" TEXT NOT NULL, 
 PRIMARY KEY("item") 
 ); 

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

Мы также будем использовать еще один файл для хранения наших вспомогательных функций. Эти функции будут иметь бизнес-логику для обработки запроса путем подключения к базе данных и выполнения соответствующих запросов.

Как только вы освоитесь с этой исходной структурой Flask, вы можете реструктурировать свое приложение как хотите.

Создание приложения

Чтобы избежать многократного написания логики для часто выполняемых задач, таких как добавление элементов в базу данных, мы можем определить вспомогательные функции в отдельном файле и просто вызывать их при необходимости. В этом руководстве мы helper.py файл helper.py.

Добавление предметов

Для реализации этой функции нам понадобятся две вещи:

  • Вспомогательная функция, содержащая бизнес-логику для добавления нового элемента в базу данных.
  • Маршрут, который должен вызываться всякий раз, когда достигается конкретная конечная точка HTTP.

Сначала давайте определим некоторые константы и напишем add_to_list() :

 import sqlite3 
 
 DB_PATH = './todo.db' # Update this path accordingly 
 NOTSTARTED = 'Not Started' 
 INPROGRESS = 'In Progress' 
 COMPLETED = 'Completed' 
 
 def add_to_list(item): 
 try: 
 conn = sqlite3.connect(DB_PATH) 
 
 # Once a connection has been established, we use the cursor 
 # object to execute queries 
 c = conn.cursor() 
 
 # Keep the initial status as Not Started 
 c.execute('insert into items(item, status) values(?,?)', (item, NOTSTARTED)) 
 
 # We commit to save the change 
 conn.commit() 
 return {"item": item, "status": NOTSTARTED} 
 except Exception as e: 
 print('Error: ', e) 
 return None 

Эта функция устанавливает соединение с базой данных и выполняет запрос на вставку. Он возвращает вставленный элемент и его статус.

Затем мы импортируем несколько модулей и настроим маршрут для пути /item/new :

 import helper 
 from flask import Flask, request, Response 
 import json 
 
 app = Flask(__name__) 
 
 @app.route('/') 
 def hello_world(): 
 return 'Hello World!' 
 
 @app.route('/item/new', methods=['POST']) 
 def add_item(): 
 # Get item from the POST body 
 req_data = request.get_json() 
 item = req_data['item'] 
 
 # Add item to the list 
 res_data = helper.add_to_list(item) 
 
 # Return error if item not added 
 if res_data is None: 
 response = Response("{'error': 'Item not added - " + item + "'}", status=400 , mimetype='application/json') 
 return response 
 
 # Return response 
 response = Response(json.dumps(res_data), mimetype='application/json') 
 
 return response 

Модуль request используется для анализа запроса и получения данных тела HTTP или параметров запроса из URL-адреса. response используется для возврата ответа клиенту. Ответ имеет тип JSON .

Если вы хотите узнать больше о чтении и написании JSON в Python , мы вам поможем!

Мы вернули статус 400, если элемент не был добавлен из-за какой-то клиентской ошибки. Функция json.dumps() преобразует объект или словарь Python в действительный объект JSON.

Давайте сохраним код и проверим, правильно ли реализована наша функция.

Мы можем использовать cURL для отправки запроса POST и тестирования нашего приложения. Нам также нужно передать имя элемента в качестве тела POST:

 $ curl -X POST http://127.0.0.1:5000/item -d '{"item": "Setting up Flask"}' -H 'Content-Type: application/json' 

Если вы работаете в Windows, вам нужно отформатировать данные JSON из одинарных кавычек в двойные кавычки и избежать их:

 $ curl -X POST http://127.0.0.1:5000/item -d "{\"item\": \"Setting up Flask\"}" -H 'Content-Type: application/json' 

Обратите внимание на следующее:

  • Наш URL-адрес состоит из двух частей - базового URL-адреса ( http://127.0.0.1:5000 ) и маршрута или пути ( /item/new ).
  • Метод запроса - POST
  • Как только запрос попадает на веб-сервер, он пытается найти конечную точку на основе этой информации.
  • Мы передаем данные в формате JSON - {"item": "Настройка Flask"}

Когда мы запускаем запрос, мы должны получить ответ:

 {"Setting up Flask": "Not Started"} 

Давайте запустим следующую команду, чтобы добавить еще один элемент в список:

 $ curl -X POST http://127.0.0.1:5000/item -d '{"item": "Implement POST endpoint"}' -H 'Content-Type: application/json' 

Нас должен встретить ответ, который показывает нам описание задачи и ее статус:

 {"Implement POST endpoint": "Not Started"} 

Поздравляем !!! Мы успешно реализовали функцию добавления элемента в список дел.

Получение всех элементов

Мы часто хотим получить все элементы из списка, что, к счастью, очень просто:

 def get_all_items(): 
 try: 
 conn = sqlite3.connect(DB_PATH) 
 c = conn.cursor() 
 c.execute('select * from items') 
 rows = c.fetchall() 
 return { "count": len(rows), "items": rows } 
 except Exception as e: 
 print('Error: ', e) 
 return None 

Эта функция устанавливает соединение с базой данных и создает запрос SELECT, а затем выполняет его через c.fetchall() . Это возвращает все записи, возвращенные запросом SELECT. Если нас интересует только один элемент, мы можем вместо этого вызвать c.fetchone() .

Наш метод get_all_items возвращает объект Python, содержащий 2 элемента:

  • Количество элементов, возвращаемых этим запросом
  • Фактические элементы, возвращенные запросом

В main.py мы определим маршрут /item/new который принимает запрос GET. Здесь мы не будем передавать аргумент ключевого слова methods @app.route() , потому что, если мы пропустим этот параметр, по умолчанию будет GET :

 @app.route('/items/all') 
 def get_all_items(): 
 # Get items from the helper 
 res_data = helper.get_all_items() 
 
 # Return response 
 response = Response(json.dumps(res_data), mimetype='application/json') 
 return response 

Давайте воспользуемся cURL для получения элементов и тестирования нашего маршрута:

 $ curl -X GET http://127.0.0.1:5000/items/all 

Нас должен встретить ответ:

json {"count": 2, "items": [["Setting up Flask", "Not Started"], [Implement POST endpoint", "Not Started"]]}

Получение статуса отдельных элементов

Как и в предыдущем примере, мы напишем для этого вспомогательную функцию:

 def get_item(item): 
 try: 
 conn = sqlite3.connect(DB_PATH) 
 c = conn.cursor() 
 c.execute("select status from items where item='%s'" % item) 
 status = c.fetchone()[0] 
 return status 
 except Exception as e: 
 print('Error: ', e) 
 return None 

Мы также определим маршрут в main.py для анализа запроса и предоставления ответа. Нам нужен маршрут для приема запроса GET, а имя элемента должно быть отправлено в качестве параметра запроса.

Параметр запроса передается в формате ?name=value вместе с URL-адресом. например http://base-url/path/to/resource/?name=value . Если в значении есть пробелы, вам необходимо заменить их на + или на %20 , что является версией пробела в кодировке URL. У вас может быть несколько пар имя-значение, разделив их символом &

Вот некоторые из допустимых примеров параметров запроса:

  • http://127.0.0.1:8080/search?query=what+is+flask
  • http://127.0.0.1:8080/search?category=mobiles&brand=apple
1
<!-- -->
 @app.route('/item/status', methods=['GET']) 
 def get_item(): 
 # Get parameter from the URL 
 item_name = request.args.get('name') 
 
 # Get items from the helper 
 status = helper.get_item(item_name) 
 
 # Return 404 if item not found 
 if status is None: 
 response = Response("{'error': 'Item Not Found - %s'}" % item_name, status=404 , mimetype='application/json') 
 return response 
 
 # Return status 
 res_data = { 
 'status': status 
 } 
 
 response = Response(json.dumps(res_data), status=200, mimetype='application/json') 
 return response 

Опять же, давайте использовать cURL для запуска запроса:

 $ curl -X GET http://127.0.0.1:5000/item/status?name=Setting+up+Flask 

Нас должен встретить ответ:

 {"status": "Not Started"} 

Обновление предметов

Поскольку мы выполнили задачу «Настройка Flask» некоторое время назад, самое время обновить его статус до «Завершено».

Сначала напишем функцию в helper.py которая выполняет запрос на обновление:

 def update_status(item, status): 
 # Check if the passed status is a valid value 
 if (status.lower().strip() == 'not started'): 
 status = NOTSTARTED 
 elif (status.lower().strip() == 'in progress'): 
 status = INPROGRESS 
 elif (status.lower().strip() == 'completed'): 
 status = COMPLETED 
 else: 
 print("Invalid Status: " + status) 
 return None 
 
 try: 
 conn = sqlite3.connect(DB_PATH) 
 c = conn.cursor() 
 c.execute('update items set status=? where item=?', (status, item)) 
 conn.commit() 
 return {item: status} 
 except Exception as e: 
 print('Error: ', e) 
 return None 

Хорошая практика - не полагаться на вводимые пользователем данные и выполнять наши проверки, поскольку мы никогда не знаем, что конечный пользователь может сделать с нашим приложением. Здесь выполняются очень простые проверки, но если бы это было реальное приложение, мы бы хотели защитить себя от других вредоносных входных данных, таких как атаки с использованием SQL-инъекций.

Затем мы main.py который принимает метод PUT для обновления ресурса:

 @app.route('/item/update', methods=['PUT']) 
 def update_status(): 
 # Get item from the POST body 
 req_data = request.get_json() 
 item = req_data['item'] 
 status = req_data['status'] 
 
 # Update item in the list 
 res_data = helper.update_status(item, status) 
 
 # Return error if the status could not be updated 
 if res_data is None: 
 response = Response("{'error': 'Error updating item - '" + item + ", " + status + "}", status=400 , mimetype='application/json') 
 return response 
 
 # Return response 
 response = Response(json.dumps(res_data), mimetype='application/json') 
 
 return response 

Давайте использовать cURL для проверки этого маршрута, как и раньше:

 $ curl -X PUT http://127.0.0.1:5000/item/update -d '{"item": "Setting up Flask", "status": "Completed"}' -H 'Content-Type: application/json' 

Нас должен встретить ответ:

 {"Setting up Flask": "Completed"} 

Удаление элементов

Сначала мы напишем функцию в helper.py которая выполняет запрос на удаление:

 def delete_item(item): 
 try: 
 conn = sqlite3.connect(DB_PATH) 
 c = conn.cursor() 
 c.execute('delete from items where item=?', (item,)) 
 conn.commit() 
 return {'item': item} 
 except Exception as e: 
 print('Error: ', e) 
 return None 

Примечание : обратите внимание, что (item,) не является опечаткой. Нам нужно передать execute() кортеж, даже если в кортеже есть только один элемент. Добавление запятой заставляет это стать кортежем.

Затем мы main.py который принимает запрос DELETE:

 @app.route('/item/remove', methods=['DELETE']) 
 def delete_item(): 
 # Get item from the POST body 
 req_data = request.get_json() 
 item = req_data['item'] 
 
 # Delete item from the list 
 res_data = helper.delete_item(item) 
 
 # Return error if the item could not be deleted 
 if res_data is None: 
 response = Response("{'error': 'Error deleting item - '" + item + "}", status=400 , mimetype='application/json') 
 return response 
 
 # Return response 
 response = Response(json.dumps(res_data), mimetype='application/json') 
 
 return response 

Давайте воспользуемся cURL для проверки нашего маршрута удаления:

 $ curl -X DELETE http://127.0.0.1:5000/item/remove -d '{"item": "Setting up Flask"}' -H 'Content-Type: application/json' 

Нас должен встретить ответ:

 {"item": "Temporary item to be deleted"} 

И это завершает приложение со всеми необходимыми функциями!

Заключение

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

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

Хотя Flask отлично подходит для управления HTML-шаблонами, большинство людей используют Flask для создания серверных служб и создания клиентской части приложения с помощью любой из популярных библиотек JavaScript. Вы можете попробовать то, что подходит вам лучше всего. Удачи в путешествии по Flask!

Если вы хотите поэкспериментировать с исходным кодом или у вас возникнут трудности с его запуском из приведенного выше кода, вот он, на GitHub !

comments powered by Disqus