【问题标题】:All the values of the many to many field : Django多对多字段的所有值:Django
【发布时间】:2012-08-21 19:40:15
【问题描述】:

我有两个模型:

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')

而且,它给出的输出如下(对于有 2 个作者的 1 本书):

[{'title': u'book1', 'authors__name': u'author1'},{'title': u'book1', 'authors__name': u'author2'}]

我想要的是这样的:

[{'title': u'book1', 'authors': [{'name':u'author1'},{'name':u'author2'}]}]

我可能在作者模型中有更多字段,所以我也想获得这些字段。

我可以在单个查询中执行此操作吗?

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

【问题讨论】:

  • 不确定这样的东西是否可用,主要是你需要自己构建这样的字典。

标签: django django-models django-queryset


【解决方案1】:

Django 1.4

好问题,使用prefetch_related

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'}]

Django 1.3

prefetch_related 是在 Django 1.4 中引入的。对于 Django 1.3,你需要django-selectreverse:

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'}]

使用django-selectreverse

class Book(models.Model):
    name = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author, null=True, blank=True)

    objects = ReverseManager()

【讨论】:

  • 执行两个查询,而不是执行objects.all 并使用python 制作您想要的字典,这不是很有效吗?
  • 我不这么认为,但也许我不明白你的意思(对不起)
  • 更新:使用 prefetch_related 代替 django-selectreverse
  • 只是补充一下:prefetch_related 是在Django 1.4 之后引入的。所以你可能想确保你在那个。
  • 感谢您的反馈,添加了 1.4 之前的说明
【解决方案2】:

为避免像@jpic 的描述性答案中那样进行两次查询,您可以在之后手动合并结果。这对我来说有点 hacky,但它确实有效。

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 函数取自this gist

【讨论】:

    【解决方案3】:

    我花了至少 3 个小时搜索终于得到这个正确答案:

    at full M2M object values

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-06-25
      • 2021-11-14
      • 2020-06-08
      • 2019-10-12
      • 1970-01-01
      • 2013-04-27
      • 2021-09-29
      相关资源
      最近更新 更多