在列表理解中使用Q对象

时间:2015-03-31 20:39:59

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

我有以下代码:

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

感谢任何帮助!

2 个答案:

答案 0 :(得分:0)

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

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时将它们转换为子查询。< / p>

答案 1 :(得分:0)

您应该可以在这样的一个查询中执行此操作。它利用了反向关系。

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