【问题标题】:Using Q object in list comprehension在列表理解中使用 Q 对象
【发布时间】:2015-06-05 08:29:23
【问题描述】:

我有以下代码:

def get_elements(self, obj):
  book_elements = Element.objects.filter(book__pk=obj.id)
  elements = Element.objects.filter( (Q(book__pk=obj.id) | Q(theme__pk=obj.theme_id)), ~Q(pk__in = [o.element_id for o in book_elements if o.element_id]))
  serializer = GetElementSerializer(elements, context=self.context, many=True)

elements 变量是使用 Q 对象实现的查询。但是,Q(book__pk=obj.id)book_elements 引用完全相同的一组值。如何在列表理解中引用 Q(book__pk=obj.id) 以避免运行 2 个查询。类似于以下内容:

def get_elements(self, obj):
  elements = Element.objects.filter( (Q(book__pk=obj.id) | Q(theme__pk=obj.theme_id)), ~Q(pk__in = [o.element_id for o in Q(book__pk=obj.id) if o.element_id]))
  serializer = GetElementSerializer(elements, context=self.context, many=True)

按要求我的元素模型:

class Element(models.Model):
  name = models.CharField(max_length=100)
  pub_date = models.DateTimeField('date published', auto_now_add=True)
  mod_date = models.DateTimeField('modified date', auto_now=True)
  book = models.ForeignKey(Book, blank=True, null=True, related_name='elements')
  book_part = models.ForeignKey(BookPart)
  theme = models.ForeignKey(Theme, blank=True, null=True, related_name='elements')
  element = models.ForeignKey('self', blank=True, null=True, related_name='parent')
  def __str__(self):
    return self.name

感谢任何帮助!

【问题讨论】:

  • 如果我正确理解了您的查询(用简单的英语),您正在做的 - 给我所有的元素,包括 book_id 或 theme_id,但不包括与 book 相关的所有元素。我说的对吗?
  • 你几乎是对的。我想要的是 - 获取所有元素,包括 book_id 或 theme_id,等于本书的书籍 ID 和主题 ID,不包括与本书中的另一个元素有 FK 关系的元素。
  • 你能添加你的模型吗?
  • 你可以试一试吗? elements = Element.objects.filter(Q(book_id=obj.id) | Q(theme_id=obj.theme_id)).exclude(book__elements__id=obj.element_id)
  • @schillingt 我用 Element 模型代码更新了帖子

标签: django django-queryset list-comprehension django-q


【解决方案1】:

这是一个带有子查询的单查询解决方案:

elements = Element.objects.filter( 
    (Q(book__pk=obj.id) | Q(theme__pk=obj.theme_id)),
    ~Q(pk__in = Element.objects.filter(
            book__pk=obj.id,
            element__isnull=False
        ).values_list('element', flat=True)
    )
)

这只会访问数据库一次。

顺便说一句,在阅读了您真正想要实现的目标之后:

获取所有元素,包括 book_id 或 theme_id,等于 这本书的书籍 ID 和主题 ID,不包括同样具有 与本书中另一个元素的 FK 关系

在 ORM 方式中,我认为这也可以这样实现:

elements = Element.objects \
    .filter(Q(book_id=book.id)|Q(theme_id=book.theme_id)) \
    .exclude(element__in=book.elements.all()) #not tested, if throws an error transform it into: book.elements.values_list('id', flat=True)

同样,这只会访问数据库一次,因为 querysets 是惰性的,而 Django 足够聪明,可以在它们分解为另一个 queryset 时将它们转换为子查询。

【讨论】:

  • 谢谢,这行得通,但是如果不使用第二个元素过滤器子查询,还有什么办法吗?如果我错了,请纠正我,但这不会两次访问数据库吗?这就是我要避免的,因为第二个查询与Q(book__pk=obj.id) 完全相同
  • 不,这不会两次访问数据库,请查看我更新的答案。
【解决方案2】:

您应该能够在这样的一个查询中完成此操作。它利用了反向关系。

elements = Element.objects.filter( 
    Q(book=obj) | Q(theme__pk=obj.theme_id)
).exclude(
    book=obj, element__isnull=False
)

【讨论】:

    猜你喜欢
    • 2011-01-27
    • 1970-01-01
    • 1970-01-01
    • 2013-05-30
    • 1970-01-01
    • 2017-09-16
    • 1970-01-01
    • 1970-01-01
    • 2015-05-13
    相关资源
    最近更新 更多