Проверка, является ли поле M2M пустым или нет в Django - PullRequest
1 голос
/ 31 октября 2019

У меня есть следующие модели:

class Pizza(models.Model):
    toppings = models.ManyToManyField(Topping)
    has_toppings = models.BooleanField(default=False)

    def check_if_there_are_toppings(self):
         if len(self.toppings.all()) > 0:
             self.has_toppings = True

@receiver(m2m_changed, sender=Pizza.toppings.through)
def toppings_changed(sender, instance, **kwargs):
    instance.check_if_there_are_toppings()
    instance.save()

Я хочу обновить поле has_toppings, если длина toppings больше 0. Какой правильный способ сделать это? Спасибо за любую помощь.

Ответы [ 2 ]

1 голос
/ 31 октября 2019

Я думаю, что, вероятно, не хорошая идея сделать это. Может быть много причин, по которым меняется отношение «многие ко многим». Будет очень трудно или даже невозможно охватить все это. Например, сам объект Topping может быть удален, и, следовательно, может вызвать изменение во всех отношениях «многие ко многим», в которых использовалась начинка. Кроме того, в Django ORM есть некоторые функции, такие как .bulk_create(..) [Django-doc] и .update(..) [Django-doc] , которыеобойти сигнальный механизм Django и, таким образом, может привести базу данных в несогласованное состояние. Поэтому может иметь смысл просто удалить поле has_toppings:

class Pizza(models.Model):
    toppings = models.ManyToManyField(Topping)
    # <i>no</i> has_toppings

Возможно, имеет смысл просто аннотировать ваши Pizza наборы запросов. Например, с помощью:

from django.db.models import Exists, OuterRef

Pizza.objects.annotate(
    has_toppings=Exists(
        <b>Pizza.toppings.through.objects.filter(pizza_id=OuterRef('pk'))</b>
    )
)

Это сгенерирует запрос, который выглядит следующим образом:

SELECT pizza.id,
       <b>EXISTS(</b>
           SELECT U0.id, U0.pizza_id, U0.topping_id
           FROM pizza_toppings U0
           WHERE U0.pizza_id = pizza.id
       <b>) AS has_toppings</b>
FROM pizza

Этот набор запросов можно использовать при доступе к Pizza.objects, установив менеджер:

from django.db.models import Exists, OuterRef

class <b>PizzaManager</b>(models.Manager):
    def get_queryset(self):
        return Pizza.objects.annotate(
            has_toppings=Exists(
                Pizza.toppings.through.objects.filter(pizza_id=OuterRef('pk'))
            )
        )

class Topping(models.Model):
    pass

class Pizza(models.Model):
    toppings = models.ManyToManyField(Topping)
    <b>objects = PizzaManager()</b>

Так что теперь мы можем, например, получить все Pizza s с начинками с:

Pizza.objects.filter(<b>has_toppings=True</b>)
1 голос
/ 31 октября 2019

Вы можете просто проверить количество начинки, позвонив:

is_empty = instance.toppings.all().count() == 0

is_empty будет иметь False, если имеется более 0 добавок, и True, если нетначинки.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...