JavaRush /Курсы /Модуль 3: Django /Вложенные запросы и выборки данных в GraphQL

Вложенные запросы и выборки данных в GraphQL

Модуль 3: Django
25 уровень , 1 лекция
Открыта

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

Что такое вложенные запросы?

Давайте начнем с базового понятия: вложенные запросы в GraphQL представляют собой запросы, которые позволяют извлекать данные из связанных объектов (например, связи между таблицами в базе данных). В отличие от REST API, где для сложного извлечения информации могут понадобиться несколько запросов, GraphQL позволяет делать это в едином запросе, благодаря своей декларативной природе.

Давайте рассмотрим пример из реальной работы. Допустим, у нас есть два объекта в базе данных: Пользователь (User) и Статьи (Articles). У каждого пользователя может быть несколько статей (связь One-to-Many). Если мы хотим получить список пользователей вместе с их статьями, мы можем воспользоваться вложенным запросом.

Создание вложенных запросов

Для реализации вложенных запросов нужно сначала правильно описать схему GraphQL. Давайте создадим объекты User и Article и свяжем их.

# apps/users/models.py
from django.db import models

class User(models.Model):
    name = models.CharField(max_length=128)
    email = models.EmailField(unique=True)

class Article(models.Model):
    title = models.CharField(max_length=128)
    content = models.TextField()
    author = models.ForeignKey(User, related_name="articles", on_delete=models.CASCADE)

Теперь создадим GraphQL-схему для этих объектов.

# apps/api/schema.py
import graphene
from graphene_django.types import DjangoObjectType
from apps.users.models import User, Article

# Определение DjangoObjectType для модели User
class UserType(DjangoObjectType):
    class Meta:
        model = User

# Определение DjangoObjectType для модели Article
class ArticleType(DjangoObjectType):
    class Meta:
        model = Article

# Query для извлечения данных
class Query(graphene.ObjectType):
    users = graphene.List(UserType)  # Запрос для списка пользователей
    articles = graphene.List(ArticleType)  # Запрос для списка статей

    def resolve_users(self, info, **kwargs):
        return User.objects.all()

    def resolve_articles(self, info, **kwargs):
        return Article.objects.all()

schema = graphene.Schema(query=Query)

Выполнение запроса

Теперь мы можем выполнить запрос на получение пользователей вместе с их статьями.

query {
  users {
    name
    email
    articles {
      title
      content
    }
  }
}

Ответ сервера

При выполнении этого запроса сервер вернет JSON-ответ:

{
  "data": {
    "users": [
      {
        "name": "Иван Иванов",
        "email": "ivan@example.com",
        "articles": [
          {
            "title": "Как стать программистом",
            "content": "Lorem ipsum dolor sit amet..."
          },
          {
            "title": "Советы новичкам",
            "content": "Всегда учитесь, используйте документацию..."
          }
        ]
      },
      {
        "name": "Алиса Смирнова",
        "email": "alisa@example.com",
        "articles": []
      }
    ]
  }
}

Заметьте, что мы не только получили пользователей, но и каждую статью, связанную с пользователем. Это мощь GraphQL: мы определяем структуру ответа в запросе.

Управление выборками данных (Selections)

Часто возникает вопрос: должны ли мы всегда получать все данные? Ответ — определённо нет. GraphQL дает возможность запрашивать только те данные, которые действительно нужны. Это называется управление выборками (или Selections). В REST API вы обычно фиксируете формат ответа на стороне сервера. С GraphQL клиент сам решает, что ему нужно.

Давайте рассмотрим пример. Допустим, нам нужно только имя пользователя и заголовок его статьи. Мы легко можем настроить запрос:

query {
  users {
    name
    articles {
      title
    }
  }
}

Сервер вернёт всего несколько полей:

{
  "data": {
    "users": [
      {
        "name": "Иван Иванов",
        "articles": [
          {
            "title": "Как стать программистом"
          },
          {
            "title": "Советы новичкам"
          }
        ]
      },
      {
        "name": "Алиса Смирнова",
        "articles": []
      }
    ]
  }
}

Клиент запрашивает только то, что ему нужно, что помогает минимизировать объём передаваемых данных.

Использование полей и фрагментов

Если вы повторяете одни и те же выборки в нескольких местах вашего GraphQL-запроса, есть способ избежать дублирования кода. Для этого используются фрагменты (Fragments).

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

fragment ArticleFields on ArticleType {
  title
  content
}

query {
  users {
    name
    email
    articles {
      ...ArticleFields
    }
  }
}

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

Проблемы и подходы к их решению

  1. Проблема N+1 запросов

Если вы не оптимизируете ваш GraphQL-запрос, то при извлечении связанных данных (например, статей для каждого пользователя) сервер может выполнить множество отдельных SQL-запросов. Это называется N+1 проблема: например, 1 запрос для получения пользователей и N запросов для получения статей для каждого пользователя.

Решение: использование select_related и prefetch_related

Для оптимизации запросов в Django мы можем использовать методы select_related и prefetch_related.

# apps/api/schema.py
class Query(graphene.ObjectType):
    users = graphene.List(UserType)

    def resolve_users(self, info, **kwargs):
        return User.objects.prefetch_related('articles').all()

Теперь все статьи будут извлечены одним дополнительным SQL-запросом.

  1. Избыточная выборка данных

Если клиент запрашивает слишком много данных (например, все статьи у всех пользователей), это может повлиять на производительность.

Решение: ограничение данных на уровне сервера

Вы можете ограничить количество возвращаемых данных, добавив дополнительные фильтры:

class Query(graphene.ObjectType):
    users = graphene.List(UserType, limit=graphene.Int())

    def resolve_users(self, info, limit=None, **kwargs):
        query = User.objects.prefetch_related('articles')
        if limit:
            query = query[:limit]
        return query

Клиент теперь может указать limit для сокращения данных:

query {
  users(limit: 2) {
    name
    articles {
      title
    }
  }
}

Практическое задание

Для практики попробуйте следующее:

  1. Добавьте третью модель (например, Комментарии к статьям), создайте связь Article -> Comment и настройте вложенные запросы для извлечения комментариев.
  2. Реализуйте управление выборками и добавьте возможность ограничить глубину вложения (например, только 2 уровня).

На этом этапе мы разобрались с основами вложенных запросов и выборок в GraphQL. В следующей лекции мы перейдём к оптимизации этих запросов, чтобы избежать проблем с производительностью. А пока — тестируйте, экспериментируйте и наслаждайтесь гибкостью GraphQL!

1
Задача
Модуль 3: Django, 25 уровень, 1 лекция
Недоступна
Множественные вложенные запросы
Множественные вложенные запросы
1
Задача
Модуль 3: Django, 25 уровень, 1 лекция
Недоступна
Запрос для электронной коммерции
Запрос для электронной коммерции
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ