Разработка API GraphQL в Django с помощью Strawberry
В постоянно меняющемся ландшафте веб-разработки предоставление эффективных и гибких API имеет решающее значение. В то время как RESTful API были основным продуктом на протяжении многих лет, GraphQL стал мощной альтернативой, предлагая большую гибкость и эффективность при поиске данных. Позволяя клиентам запрашивать именно то, что им нужно, GraphQL сокращает избыточную и недостаточную выборку данных, повышая производительность и удобство использования ваших приложений.
Среди многочисленных доступных библиотек GraphQL, Strawberry GraphQL выделяется своей простотой и удобством использования. Созданный с акцентом на безопасность типов и современные возможности Python, Strawberry использует подсказки по типам Python для создания схем GraphQL без особых усилий. А поддержка множества интеграций, позволяющая использовать его с вашим любимым веб-фреймворком, делает его отличным выбором для разработчиков на Python, которые хотят интегрировать GraphQL в свои проекты.
С другой стороны, Django, высокоуровневый веб-фреймворк на Python, стал популярным выбором для разработчиков благодаря своему надежному набору функций и философии "без батареек". Масштабируемость Django, функции безопасности и простота использования делают его идеальной платформой для создания веб-приложений любого размера.
В этом руководстве мы рассмотрим, как интегрировать Strawberry с Django для создания мощного и гибкого API. Мы рассмотрим настройку проекта Django, определение моделей и создание запросов GraphQL и мутаций с помощью Strawberry. К концу этого руководства у вас будет функциональный GraphQL API на базе Django, готовый точно и эффективно обслуживать ваши потребности в данных.
Давайте начнем!
Содержимое
- Почему именно клубничный
- Почему именно Django
- Что Мы создаем?
- Первоначальная настройка
- Добавление мутаций
- Тестирование установки
- Обработка ошибок
- Тестирование с помощью pytest
- Заключение
Почему Strawberry
Strawberry - это современная библиотека Python, предназначенная для упрощения создания API-интерфейсов GraphQL.
Вот несколько причин, по которым Strawberry является отличным выбором для создания GraphQL API:
- Безопасность типов: Strawberry использует подсказки по типам в Python для создания схем GraphQL. Это помогает обеспечить типобезопасность вашего кода, что может уменьшить количество ошибок и улучшить удобство сопровождения.
- Простота и удобоиспользование: Strawberry разработан с учетом простоты. Его API интуитивно понятен и легок в освоении, что делает его доступным как для начинающих, так и для опытных разработчиков.
- Современные возможности Python: Strawberry в полной мере использует современные возможности Python, такие как классы данных и синтаксис async/await. Это позволяет создавать более чистый и удобочитаемый код.
- Интеграция с популярными фреймворками Python: Strawberry предлагает полную интеграцию с популярными фреймворками Python, такими как Django, Flask и т.д., с примерами для начала работы. Это позволяет легко добавлять возможности GraphQL практически во все существующие веб-приложения.
- Сообщество и поддержка: У Strawberry активное и растущее сообщество. Разработчики отзывчивы, и доступно множество ресурсов, которые помогут вам начать работу и устранить неполадки.
Выбирая Strawberry, вы выбираете мощную и удобную в использовании библиотеку, которая упрощает создание и поддержку ваших API-интерфейсов GraphQL.
Почему именно Django
Django - это веб-фреймворк высокого уровня на Python, который способствует быстрой разработке и чистому, прагматичному дизайну.
Вот несколько причин, по которым Django является предпочтительным выбором для веб-разработки:
- Батарейки в комплекте: Django поставляется с множеством встроенных функций, таких как ORM, аутентификация, панель администратора и многое другое. Это сокращает количество времени, которое вы тратите на разработку шаблонного кода, и позволяет вам сосредоточиться на создании вашего приложения.
- Масштабируемость: Django разработан для работы с сайтами с высокой посещаемостью и может эффективно масштабироваться по мере роста вашего проекта. Он используется некоторыми из крупнейших веб-сайтов в мире, демонстрируя свою масштабируемость и надежность.
- Безопасность: Django предоставляет надежные функции безопасности "из коробки", защищая ваше приложение от распространенных угроз безопасности, таких как внедрение SQL, межсайтовый скриптинг (XSS) и подделка межсайтовых запросов (CSRF).
- Сообщество и документация: У Django большое и активное сообщество. Обширная документация и многочисленные учебные пособия облегчают разработчикам процесс обучения и получения поддержки.
- Модульность: Модульная структура Django позволяет использовать только те компоненты, которые вам нужны. Это делает его достаточно гибким для адаптации к различным требованиям проекта.
Объединяя Django с Strawberry, вы используете сильные стороны обоих фреймворков для создания надежного, масштабируемого и гибкого API. Эта интеграция позволяет вам воспользоваться преимуществами развитой экосистемы Django, одновременно используя эффективность и гибкость Graphql для обработки запросов к данным.
Что Мы Строим?
В этом руководстве мы создадим простое приложение для каталога книг, используя Django и Strawberry. Это приложение позволит пользователям управлять коллекцией книг и запрашивать информацию о ней, включая такие сведения, как название, автор и дата публикации.
Вот краткий обзор того, что мы рассмотрим:
- Определение модели: Мы определим модель
Book
с полями дляtitle
,author
, иpublished_date
. - Схема GraphQL: Используя Strawberry, мы создадим схему GraphQL для отображения данных нашей книги. Схема будет включать в себя:
- Введите
Query
, чтобы получить список книг. - A
Mutation
введите, чтобы добавить новые книги в каталог.
- Введите
- Запросы и изменения в GraphQL: Мы реализуем запросов для получения сведений о книге и мутаций для добавления новых книг.
- Тестирование: Мы напишем тесты, чтобы убедиться, что наши запросы GraphQL и мутации работают должным образом, используя pytest.
К концу этого урока у вас будет функциональный GraphQL API на базе Django, способный точно и эффективно управлять коллекцией книг.
Начальная настройка
Самым первым шагом является настройка среды Python и установка Django.
Создайте новую папку проекта, создайте и активируйте виртуальную среду и установите Django:
$ mkdir strawberry_tut && cd strawberry_tut
$ python3 -m venv .env
$ source .env/bin/activate
(.env)$ pip install django==5.0.6
Не стесняйтесь заменить virtualenv и Pip на Poetry или Pipenv. Для получения дополнительной информации ознакомьтесь с Современными средами Python.
Запускаем новый проект на django:
(.env)$ django-admin startproject strawberry_django_tut
(.env)$ cd strawberry_django_tut
Создайте приложение django:
(.env)$ python manage.py startapp book
Чтобы убедиться, что все работает как надо, запустите сервер Django:
(.env) python manage.py runserver
Перейдите к http://127.0.0.1:8000/, чтобы убедиться, что отображается домашняя страница приветствия Django.
Модель Django
Давайте определим нашу модель книги с соответствующими полями. В файле strawberry_django_tut/book/models.py добавьте следующий код
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=100)
published_date = models.DateField()
Добавьте приложение book
в список INSTALLED_APPS
в strawberry_django_tut/strawberry_django_tut/settings.py:
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"book",
]
Создайте и примените миграции:
(.env)$ python manage.py makemigrations
(.env)$ python manage.py migrate
Интеграция Strawberry
Чтобы добавить Strawberry в наше приложение, начните с установки strawberry-django:
(.env)$ pip install strawberry-graphql-django==0.44.2
После установки добавьте strawberry_django
в список INSTALLED_APPS
в файле настроек.
Затем создайте новый файл с именем strawberry_django_tut/book/schema.py:
from typing import List
import strawberry
import strawberry_django
from strawberry import auto
from .models import Book
@strawberry_django.type(Book)
class BookType:
id: auto
title: auto
author: auto
published_date: auto
@strawberry.type
class Query:
books: List[BookType] = strawberry_django.field()
schema = strawberry.Schema(query=Query)
Примечания:
- Мы преобразовали нашу модель
Book
в тип Strawberry, сопоставив поля модели с полями GraphQL с помощью@strawberry_django.type
декоратора. Утилитаauto
, предоставляемая Strawberry, используется для автоматического определения типов полей, обеспечивая согласованность между моделью Django и схемой GraphQL. - Класс
Query
определяет тип корневого запроса для нашего GraphQL API. ТипQuery
используется для определения того, какие запросы клиенты могут выполнять к нашим данным. В этом случае у него есть только полеbooks
, которое при запросе возвращает список изBookType
объектов. Поскольку мы сопоставили типBookType
с модельюBook
, запросbooks
автоматически запросит все объектыBook
из базы данных.
Создайте другой файл с именем urls.py в "strawberry_django_tut/book":
from django.urls import path
from strawberry.django.views import GraphQLView
from .schema import schema
urlpatterns = [
path("graphql/", GraphQLView.as_view(schema=schema), name="graphql"),
]
Наконец, обновите конфигурацию URL-адреса в strawberry_django_tut/strawberry_django_tut/urls.py:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("book.urls")),
]
Добавление мутаций
Помимо запроса данных, GraphQL позволяет изменять данные на сервере с помощью мутаций. Strawberry обрабатывает вводимые данные, позволяя вам определять типы ввода, которые определяют структуру данных, которые могут быть отправлены в ваши мутации GraphQL. Типы ввода в Strawberry создаются с помощью @strawberry_django.input
декоратора для моделей Django или @strawberry.input
для пользовательских типов ввода. В нашем приложении для каталога книг мы добавим мутацию, которая позволит пользователям добавлять новые книги в каталог.
В нашем schema.py файле давайте определим тип BookInput
, который будет соответствовать модели Book
. Он будет включать поля title
, author
и published_date
:
@strawberry_django.input(Book)
class BookInput:
title: auto
author: auto
published_date: auto
Если вам нужно больше контролировать тип ввода или если он напрямую не привязан к модели Django, вы можете определить тип ввода с помощью @strawberry.input
декоратора:
@strawberry.input
class BookInput:
title: str
author: str
published_date: str
Этот подход особенно полезен, когда ваш тип ввода должен отличаться от модели или когда вы не используете модели Django.
Давайте используем тип ввода в нашей мутации. Мутация примет аргумент этого типа ввода и будет использовать предоставленные данные для выполнения необходимых действий, таких как создание новой книги в базе данных.
Интеграция strawberry с django дает вам возможность напрямую использовать модель для создания без написания дополнительного кода.
В файле schema.py добавьте следующий код:
@strawberry.type
class Mutation:
create_book: BookType = strawberry_django.mutations.create(BookInput)
Затем добавьте мутацию в strawberry.Schema
:
schema = strawberry.Schema(query=Query, mutation=Mutation)
Если вы хотите обновить и удалить все значения в базе данных, вы можете использовать встроенную функцию обновления и удаления мутаций, предоставляемую strawberry-django.
Добавьте ввод для strawberry-django с необязательными полями:
@strawberry_django.input(Book)
class BookUpdateInput:
title: str | None = None
author: str | None = None
published_date: str | None = None
В вашем классе Mutation
добавьте следующий метод:
@strawberry.mutation
def update_book(self, book_id: int, data: BookUpdateInput) -> BookType:
try:
book = Book.objects.get(id=book_id)
for key, value in asdict(data).items():
if value is not None:
setattr(book, key, value)
book.save()
return book
except Book.DoesNotExist:
raise Exception("Not Found")
Не забудьте импортировать:
from dataclasses import asdict
Давайте также добавим мутацию для удаления:
@strawberry.mutation
def delete_book(self, book_id: int) -> bool:
try:
book = Book.objects.get(pk=book_id)
book.delete()
return True
except Book.DoesNotExist:
raise Exception("Not Found")
Почему мы не можем просто вернуть книгу через
BookType
, как мы делали вupdate_book
?
Проверка настройки
Давайте протестируем это на платформе GraphQL Playground. Запустив сервер разработки Django, перейдите к http://127.0.0.1:8000/graphql/.
Выполните следующие запросы и изменения:
mutation CREATE_BOOKS {
createBook(data: {
title: "My New Book",
author: "Oluwole",
publishedDate: "2024-05-31"
}) {
id
title
}
}
query GET_BOOKS {
books {
id
title
author
publishedDate
}
}
mutation UPDATE_BOOK {
updateBook(bookId:1, data: {
title: "My Biography"
}){
id
title
}
}
mutation DELETE_BOOK {
deleteBook(bookId: 1)
}
Обработка ошибок
В наших примерах, приведенных выше, мы обработали недопустимый bookId's
, вызвав исключение. Strawberry предлагает нам лучший способ обработки ошибок, возвращая типы объединения, которые либо представляют ошибку, либо отвечают успешно. Затем ваш клиент может просмотреть __typename
результата, чтобы определить реакцию.
Мы можем добавить сообщение об ошибке Strawberry с полем message
в поле schema.py:
@strawberry.type
class Error:
message: str
Давайте также добавим тип успешной клубники с полем под названием result
:
@strawberry.type
class Success:
result: bool
Затем создайте объединяющий ответ для обновления и удаления мутаций:
Response = Annotated[
Union[BookType, Error],
strawberry.union("BookResponse")
]
DeleteResponse = Annotated[
Union[Success, Error],
strawberry.union("DeleteResponse")
]
Добавьте импортные данные:
from typing import Annotated, List, Union
Теперь обновите класс Mutation
следующим образом:
@strawberry.type
class Mutation:
create_book: List[BookType] = strawberry_django.mutations.create(BookInput)
@strawberry.mutation
def update_book(self, book_id: int, data: BookUpdateInput) -> Response:
try:
book = Book.objects.get(id=book_id)
for key, value in asdict(data).items():
if value is not None:
setattr(book, key, value)
book.save()
return book
except Book.DoesNotExist:
return Error(message="Not Found")
except Exception as e:
return Error(f"An error occurred: {str(e)}")
@strawberry.mutation
def delete_book(self, book_id: int) -> DeleteResponse:
try:
book = Book.objects.get(pk=book_id)
book.delete()
return Success(result=True)
except Book.DoesNotExist:
return Error(message="Not Found")
except Exception as e:
return Error(f"An error occurred: {str(e)}")
Чтобы проверить это, в GraphQL Playground запустите следующие изменения:
mutation UPDATE_BOOK {
updateBook(bookId: 2, data: {
title: "Updated Title Book"
}){
__typename
... on BookType {
id
title
}
... on Error {
message
}
}
}
mutation DELETE_BOOK {
deleteBook(bookId: 2){
__typename
... on Success {
result
}
... on Error {
message
}
}
}
Это упрощает обработку конкретных ошибок в нашем коде.
Тестирование с помощью pytest
Для написания тестов установите pytest и pytest-django:
(.env)$ pip install pytest==8.2.2 pytest-django==4.8.0
Далее, чтобы настроить pytest для вашего проекта Django, создайте файл с именем pytest.ini в корне вашего проекта:
[pytest]
DJANGO_SETTINGS_MODULE = strawberry_django_tut.settings
Создайте папку с именем "тесты" в "strawberry_django_tut" и в этой папке создайте файл с именем test_graphql.py:
import pytest
from django.test import Client
from django.urls import reverse
from book.models import Book
@pytest.fixture
def client():
return Client()
@pytest.fixture
def create_books(db):
Book.objects.create(title="Book 1", author="Author 1", published_date="2023-01-01")
Book.objects.create(title="Book 2", author="Author 2", published_date="2023-01-02")
Здесь мы создали приспособления для добавления книг в нашу тестовую базу данных.
Продолжайте и добавляйте тесты, обязательно просматривая каждый из них:
@pytest.mark.django_db
def test_books_query(client, create_books):
query = """
query {
books {
title
author
publishedDate
}
}
"""
response = client.post(reverse("graphql"), {"query": query}, content_type="application/json")
assert response.status_code == 200
data = response.json()["data"]["books"]
assert len(data) == 2
assert data[0]["title"] == "Book 1"
assert data[1]["title"] == "Book 2"
@pytest.mark.django_db
def test_add_book_mutation(client):
mutation = """
mutation {
createBook(data: {title: "New Book", author: "New Author", publishedDate: "2023-05-01"}) {
title
author
publishedDate
}
}
"""
response = client.post(reverse("graphql"), {"query": mutation}, content_type="application/json")
assert response.status_code == 200
data = response.json()["data"]["createBook"][0]
assert data["title"] == "New Book"
assert data["author"] == "New Author"
assert data["publishedDate"] == "2023-05-01"
assert Book.objects.filter(title="New Book").exists()
@pytest.mark.django_db
def test_update_book_mutation(client, create_books):
book = Book.objects.first()
mutation = f"""
mutation {{
updateBook(bookId: {book.id}, data: {{title: "Updated Book"}}) {{
__typename
... on BookType {{
title
author
publishedDate
}}
... on Error {{
message
}}
}}
}}
"""
response = client.post(reverse("graphql"), {"query": mutation}, content_type="application/json")
assert response.status_code == 200
data = response.json()["data"]["updateBook"]
assert data["title"] == "Updated Book"
assert data["author"] == book.author
assert data["publishedDate"] == str(book.published_date)
assert Book.objects.filter(title="Updated Book").exists()
assert not Book.objects.filter(title="Book 1").exists()
assert Book.objects.filter(title="Book 2").exists()
assert Book.objects.count() == 2
@pytest.mark.django_db
def test_delete_book_mutation(client, create_books):
book = Book.objects.first()
mutation = f"""
mutation {{
deleteBook(bookId: {book.id}) {{
__typename
... on Success {{
result
}}
... on Error {{
message
}}
}}
}}
"""
response = client.post(reverse("graphql"), {"query": mutation}, content_type="application/json")
assert response.status_code == 200
data = response.json()["data"]["deleteBook"]
assert data["result"]
assert not Book.objects.filter(title=book.title).exists()
assert Book.objects.count() == 1
Итак, мы добавили тесты для получения всех книг, добавления книги, а также обновления и удаления книги.
Убедитесь, что они соответствуют:
(.env)$ python -m pytest
Заключение
В этом руководстве мы рассмотрели, как интегрировать Strawberry с Django для создания надежного GraphQL API для приложения каталога книг. Мы рассмотрели следующие ключевые шаги:
- Настройка: Установка необходимых пакетов и настройка проекта Django с помощью Strawberry.
- Определение моделей: Создание модели Django для книг.
- Создание схем: Определение типов и запросов в схеме GraphQL с помощью Strawberry.
- Добавление мутаций: Реализация мутаций для добавления и обновления записей в книге.
- Обработка ошибок: Как лучше обрабатывать ошибки с помощью strawberry union.
- Тестирование: Написание тестов с использованием pytest, чтобы убедиться, что запросы GraphQL и мутации работают должным образом.
Strawberry в сочетании с Django предлагает простой способ создания типобезопасного и эффективного GraphQL API. Гибкость определения схем и мощные возможности Django делают это сочетание привлекательным для современной веб-разработки. Следуя описанным шагам, вы сможете быстро создать и протестировать GraphQL API, что упростит структурированное управление вашими данными и выполнение запросов к ним.
С помощью этой основы вы сможете еще больше расширить функциональность вашего приложения для каталога книг, добавив больше моделей, запросов и изменений. Кроме того, вы можете изучить расширенные возможности Strawberry, такие как подписки, пользовательские скаляры и промежуточное программное обеспечение, чтобы расширить возможности вашего API. Счастливого кодирования!
Вернуться на верх