ListAPIView с набором запросов User.objects.all() делает 7 запросов только с двумя пользователями в тестовой базе данных?

Я создавал конечную точку с использованием drf для вывода списка пользователей. Во время тестирования кода я понял, что он вызывает 7 запросов.

models.py: (я думаю, что использование модели User от django позволит достичь того же результата)

class CustomUserManager(BaseUserManager):
    """
    Custom user model manager where email is the unique identifiers
    for authentication instead of usernames.
    """

    def create_user(self, email, password, **extra_fields):
        """
        Create and save a User with the given email and password.
        """
        if not email:
            raise ValueError(_("The Email must be set"))
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save()
        return user

    def create_superuser(self, email, password, **extra_fields):
        """
        Create and save a SuperUser with the given email and password.
        """
        extra_fields.setdefault("is_staff", True)
        extra_fields.setdefault("is_superuser", True)
        extra_fields.setdefault("is_active", True)

        if extra_fields.get("is_staff") is not True:
            raise ValueError(_("Superuser must have is_staff=True."))
        if extra_fields.get("is_superuser") is not True:
            raise ValueError(_("Superuser must have is_superuser=True."))
        return self.create_user(email, password, **extra_fields)
class BaseUser(AbstractUser):
    email = models.EmailField(_("email address"), unique=True)
    id_number = models.CharField(max_length=MID_LENGTH)
    username = None

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = [email]

    objects = CustomUserManager()

    def __str__(self) -> str:
        return self.email

serializers.py:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.BaseUser
        fields = "__all__"

api.py:

class ListUserView(ListAPIView):
    permission_classes = [IsAdminUser]
    queryset = models.BaseUser.objects.all().order_by("id")
    serializer_class = serializers.UserSerializer

test.py:

from . import models
from django.db import connection
from django.test.utils import CaptureQueriesContext
from django.urls import reverse
from django_seed import Seed
from rest_framework.test import APIClient, APITestCase


seeder = Seed.seeder()

CREDENTIALS = ["test@email.com", "12345678"]


class UserViewsTest(APITestCase):
    def setUp(self) -> None:
        #   Create user and login
        self.user = models.BaseUser.objects.create(email=CREDENTIALS[0], is_staff=True)
        self.user.set_password(CREDENTIALS[1])
        self.user.save()

        self.assertTrue(
            self.client.login(email=CREDENTIALS[0], password=CREDENTIALS[1])
        )

        #   Seed users
        seeder.add_entity(models.BaseUser, 1)
        seeder.execute()

    def tearDown(self) -> None:
        self.client.logout()

    def test_list_users(self):
        """
        7 queries:
            1. Logged in user session
            2. 2 of each of:
                i.   1 query for base user
                ii.  1 query for base user's group
                iii. 1 query for base user's permission
        """
        with CaptureQueriesContext(connection) as queries:
            response = self.client.get(reverse("accounts-list-users"))

            self.assertEqual(200, response.status_code)
            self.assertIsNotNone(response.content)

        print(queries.captured_queries)

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

Ниже приведен результат print(queries.captured_queries):

7 запросов:

  1. Получение сессии. Чтобы избежать этого запроса, вы можете использовать, например, бэкенд сессии cached_db..
  2. Получить данные о вошедшем в систему пользователе из базы данных. Этого также можно избежать с помощью другого бэкенда сессии, который получает эти данные из кэша (или, может быть, из самого объекта сессии) (но вам придется быть осторожным, например, с аннулированием кэша).
  3. .
  4. Перечислите пользователей. Поскольку вы перечисляете пользователей, этого и следовало ожидать.
  5. Получите назначенные группы пользователя 1.
  6. Получите разрешения, назначенные пользователю 1.
  7. Получение назначенных групп пользователя 2.
  8. Получение назначенных разрешений пользователя 2.

Запросы от 4 до 7 можно обойти либо путем:

  • Вытаскивание данных из сериализатора. (В настоящее время вы используете fields = "__all__".) Если сериализатор не сериализует разрешения и группы, они также не будут извлечены.
  • .
  • Добавление подходящего предложения .prefetch_related() (поскольку это M2Ms) в кверисет viewset, чтобы они выполнялись за 2 запроса, а не за 2N запросов.
Вернуться на верх