2016-06-22 19 views
2

我想根據涉及預取對象的django orm查詢創建一個字典或類字典對象。由於涉及的對象數量衆多,我希望儘可能在數據庫中完成這些操作。Django:在值中包含預取對象()dict

要使用熟悉的例子結構,如果我有一個作者和書籍與圖書通常ForeignKey的,以作家,我也有一個ForeignKey回書一版,我想這樣做

Author.objects.prefetch_related(Prefetch('book_set', queryset=Book.objects.filter(cycle__id=3).select_related('edition__binding'), to_attr="bindings")) # clearly this is wrong 

並最終調用.values()或類似的東西來獲得由作者行組成的類似於字典的結果,其中應包括條目「綁定」,其中包含每個作者已發佈的綁定列表。拉伸目標是將綁定分配爲分號分隔列表,即[{"Author": "Daniel Dennett", "bindings": "paper; hardback; cloth"}, {"Author": "Jemiah Jefferson", "bindings": "paper; zine"}]

到目前爲止,我已經能夠像上面那樣使用prefetch_related和select_related來獲得附加到查詢集的「綁定」字段,但是該字段不包含在調用.values()的結果中。這意味着我必須循環對象,這對我的目的來說太長了(有很多對象,並且請求超時)

回答

1

創建自定義的Concat註釋,它將模仿MySQLGROUP_CONCAT函數。然後,您可以在註釋bindings上使用.values

Django的1.8您的Concat類可以是這樣的:

from django.db import models 
class Concat(models.Aggregate): 
    # supports GROUP_CONCAT(DISTINCT field SEPARATOR str_val) 
    # do not support order_by 
    function = 'GROUP_CONCAT' 
    template = '%(function)s(%(distinct)s%(expressions)s SEPARATOR "%(separator)s")' 
    def __init__(self, expression, distinct=False, separator=None, **extra): 
     super(Concat, self).__init__(
      expression, 
      distinct='DISTINCT ' if distinct else '', 
      separator=separator or '', 
      output_field=models.CharField(), 
      **extra) 

雖然Django 1.7

class Concat(models.Aggregate): 
    def add_to_query(self, query, alias, col, source, is_summary): 
     #we send source=CharField to prevent Django from casting string to int 
     aggregate = SQLConcat(col, source=models.CharField(), is_summary=is_summary, **self.extra) 
     query.aggregates[alias] = aggregate 

#for mysql 
class SQLConcat(models.sql.aggregates.Aggregate): 
    sql_function = 'group_concat' 
    @property 
    def sql_template(self): 
     if self.extra.get('separator'): 
      return '%(function)s(%(field)s SEPARATOR "%(separator)s")' 
     else: 
      return '%(function)s(%(field)s)' 

現在你可以這樣做:

Author.objects.annotate(bindings=Concat('book__edition__binding')).values('name', 'bindings') 

不幸的是這不會篩選books by cycle__id=3,bu您可以在註釋發生之前應用過濾器。

Author.objects.filter(book__cycle__id=3).annotate(bindings=Concat('book__edition__binding')).values('name', 'bindings') 

這將從結果authors剝離而不bookcycle__id=3