2015-03-31 50 views
0

运用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) 

元素变量是使用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 

任何帮助表示赞赏!

+0

如果我理解你的查询正确无误(用简单的英语),你正在做 - 将所有元素都带到book_id或theme_id中,但不包括与书相关的所有元素。我对么? – karthikr 2015-03-31 20:45:30

+0

你几乎没错。我想要的是 - 将所有元素与book_id或theme_id相提并论,这些元素与本书的书籍ID和主题ID相等,不包括与本书中另一个元素具有FK关系的元素。 – Zach 2015-03-31 21:22:31

+0

你可以添加你的模型吗? – schillingt 2015-03-31 21:25:22

回答

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为这本书,但不包括元素也有在这本书

在一个ORM的方式,我认为这也可以实现这样的 FK关系到另一个元素:

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的子查询。

+0

谢谢,这是有效的,但无论如何要做,而不使用第二个元素过滤器子查询?纠正我,如果我错了,但不会这两次打到数据库?这是我试图避免,因为第二个查询是完全一样的'Q(book__pk = obj.id)' – Zach 2015-04-01 18:54:07

+0

没有这将不会击中数据库两次,检查我更新的答案。 – Todor 2015-04-01 21:31:41

0

你应该可以在一个这样的查询中做到这一点。它利用了相反的关系。

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