多对多字段的所有值:Django

多对多字段的所有值:Django,django,django-models,django-queryset,Django,Django Models,Django Queryset,我有两种型号: class Author(models.Model); name = models.CharField(max_length=255) class Book(models.Model): title = models.CharField(max_length=255) authors = models.ManyToManyField(Author, null=True, blank=True) 现在,我要所有书的信息。所以,我做到了: book_inf

我有两种型号:

class Author(models.Model);
    name = models.CharField(max_length=255)

class Book(models.Model):
    title = models.CharField(max_length=255)
    authors = models.ManyToManyField(Author, null=True, blank=True)
现在,我要所有书的信息。所以,我做到了:

book_info = Book.objects.all().values('title', 'authors__name')
并且,它给出了如下输出(对于一本有两位作者的书):

我想要的是:

[{'title': u'book1', 'authors': [{'name':u'author1'},{'name':u'author2'}]}]
我可能在作者模型中有更多的字段,所以我也想得到这些字段

我可以在一个查询中完成此操作吗

我能做些什么来获得想要的结果?

Django 1.4 好问题,使用:

Django 1.3 Django 1.4中引入了预取_相关。对于Django 1.3,您需要:

使用:


为了避免像@jpic的描述性回答中那样执行两个查询,您可以在之后手动合并结果。我觉得有点不舒服,但它很管用

def merge_values(values):
    grouped_results = itertools.groupby(values, key=lambda value: value['id'])
    merged_values = []
    for k, g in grouped_results:
        groups = list(g)
        merged_value = {}
        for group in groups:
            for key, val in group.iteritems():
                if not merged_value.get(key):
                    merged_value[key] = val
                elif val != merged_value[key]:
                    if isinstance(merged_value[key], list):
                        if val not in merged_value[key]:
                            merged_value[key].append(val)
                    else:
                        old_val = merged_value[key]
                        merged_value[key] = [old_val, val]
        merged_values.append(merged_value)
    return merged_values

book_info = marge_values(Book.objects.all().values('title', 'authors__name'))

merge_values函数取自

不确定是否有这样的功能可用,大多数情况下您需要自己构建这样的dict。这是执行两个查询,而不是执行
对象。所有
并使用python创建您想要的字典,这会有效率吗?我不这么认为,但可能我不理解您的意思(抱歉)更新:使用预回迁相关而不是django selectreverseJust添加:
django 1.4
之后引入了与预回迁相关的。因此,您可能希望确保自己做到了这一点。感谢您的反馈,为1.4之前的版本添加了说明
In [3]: [{'name': b.name, 'authors': [a.name for a in b.authors.all()]} for b in Book.objects.prefetch_related('authors')]
(0.000) SELECT "test_app_book"."id", "test_app_book"."name" FROM "test_app_book"; args=()
(0.000) SELECT ("test_app_book_authors"."book_id") AS "_prefetch_related_val", "test_app_author"."id", "test_app_author"."name" FROM "test_app_author" INNER JOIN "test_app_book_authors" ON ("test_app_author"."id" = "test_app_book_authors"."author_id") WHERE "test_app_book_authors"."book_id" IN (1, 2); args=(1, 2)
Out[3]: 
[{'authors': [u'a', u'b'], 'name': u'book'},
 {'authors': [u'b'], 'name': u'test'}]
In [19]: [{'name': b.name, 'authors': [a.name for a in b.authors_prefetch]} for b in Book.objects.select_reverse({'authors_prefetch': 'authors'})]
(0.000) SELECT "test_app_book"."id", "test_app_book"."name" FROM "test_app_book"; args=()
(0.001) SELECT (test_app_book_authors.book_id) AS "main_id", "test_app_author"."id", "test_app_author"."name" FROM "test_app_author" INNER JOIN "test_app_book_authors" ON ("test_app_author"."id" = "test_app_book_authors"."author_id") WHERE "test_app_book_authors"."book_id" IN (1, 2); args=(1, 2)
Out[19]: 
[{'authors': [u'a', u'b'], 'name': u'book'},
 {'authors': [u'b'], 'name': u'test'}]
class Book(models.Model):
    name = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author, null=True, blank=True)

    objects = ReverseManager()
def merge_values(values):
    grouped_results = itertools.groupby(values, key=lambda value: value['id'])
    merged_values = []
    for k, g in grouped_results:
        groups = list(g)
        merged_value = {}
        for group in groups:
            for key, val in group.iteritems():
                if not merged_value.get(key):
                    merged_value[key] = val
                elif val != merged_value[key]:
                    if isinstance(merged_value[key], list):
                        if val not in merged_value[key]:
                            merged_value[key].append(val)
                    else:
                        old_val = merged_value[key]
                        merged_value[key] = [old_val, val]
        merged_values.append(merged_value)
    return merged_values

book_info = marge_values(Book.objects.all().values('title', 'authors__name'))