首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Django模型-如何过滤ForeignKey对象的数量

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

Stack Overflow用户
提问于 2008-11-03 10:38:55
回答 4查看 21.5K关注 0票数 50

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

代码语言:javascript
运行
复制
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对象,我想要执行一个查询,该查询选择所有指向A对象的B少于2个的对象。

A有点像池,用户( B)加入池。如果只有1个或0个加入,则根本不应显示池。

这样的模型设计可能吗?或者我应该稍微修改一下?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2008-11-03 10:47:39

听起来像是extra的工作。

代码语言:javascript
运行
复制
A.objects.extra(
    select={
        'b_count': 'SELECT COUNT(*) FROM yourapp_b WHERE yourapp_b.a_id = yourapp_a.id',
    },
    where=['b_count < 2']
)

如果B计数是您经常需要的过滤或排序标准,或者需要在列表视图上显示,您可以考虑去规范化,方法是向A模型添加一个b_count字段,并在添加或删除B时使用信号更新它:

代码语言:javascript
运行
复制
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)

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

代码语言:javascript
运行
复制
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 = b.a
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)
票数 7
EN

Stack Overflow用户

发布于 2011-06-02 01:30:34

问题和选择的答案来自2008年,从那时起,这一功能已经集成到django框架中。由于这是谷歌最热门的"django filter外键计数“,我想用最新的使用Aggregation的django版本添加一个更简单的解决方案。

代码语言:javascript
运行
复制
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对象。

代码语言:javascript
运行
复制
A.objects.filter(B__is_available=True).annotate(num_b=Count('b')).filter(num_b__gt=0).order_by('-num_items')
票数 145
EN

Stack Overflow用户

发布于 2008-11-03 11:00:13

我建议修改你的设计,在A上加入一些状态字段。

问题是“为什么?”为什么A有<2个B,为什么A有>= 2 B,是因为用户没有输入吗?或者是因为他们尝试了,但他们的输入有错误。或者是因为<2规则在这种情况下不适用。

使用外键的存在或不存在将含义限制为--很好--存在或不存在。你没有任何方式来表达“为什么?”

此外,您还可以选择以下选项

代码语言:javascript
运行
复制
[ a for a in A.objects.all() if a.b_set.count() < 2 ]

这可能是昂贵的,因为它确实获取了所有的A,而不是强制数据库来做这项工作。

编辑:来自注释“需要我观察用户加入/用户离开池事件”。

您不需要“监视”任何东西--您只需要提供一个API来完成您所需要的工作。这是Django模型的核心优势。这里有一种方法,在A类中使用显式方法。

代码语言:javascript
运行
复制
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,并将默认的b_set管理器替换为计算引用和更新A的管理器。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/258296

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档