Вступление
Django - это мощная веб-платформа Python, используемая для быстрого создания безопасных масштабируемых веб-приложений с меньшими усилиями. Он стал популярным из-за низкого барьера для входа и сильного сообщества, которое использует и развивает фреймворк.
В этом руководстве мы собираемся создать RESTful API с использованием Django без каких-либо внешних библиотек . Мы рассмотрим основы Django и реализуем API на основе JSON для выполнения операций CRUD для приложения корзины покупок.
Что такое REST API?
REST (передача репрезентативного состояния)
- это стандартная архитектура для создания веб-сервисов и взаимодействия с ними. Обычно он требует, чтобы ресурсы в Интернете были представлены в текстовом формате (например, JSON, HTML или XML), и к ним можно получить доступ или изменить их с помощью заранее определенного набора операций. Учитывая, что мы обычно создаем REST API для использования с HTTP вместо других протоколов, эти операции соответствуют HTTP-методам, таким как GET, POST или PUT.
API (интерфейс прикладного программирования) , как следует из названия, представляет собой интерфейс, который определяет взаимодействие между различными программными компонентами. Веб-API определяют, какие запросы могут быть сделаны к компоненту (например, конечной точке для получения списка элементов корзины покупок), как их выполнять (например, запрос GET) и их ожидаемые ответы.
Мы объединяем эти две концепции для создания REST (ful) API , API, который соответствует ограничениям архитектурного стиля REST. Давайте сделаем один, используя Python и Django.
Настройка Django и нашего приложения
Как упоминалось ранее, Django - это веб-платформа, которая способствует быстрой разработке безопасных и масштабируемых веб-сервисов.
Примечание. Мы будем использовать Django версии 3.1, так как это последняя версия на момент написания.
Перед установкой Django, для удобства и во имя изоляции зависимостей, давайте создадим виртуальную среду:
$ python3 -m venv env
В некоторых редакторах кода вы обнаружите, что он уже активирован. Если
нет, вы можете перейти в каталог сценариев внутри среды и запустить
activate
.
В Windows:
$ env\scripts\activate
На Mac или Linux:
$ . env/bin/activate
Теперь давайте продолжим и установим Django через pip
:
$ pip install django
После установки мы можем создать наш проект. Хотя вы можете сделать это вручную, гораздо удобнее начать со скелетного проекта через сам Django.
Инструмент django-admin
позволяет нам создать пустой скелетный проект,
над которым мы можем немедленно начать работу. Он поставляется в
комплекте с самим Django, поэтому дальнейшая установка не требуется.
Давайте начнем проект с вызова инструмента, а также команды
startproject
, за которой следует имя проекта:
$ django-admin startproject shopping_cart
Это создает простой скелет проекта в рабочем каталоге. Каждый проект
Django может содержать несколько приложений, но мы создадим одно.
Давайте вызовем manage.py
, созданный с помощью команды startproject
для запуска приложения:
$ cd shopping_cart
$ python manage.py startapp api_app
После создания наша структура проекта будет выглядеть примерно так:
> env
> shopping_cart
> api_app
> migrations
__init__.py
admin.py
apps.py
models.py
tests.py
views.py
> shopping_cart
__init__.py
asgi.py
settings.py
urls.py
wsgi.py
manage.py
shopping_cart
верхнего уровня - это корневой каталог Django, имя
которого совпадает с именем проекта. Корневой каталог является мостом
между фреймворком и самим проектом и содержит несколько классов
настройки, таких как manage.py
который используется для запуска
приложений.
api_app
- это приложение, которое мы развиваем, и их может быть много.
У каждого есть несколько скриптов по умолчанию, которые мы будем
модифицировать, чтобы приспособить к функциональности CRUD.
Низкоуровневая shopping-cart
- это каталог проекта , который
содержит файлы, связанные с settings.py
, такие как settings.py, в
которых будут храниться все свойства нашего приложения.
Django - это модель-представление-контроллер (MVC) . Это шаблон проектирования, который разделяет приложение на три компонента: модель, которая определяет данные, которые хранятся и с которыми происходит взаимодействие, представление, которое описывает, как данные представляются пользователю, и контроллер, который действует как посредник между моделью и Посмотреть. Однако интерпретация этого шаблона в Django немного отличается от стандартной интерпретации. Например, в стандартной платформе MVC логика, обрабатывающая HTTP-запросы для управления элементами корзины покупок, будет находиться в контроллере.
В Django эта логика находится в файле, содержащем представления . Подробнее об их интерпретации вы можете прочитать здесь . Понимание основной концепции MVC, а также интерпретации Django упрощает понимание структуры этого приложения.
В каждом проекте Django предустановлено несколько приложений (модулей)
Django. Они используются для аутентификации, авторизации, сеансов и т.
Д. Чтобы Django знал, что мы также хотели бы включить наше собственное
приложение api_app
, нам нужно внести его в список INSTALLED_APPS
Перечислим его, перейдя в settings.py
и изменив список, включив в него
наше собственное приложение:
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'api_app',
]
После внесения в список мы закончили с настройкой проекта. Однако мы еще не закончили с основами, на которых будет построено наше приложение. Перед разработкой функциональности CRUD нам понадобится модель , с которой мы будем работать в качестве нашей базовой структуры данных.
Примечание. При запуске проекта Django по умолчанию также подготавливается база данных SQLite для этого проекта. Вам вообще не нужно его настраивать - просто определение моделей и вызов соответствующих функций CRUD запустит скрытый процесс, который сделает все за вас.
Определение модели
Давайте начнем с простой базовой модели - CartItem
, который
представляет элемент, указанный на вымышленном веб-сайте электронной
коммерции. Чтобы определить модели, которые может подхватить Django, мы
модифицируем файл api_app/models.py
from django.db import models
class CartItem(models.Model):
product_name = models.CharField(max_length=200)
product_price = models.FloatField()
product_quantity = models.PositiveIntegerField()
Здесь мы используем встроенный db
, в котором есть пакет models
Класс Model
представляет собой модель . Он имеет различные поля,
такие как CharField
, IntegerField
и т. Д., Которые используются
для определения схемы модели в базе данных. Они отображаются под
капотом ORM Django, когда вы хотите сохранить экземпляр модели в базе
данных.
Существуют различные поля, и они предназначены для работы с реляционными базами данных, что мы и будем делать здесь. Однако для нереляционных баз данных он работает не очень хорошо из-за существенной разницы в способах хранения данных.
Если вы хотите работать с нереляционной базой данных, такой как MongoDB, ознакомьтесь с нашим Руководством по использованию Django MongoDB Engine .
Чтобы внести изменения в схемы модели, как мы только что сделали, нам нужно вызвать Django Migrations . Миграции выполнить довольно легко, однако вам придется запускать их каждый раз, когда вы хотите сохранить изменение в схеме.
Для этого мы вызовем файл manage.py
makemigrations
аргументы
makemigrations и migrate
$ python manage.py makemigrations # Pack model changes into a file
$ python manage.py migrate # Apply those changes to the database
Результатом migrate
должно стать что-то вроде этого:
Operations to perform:
Apply all migrations: admin, api_app, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying api_app.0001_initial... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying sessions.0001_initial... OK
Если миграция не прошла успешно, просмотрите предыдущие шаги, чтобы убедиться, что вы правильно настроили, прежде чем продолжить.
Сайт администратора Django
При создании приложений Django автоматически создает админ-сайт , предназначенный для использования разработчиком для тестирования и предоставления им доступа к формам, созданным для зарегистрированных моделей. Он предназначен только для использования в качестве удобной панели инструментов во время разработки, а не как фактическая панель управления, которую вы создадите отдельно, если хотите ее иметь.
Модуль admin
django.contrib
- это пакет, который позволяет нам
настраивать сайт администратора .
Чтобы использовать автоматическое создание форм и управление моделями в
Django, нам нужно зарегистрировать нашу модель на сайте admin.site
.
Заходим в api_app/admin.py
и зарегистрируем нашу модель:
from django.contrib import admin
from .models import CartItem
admin.site.register(CartItem)
Теперь мы хотим создать пользователя, который сможет получить доступ к этой панели инструментов и использовать ее. Давайте создадим учетную запись суперадмина и подтвердим, что все эти изменения были успешно внесены:
$ python manage.py createsuperuser
Чтобы создать учетную запись, вам необходимо указать имя пользователя, адрес электронной почты и пароль. Вы можете оставить поле пустым. Пароль не отображается при вводе. Просто введите и нажмите Enter:
Username (leave blank to use 'xxx'): naazneen
Email address:
Password:
Password (again):
Superuser created successfully.
Наконец, давайте запустим наше приложение, чтобы убедиться, что все работает так, как задумано:
$ python manage.py runserver
По умолчанию приложение запускается на нашем localhost
127.0.0.1
(127.0.0.1) на порту 8000
Давайте перейдем в браузер к
http://127.0.0.1:8000/admin
:
{.ezlazyload}
Теперь, когда наши модели приложения и базы данных настроены, давайте сосредоточимся на разработке REST API.
Создание REST API в Django
Приложение Django полностью настроено - параметры определены, наше приложение подготовлено, модель создана, и мы создали пользователя-администратора, чтобы убедиться, что модель зарегистрирована на панели администратора.
Теперь давайте реализуем функциональность CRUD для нашей модели.
Создание объектов - обработчик запросов POST
POST
используются для отправки данных на сервер. Обычно они содержат
в своем теле данные, которые предполагается сохранить. При заполнении
форм, загрузке изображений или отправке сообщения - POST
отправляются
с этими данными, которые затем обрабатываются и сохраняются.
Давайте создадим представление Django, чтобы принимать данные от клиента, заполнять ими экземпляр модели и добавлять его в базу данных. По сути, мы сможем добавить товар в нашу корзину с помощью нашего API. Представления в Django можно писать просто как функции или как методы класса. Мы собираемся использовать представления на основе классов .
Чтобы добавить представление, мы api_app_views.py
файл
api_app_views.py и добавим post()
который получает запрос POST
Он
запишет тело входящего запроса в словарь и создаст CartItem
, сохраняя
его в базе данных:
from django.views import View
from django.http import JsonResponse
import json
from .models import CartItem
class ShoppingCart(View):
def post(self, request):
data = json.loads(request.body.decode("utf-8"))
p_name = data.get('product_name')
p_price = data.get('product_price')
p_quantity = data.get('product_quantity')
product_data = {
'product_name': p_name,
'product_price': p_price,
'product_quantity': p_quantity,
}
cart_item = CartItem.objects.create(**product_data)
data = {
"message": f"New item added to Cart with id: {cart_item.id}"
}
return JsonResponse(data, status=201)
Используя json
, мы декодировали и проанализировали тело входящего
запроса в объект, с которым мы можем работать, а затем извлекли эти
данные в переменные p_name
, p_price
и p_quantity
.
Наконец, мы создали product_data
для хранения наших полей и их
значений и сохранили CartItem
в нашей базе данных с помощью метода
create()
Model
, заполнив его нашими product_data
.
Обратите внимание на использование JsonResponse
в конце. Мы используем
этот класс для преобразования нашего словаря Python в действительный
объект JSON, который отправляется по HTTP обратно клиенту. Мы
устанавливаем код состояния 201, чтобы обозначить создание ресурса на
стороне сервера.
Если мы запустим наше приложение и попытаемся попасть в эту конечную точку, Django отклонит запрос с ошибкой безопасности. По умолчанию Django добавляет уровень защиты от атак с подделкой межсайтовых запросов (CSRF) . На практике этот токен хранится в файлах cookie нашего браузера и отправляется с каждым запросом к серверу. Поскольку этот API будет использоваться без браузера или файлов cookie, запросы никогда не будут содержать токен CSRF. Следовательно, мы должны сообщить Django, что этому методу POST не нужен токен CSRF.
Мы можем добиться этого, добавив декоратор к dispatch
нашего класса,
который установит для csrf_exempt
значение True
:
...
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
@method_decorator(csrf_exempt, name='dispatch')
class ShoppingCart(View):
def post(self, request):
data = json.loads(request.body.decode("utf-8"))
...
Теперь у нас есть модели, в которых хранятся наши данные, и представление, которое может создавать новый элемент корзины с помощью HTTP-запроса. Осталось только указать Django, как обращаться с URL-адресами и соответствующими обработчиками. Для каждого доступного URL-адреса у нас будет соответствующее отображение представления, которое его обрабатывает.
Считается хорошей практикой писать соответствующие URL-адреса в каждом
приложении, а затем включать их в файл urls.py проекта, вместо того,
чтобы с urls.py
Начнем с изменения urls.py
shopping_cart
:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('api_app.urls')),
]
Обязательно импортировать include
библиотеку из django.urls
, это не
импортируется по умолчанию.
Первый аргумент - это строковый путь, а второй - откуда мы получаем
URL-адреса. Если наш путь равен ''
или пустой, это означает, что
URL-адреса нашего API будут корневым путем веб-приложения .
Теперь нам нужно добавить конечные точки для urls.py
нашего приложения
API. В api_app
мы создадим новый файл с именем urls.py
:
from django.urls import path
from .views import ShoppingCart
urlpatterns = [
path('cart-items/', ShoppingCart.as_view()),
]
Подобно собственному urls.py
, первый аргумент - это подпуть, по
которой будут доступны наши представления, а второй аргумент - сами
представления.
Наконец, мы можем запустить приложение. Как и раньше, мы будем
использовать manage.py
и передать аргумент runserver
$ python manage.py runserver
Давайте откроем терминал и отправим POST
на нашу конечную точку. Здесь
вы можете использовать любой инструмент - от более продвинутых
инструментов, таких как Postman, до простых инструментов на основе
интерфейса командной строки, таких как curl
:
$ curl -X POST -H "Content-Type: application/json" http://127.0.0.1:8000/car
t-items/ -d "{\"product_name\":\"name\",\"product_price\":\"41\",\"product_quantity\":\"1\"}"
Если все работает нормально, вы увидите сообщение:
{
"message": "New item added to Cart with id: 1"
}
Если вы посетите http://127.0.0.1:8000/admin/api_app/cartitem/
,
добавленный нами товар также будет в списке.
Получение сущностей - обработчик запроса GET
Давайте создадим обработчик для GET
, которые обычно отправляются
клиентами, когда они хотят получить некоторую информацию. Поскольку у
нас есть CartItem
сохраненный в базе данных, имеет смысл, что кто-то
захочет получить информацию о нем.
Предполагая, что существует более одного элемента, мы CartItem
все
записи CartItem и добавим их атрибуты в словарь, который легко
преобразуется в ответ JSON для клиента.
Давайте изменим представление ShoppingCart
:
...
@method_decorator(csrf_exempt, name='dispatch')
class ShoppingCart(View):
def post(self, request):
...
def get(self, request):
items_count = CartItem.objects.count()
items = CartItem.objects.all()
items_data = []
for item in items:
items_data.append({
'product_name': item.product_name,
'product_price': item.product_price,
'product_quantity': item.product_quantity,
})
data = {
'items': items_data,
'count': items_count,
}
return JsonResponse(data)
Метод count()
подсчитывает количество вхождений в базу данных, а метод
all()
извлекает их в список сущностей. Здесь мы извлекаем их данные и
возвращаем их в виде ответа JSON.
Отправим GET
запрос на нашу конечную точку:
$ curl -X GET http://127.0.0.1:8000/cart-items/
Это приводит к ответу JSON клиенту:
{
"items": [
{
"product_name": "name",
"product_price": 41.0,
"product_quantity": 1
},
],
"count": 1
}
Обновление объектов - обработчик запросов PATCH
Мы можем сохранять и извлекать данные через наш API, хотя не менее важно
иметь возможность обновлять уже сохраненные сущности. Здесь в игру
вступают запросы PATCH
и PUT
PUT
полностью заменяет данный ресурс. В то время как PATCH
изменяет
часть данного ресурса.
В случае PUT
, если данный контекст ресурса не существует, он его
создаст. Для выполнения запроса PATCH
ресурс должен уже
существовать . Для этого приложения мы хотим обновлять ресурс, только
если он уже существует, поэтому мы будем использовать запрос PATCH
post()
и get()
находятся в одном классе представления ShoppingCart
Это потому, что они влияют на более чем один товар в корзине.
PATCH
влияет только на один товар в корзине. Итак, мы создадим
новый класс, который будет содержать это представление, а также будущий
метод delete()
Добавим обработчик запроса PATCH
api_app/views.py
:
...
@method_decorator(csrf_exempt, name='dispatch')
class ShoppingCartUpdate(View):
def patch(self, request, item_id):
data = json.loads(request.body.decode("utf-8"))
item = CartItem.objects.get(id=item_id)
item.product_quantity = data['product_quantity']
item.save()
data = {
'message': f'Item {item_id} has been updated'
}
return JsonResponse(data)
Мы получим элемент с заданным идентификатором и изменим его перед повторным сохранением.
Поскольку мы не хотим, чтобы покупатель мог изменять цену или название
продукта, мы делаем переменным только количество товара в корзине для
покупок. Затем мы вызываем метод save()
для обновления уже
существующей сущности в базе данных.
Теперь нам нужно зарегистрировать конечную точку для этого, так же, как
мы это сделали для cart-items/
endpoint:
api_app/urls.py
:
from django.urls import path
from .views import ShoppingCart, ShoppingCartUpdate
urlpatterns = [
path('cart-items/', ShoppingCart.as_view()),
path('update-item/<int:item_id>', ShoppingCartUpdate.as_view()),
]
На этот раз мы полагаемся не только на HTTP-глагол. В прошлый раз
отправка запроса POST
/cart-items
привела к post()
, а отправка
GET
привела к запуску метода get()
Здесь мы добавляем URL-переменную - /<int:item_id>
. Это динамический
компонент в URL-адресе, который сопоставляется с item_id
из
представления. На основе предоставленного значения соответствующий
элемент извлекается из базы данных.
Отправим запрос PATCH
http:127.0.0.1:8000/update-item/1
с
соответствующими данными:
$ curl -X PATCH http://127.0.0.1:8000/update-item/1 -d "{\"product_quantity\":\"3\"}"
Это приводит к ответу JSON:
{
"message": "Item 1 has been updated"
}
Давайте также проверим это через панель администратора по адресу:
http://127.0.0.1:8000/admin/api_app/cartitem/1/change/
.
Удаление объектов - обработчик запроса DELETE
Наконец, последняя часть функциональности CRUD - удаление сущностей.
Чтобы удалить товар из корзины, мы будем использовать тот же
ShoppingCartUpdate
поскольку он влияет только на один товар. Мы
добавим delete()
который принимает идентификатор элемента, который мы
хотим удалить.
Подобно тому, как мы save()
элемент при обновлении его новыми
значениями, мы можем использовать delete()
для его удаления. Добавим
метод delete()
в api_app/views.py
:
...
@method_decorator(csrf_exempt, name='dispatch')
class ShoppingCartUpdate(View):
def patch(self, request, item_id):
...
def delete(self, request, item_id):
item = CartItem.objects.get(id=item_id)
item.delete()
data = {
'message': f'Item {item_id} has been deleted'
}
return JsonResponse(data)
А теперь давайте отправим DELETE
и предоставим идентификатор элемента,
который мы хотим удалить:
curl -X "DELETE" http://127.0.0.1:8000/update-item/1
И мы получим такой ответ:
{
"message": "Item 1 has been deleted"
}
Посещение http://127.0.0.1:8000/admin/api_app/cartitem/
подтверждает,
что элемента больше нет.
Заключение
В этом кратком руководстве мы рассмотрели, как создать REST API в Python с помощью Django . Мы рассмотрели некоторые основы Django, начали новый проект и приложение в нем, определили необходимые модели и реализовали функциональность CRUD.
Полный код этого приложения можно найти здесь .