2015-03-31 68 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 
)