@ErikHabr

Как оптимизировать вложенные запросы в Django?

как правильно сделать оптимизацию запросов в цикле for?

checked_additives_sum = 0
        if additives:
            for additive in additives:
                for cat in product.additives.filter(institution=institution,
                                                    is_active=True):
                    for i in cat.category_additive.filter(
                            institution=institution, is_active=True).only(
                            "title", "price"):
                        if additive["title"] == i.title and additive["price"] != int(i.price):
                            additive["price"] = int(i.price)
                checked_additives_sum += additive["price"]

additives - это массив с данными от фронтенда так что думаю тут оптимизировать нечего в for additive in additives, но вот остальные циклы ниже, которые обращаются к БД хотелось бы, если это возможно. Ниже примеры моделей добавлю на всякий случай.
class Product(models.Model):
    """
    Product of institution
    """
    user = models.ForeignKey(User, on_delete=models.CASCADE,
                             null=True, blank=True)
    institution = models.ManyToManyField("company.Institution",
                                         related_name="products",
                                         blank=True)
    category = models.ForeignKey("product.Category", on_delete=models.PROTECT,
                                 related_name="products")
    sticker = models.ManyToManyField("product.Sticker", blank=True)
    is_active = models.BooleanField(default=False)
    row = models.PositiveIntegerField(default=1)
    title = models.CharField(max_length=255)
    description = models.TextField()
    images = models.ManyToManyField("media.Image", blank=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    old_price = models.DecimalField(max_digits=10, decimal_places=2,
                                    blank=True, null=True)
    cost_price = models.DecimalField(max_digits=10, decimal_places=2,
                                     blank=True, null=True)
    protein = models.FloatField(blank=True, null=True)
    fats = models.FloatField(blank=True, null=True)
    carbohydrates = models.FloatField(blank=True, null=True)
    calories = models.FloatField(blank=True, null=True)
    additives = models.ManyToManyField("product.CategoryAdditive",
                                       blank=True,
                                       related_name="product_additives")
    modifiers = models.ManyToManyField("product.Modifier",
                                       blank=True,
                                       related_name="product_modifiers")
    weight_unit = models.CharField(max_length=50, choices=WeightUnit.choices,
                                   default=WeightUnit.GRAM)
    weight = models.FloatField(max_length=50)
    cook_time = models.PositiveIntegerField()
    slug = models.SlugField(unique=True)

    def __str__(self):
        return self.title



class CategoryAdditive(models.Model):
    """
    Category of additive
    """
    user = models.ForeignKey(User, on_delete=models.CASCADE,
                             null=True, blank=True)
    institution = models.ManyToManyField("company.Institution",
                                         related_name="category",
                                         blank=True)
    title = models.CharField(max_length=255)
    is_active = models.BooleanField(default=False)
    row = models.PositiveIntegerField(default=1)

    def __str__(self):
        return self.title


class Additive(models.Model):
    """
    Additive of the product
    """
    user = models.ForeignKey(User, on_delete=models.CASCADE,
                             null=True, blank=True)
    institution = models.ManyToManyField("company.Institution",
                                         related_name="additives",
                                         blank=True)
    category = models.ForeignKey(CategoryAdditive, on_delete=models.SET_NULL,
                                 null=True, related_name="category_additive")
    title = models.CharField(max_length=255)
    description = models.TextField()
    image = models.ImageField(upload_to=get_path_additive,
                              blank=True, null=True,
                              validators=[FileExtensionValidator(
                                  allowed_extensions=['jpg', 'jpeg', 'png'])])
    price = models.DecimalField(max_digits=10, decimal_places=2)
    is_active = models.BooleanField(default=True)
    is_required = models.BooleanField(default=False)  # necessary?

    def __str__(self):
        return self.title


P.S: я прочитал про ORM и оптимизацию, потренировался через debug-toolbar на запросах во вьюшках, но вот что делать с таким видом запросов я не разобрался. Так же это логика в POST запросе, а django-debug-toolbar не показывает ничего при post запросах.
  • Вопрос задан
  • 93 просмотра
Решения вопроса 1
@ErikHabr Автор вопроса
Изучив ORM я смог сократить кол-во запросов в БД c 21 до 4.

for additive in additives:
                for i in product.additives\
                        .values("category_additive__title",
                                "category_additive__price")\
                        .filter(is_active=True, institution=institution):
                    if additive["title"] == i["category_additive__title"] and \
                       additive["price"] != int(i["category_additive__price"]):
                        additive["price"] = int(i["category_additive__price"])
                    checked_additives_sum += additive["price"]


Не знаю есть ли способ еще эффективнее, но время тоже не вечное поэтому такой результат устраивает на данный момент. Единственное хотелось бы все таки еще убрать цикл for additive in additives и сравнивать как-то два списка в которых словари друг с другом в одном запросе, может я слишком придирчив.

Если кто-то может написать еще более эффективный вариант, пишите )
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@bacon
У тебе в for запросы которые не зависят от переменной для for, получается их можно вынести из for и выполнить один раз. А так, всё это можно заменить вообще на один запрос, в котором сумма и считается. Читай доки django
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы