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

ken*_*der 43 python django database-design

我有一个模型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)
Run Code Online (Sandbox Code Playgroud)

现在我有一些AB对象,我想得到一个查询,选择所有A少于2的对象B指向它们.

A类似于池事物,用户(B)连接池.如果只有1或0加入,则不应显示池.

这种模型设计有可能吗?或者我应该修改一下?

gra*_*ron 123

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

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

就我而言,我不得不更进一步.我的"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')
Run Code Online (Sandbox Code Playgroud)

  • 添加额外的原始SQL"打破"使用ORM的想法 (8认同)
  • OP应该不接受原始并接受这一个 (2认同)
  • @llazzaro - 不,不.Django文档从未说过"你不应该使用SQL,否则你就会破坏抽象".如果他们有他们就不会让你这样做.当然你总是避免它,但有时候你必须这样做. (2认同)
  • 这只是我抱怨和质疑ORM DB抽象东西的用处.就个人而言,更容易生成一个SQL查询,如`SELECT*FROM yourapp_a WHERE id IN(SELECT a_id FROM yourapp_b GROUP BY a_id HAVING COUNT(*)<2)`而不是破译每个框架的inane语法应该是什么简单的DB事务.我认为这更像是对学习/了解SQL的厌恶,这很不幸,因为它在许多数据库平台上都很好地符合.只是我的两分钱. (2认同)

Jon*_*nan 7

听起来像是一份工作extra.

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

如果B计数是您经常需要的过滤或排序标准,或者需要在列表视图上显示,则可以通过在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)
Run Code Online (Sandbox Code Playgroud)

另一种解决方案是在添加或删除相关B时管理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 = 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)
Run Code Online (Sandbox Code Playgroud)