在Django中检查M2M字段是否为空

时间:2019-10-31 10:10:12

标签: django

我有以下型号:

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长度大于0时更新toppings字段。这样做的正确方法是什么?感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

您可以通过调用以下命令来简单地检查浇头的数量:

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

is_empty将具有False,如果浇头超过0,则True,如果没有浇头。

希望,它会有所帮助。

答案 1 :(得分:1)

我认为这样做可能不是一个好主意。多对多关系如何变化可能有很多原因。覆盖所有这些将非常困难,甚至不可能。例如,Topping对象本身可以被删除,并因此触发使用浇头的所有多对多关系的更改。此外,Django ORM具有诸如.bulk_create(..) [Django-doc].update(..) [Django-doc]之类的某些功能,可以绕过Django的信号机制,因此可以使数据库处于不一致的状态。因此,仅删除has_toppings字段可能更有意义:

class Pizza(models.Model):
    toppings = models.ManyToManyField(Topping)
    # no has_toppings

仅注释您的Pizza查询集可能更有意义。例如:

from django.db.models import Exists, OuterRef

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

这将生成如下查询:

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

您可以通过设置管理员来访问Pizza.objects时使用此查询集:

from django.db.models import Exists, OuterRef

class PizzaManager(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)
    objects = PizzaManager()

因此,我们现在可以例如检索具有以下内容的所有Pizza

Pizza.objects.filter(has_toppings=True)