2016-06-22 3 views
2

Я хотел бы создать объект dict или dict-like, основанный на запросе orj 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 функции. Затем вы можете использовать .values в аннотированном bindings.

Для 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 от cycle__id=3, bu t вы можете применить фильтр до появления аннотации.

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

Это будет полоса из результата authors без book с cycle__id=3.

Смежные вопросы