Django模型 – 如何过滤ForeignKey对象的数量

我有一个模型AB ,就是这样的:

 class A(models.Model): title = models.CharField(max_length=20) (...) class B(models.Model): date = models.DateTimeField(auto_now_add=True) (...) a = models.ForeignKey(A) 

现在我有一些AB对象,我想要得到一个查询,select所有A对象有less于2 B指向他们。

A就像一个池东西,用户(B)join池。 如果只有1或0join,则不应该显示该池。

这样的模型devise有可能吗? 还是应该修改一下?

听起来像是一个extra的工作。

 A.objects.extra( select={ 'b_count': 'SELECT COUNT(*) FROM yourapp_b WHERE yourapp_b.a_id = yourapp_a.id', }, where=['b_count < 2'] ) 

如果B计数是您经常需要的过滤或sorting标准,或者需要在列表视图中显示,则可以考虑通过向A模型添加b_count字段并使用信号在添加B时对其进行更新来进行反规范化;或者删除:

 from django.db import connection, transaction from django.db.models.signals import post_delete, post_save def update_b_count(instance, **kwargs): """ Updates the B count for the A related to the given B. """ if not kwargs.get('created', True) or kwargs.get('raw', False): return cursor = connection.cursor() cursor.execute( 'UPDATE yourapp_a SET b_count = (' 'SELECT COUNT(*) FROM yourapp_b ' 'WHERE yourapp_b.a_id = yourapp_a.id' ') ' 'WHERE id = %s', [instance.a_id]) transaction.commit_unless_managed() post_save.connect(update_b_count, sender=B) post_delete.connect(update_b_count, sender=B) 

另一种解决scheme是在添加或删除相关的B时pipe理A对象上的状态标志。

 B.objects.create(a=some_a) if some_a.hidden and some_a.b_set.count() > 1: A.objects.filter(id=some_a.id).update(hidden=False) ... some_a = ba some_b.delete() if not some_a.hidden and some_a.b_set.count() < 2: A.objects.filter(id=some_a.id).update(hidden=True) 

问题和select的答案是从2008年起,从那时起,这个function已经被集成到Django框架中。 由于这是“Djangofilter外键计数”的顶部谷歌命中我想添加一个简单的解决scheme,使用最近的Django版本使用聚合 。

 from django.db.models import Count cats = A.objects.annotate(num_b=Count('b')).filter(num_b__lt=2) 

就我而言,我不得不把这个概念更进一步。 我的“B”对象有一个名为is_available的布尔型字段,我只想返回一个拥有多于0个B对象且is_available设置为True的对象。

 A.objects.filter(B__is_available=True).annotate(num_b=Count('b')).filter(num_b__gt=0).order_by('-num_items') 

我build议修改你的devise,在A上包含一些状态栏

这个问题是“为什么?”的问题之一。 为什么A有<2 B,为什么A有> = 2 B。 是因为用户没有input? 或者是因为他们尝试了,他们的input有错误。 还是因为<2规则不适用于这种情况。

使用存在或不存在外键限制的意思是 – 目前或没有。 你没有办法来表示“为什么?”

另外,您有以下选项

 [ a for a in A.objects.all() if a.b_set.count() < 2 ] 

这可能是昂贵的,因为它取得所有的A而不是强制数据库来完成这项工作。


编辑:从评论“将要求我看用户join/用户离开池事件”。

你不“看”任何东西 – 你提供了一个你所需要的API。 这是Django模型的核心优势。 这里有一个方法,用A类中的explict方法。

 class A( models.Model ): .... def addB( self, b ): self.b_set.add( b ) self.changeFlags() def removeB( self, b ): self.b_set.remove( b ) self.changeFlags() def changeFlags( self ): if self.b_set.count() < 2: self.show= NotYet else: self.show= ShowNow 

您也可以为此定义一个特殊的Manager ,并将您的pipe理器replace为默认的b_setpipe理器,用于统计引用和更新。

我认为join或离开游泳池可能不会像列出(显示)泳池那样频繁发生。 我也相信用户join/离开操作来更新池显示状态会更有效率。 这样,列出并显示池将需要更less的时间,因为您只需运行池对象的SHOW_STATUS的单个查询。

那么这样做呢?

 queryset = A.objects.filter(b__gt=1).distinct()