如何在 `Q` 表达式中使用 Django 的隐藏 `through` 模型
How to use Django's hidden `through` models in a `Q` expression
TL;DR; 包含 through
table(对于 m2m 相关 table 字段)的正确方法是什么Q
表达式?例如。我如何不引用 Q(compounds__name__iexact="citrate")
中的 m2m 字段“化合物”,而是如何引用当你有 ManyToManyField 时自动创建的 table 隐藏字段?我的猜测是 Q(through_peakgroup_compound__name__iexact="citrate")
,但这会引发异常。
长版...
我昨天了解到,您可以在 Q
表达式中的关键路径^中使用 through
表达式。我了解到,当使用 M:M 相关 table 中的字段进行过滤时,生成的查询集更像是一个真正的 SQL 连接。我的意思在下面的示例中得到了更好的解释,但要点是,如果我通过 m2m 关系在字段上使用搜索词,我只会返回与这些搜索词匹配的 m2m 相关 table 记录,而不是每个 linked 记录 table.
我在 shell 中进行了试验,了解到我可以使用 ModelA.m2mModelB.through.filter()
来完成此操作。
例如,我有 56 条 PeakGroup 记录,通过多对多关系 link 到相同的 2 种化合物(柠檬酸盐和异柠檬酸盐)。
这是我不使用时最接近我想要的东西 through
:
仅查询柠檬酸盐:
In [93]: pgs = PeakGroup.objects.filter(Q(compounds__name__iexact="citrate")).distinct()
In [94]: pgs.count()
Out[94]: 56
In [95]: for i in range(0, 56):
...: for compound in pgs[i].compounds.all():
...: print(", ".join(map(lambda s: str(s), [pgs[i].id, pgs[i].name, compound.id, compound.name])))
...:
4, citrate/isocitrate, 12, citrate
4, citrate/isocitrate, 28, isocitrate
11, citrate/isocitrate, 12, citrate
11, citrate/isocitrate, 28, isocitrate
...
请注意,它给了我不需要的异柠檬酸记录,在 SQL 中,这些记录不会包含在左连接的行中。 (我能够通过使用重新根植^^版本的过滤器提供带有查询集的 Prefetch
来解决这个问题,但是它重新引入了下一个案例的问题,所以我不会去到那个。)
查询柠檬酸盐或异柠檬酸盐:
In [90]: pgs = PeakGroup.objects.filter(Q(compounds__name__iexact="citrate") | Q(compounds__name__iexact="isocitrate")).distinct()
In [91]: pgs.count()
Out[91]: 56
In [92]: for i in range(0, 56):
...: for compound in pgs[i].compounds.all():
...: print(", ".join(map(lambda s: str(s), [pgs[i].id, pgs[i].name, compound.id, compound.name])))
...:
4, citrate/isocitrate, 12, citrate
4, citrate/isocitrate, 28, isocitrate
11, citrate/isocitrate, 12, citrate
11, citrate/isocitrate, 28, isocitrate
...
上面的优点在于,我得到了我查询的所有内容,但如果这些显示在模板中的 112 个单独的行上(每个化合物都在显示重复的 PeakGroup 数据的另一行上),我无法对其进行分页.
这是我做使用时得到的(这就是我想要):
仅查询柠檬酸盐:
In [83]: pgs = PeakGroup.compounds.through.objects.filter(Q(compound__name__iexact="citrate"))
In [84]: pgs.count()
Out[84]: 56
In [85]: for i in range(0, 56):
...: print(", ".join(map(lambda s: str(s), [pgs[i].peakgroup.id, pgs[i].peakgroup.name, pgs[i].compound.id, pgs[i].compound.name])))
...:
4, citrate/isocitrate, 12, citrate
11, citrate/isocitrate, 12, citrate
...
^^完美!
查询柠檬酸盐或异柠檬酸盐:
In [57]: pgs = PeakGroup.compounds.through.objects.filter(Q(compound__name__iexact="citrate") | Q(compound__name__iexact="isocitrate"))
In [58]: pgs.count()
Out[58]: 112
In [59]: for i in range(0, 112):
...: print(", ".join(map(lambda s: str(s), [pgs[i].peakgroup.id, pgs[i].peakgroup.name, pgs[i].compound.id, pgs[i].compound.name])))
...:
4, citrate/isocitrate, 12, citrate
4, citrate/isocitrate, 28, isocitrate
11, citrate/isocitrate, 12, citrate
11, citrate/isocitrate, 28, isocitrate
18, citrate/isocitrate, 12, citrate
18, citrate/isocitrate, 28, isocitrate
...
^^完美!
然而,我在 this stack answer 下的评论中了解到,我应该能够在仅提供给单个过滤器的 Q
表达式中完成相同的事情,而无需引用 .compounds.through
。由于仅在 PeakGroup 的一个过滤器中完成所有这些工作将使我的重构工作大大减少,我想学习如何做我被告知是可能的事情。
我开始使用试错法在 shell 中进行实验(因为我在文档中找不到任何描述此功能的内容)。我想表达式可能看起来像这样:
PeakGroup.objects.filter(Q(through_peakgroup_compound__compound__name__exact="citrate") | Q(through_peakgroup_compound__compound__name__exact="isocitrate")).count()
但我一直无法弄清楚如何构建包含直通模型的“路径”...
每次尝试都会导致如下错误:
FieldError: Cannot resolve keyword 'through_peakgroup_compound' into field.
也许在那个 linked 堆栈答案的评论中,有一个错误的传达,这个技巧实际上不可能通过直接应用于 PeakGroup.objects
的单个过滤器中的 Q 表达式实现?那么代替“through_peakgroup_compound__compound__name__exact”的正确“路径”是什么?
模型关系如下:
class PeakGroup(Model):
compounds = models.ManyToManyField(
Compound,
related_name="peak_groups",
help_text="The compound(s) that this PeakGroup is presumed to represent.",
)
class Compound(Model):
name = models.CharField(
max_length=256,
unique=True,
)
动机
我之所以希望可以使用具有复杂 Q 表达式的单个过滤器,是因为我们有一个相当大且复杂的高级搜索界面,它使用 3 个复合视图,每个视图组合了大约十几个模型,包括一些 M:M关系。用户可以使用 and
组和 or
组以及来自任何模型的术语构建复杂查询。目前,结果合并来自那些 M:M 模型的记录,在单行的单元格中使用分隔符,但下一个版本的新要求是拆分那些 M:M 相关模型之一的输出行,因此使用上面的示例,一行将显示“柠檬酸盐”,另一行将在“化合物”列中显示“异柠檬酸盐”,而不是当前在单行上显示“柠檬酸盐;异柠檬酸盐”。如果他们搜索“柠檬酸盐”,则结果中不会包含包含“异柠檬酸盐”的行。
^ - 通过“键路径”,我的意思是在路径中串在一起的外键,就像您将提供给 .filter()
或 Q
表达式的内容,例如 modelBkey__modelCkey__modelCfieldname
的一部分:ModelA.objects.filter(modelBkey__modelCkey__modelCfieldname__exact="searchterm")
或在模板中包含相关字段,如 {{ queryset.modelBkey.modelCkey.modelCfieldname }}
.
^^ - 通过“过滤器的重新根目录版本”,我的意思是,我采用原始 filter/Q 表达式,并将关键路径更改为从与 m2m 相关的 table 开始。它工作得很好,但它并没有解决我的全部问题。
除非有人能够证明,否则似乎不可能包含 hidden through
table(我称之为SQL "linking table") 在 .filter()
或 Q()
表达式中。只有显式定义 through
table似乎能够以这种方式查询,例如:
class PeakGroup(Model):
compounds = models.ManyToManyField(
Compound,
through="Measurement",
related_name="peak_groups",
help_text="The compound(s) that this PeakGroup is presumed to represent.",
)
class Compound(Model):
name = models.CharField(
max_length=256,
unique=True,
)
class Measurement(Model):
peakgroup = ForeignKey(PeakGroup, on_delete=models.CASCADE)
compound = ForeignKey(Compound, on_delete=models.CASCADE)
然后你可以在过滤器和 Q 表达式中包含 table,例如:
PeakGroup.objects.filter(Q(measurement__compound__name="citrate") | Q(measurement__compound__name="isocitrate"))
但是,在这种情况下,您仍然只向模板发送 PeakGroup
条记录,并且必须在嵌套 for
循环,所以这不允许我做我希望它会做的事情。
请注意,在演示我想要的内容时,我有一个我没有意识到的缺陷。我没有意识到 PeakGroup.compounds.through
情况下发送到模板的记录是链接 table 记录(即 Measurement
记录),而不是 PeakGroup
记录正如我所忽略的。这就是为什么这是一个问题...
如果您在视图中有多个链接 table(就像我一样),您将 运行 在下一个 table 遇到同样的问题,(除非您是发送多个查询集(我们现有的代码库不支持)),因此以这种方式使用 through/linking table 不是可扩展的解决方案。它可以用于一个 M:M 关系,但不能再多了。
我确实找到了一个 work-around,它确实解决了整个问题(想要显示和分页一组经过多重链接的查询结果 tables 就好像它是真的 SQL left-join).
使我的 work-around 工作的基本前提是 在 将查询集发送到模板之前,查询集可以访问 full/true 加入数据。它将每个重复的 PeakGroup 记录与单个 Compound 记录链接起来,这是我在模板中需要的。当您将生成的查询集发送到模板时,您只会失去对这些关联的访问权限,这就是为什么每个人总是简单地在 Django 模板中使用嵌套 for
循环来显示 M:M 相关记录。
注意,Django 会返回重复的 PeakGroup 记录,每个记录都连接到单个 Compound
记录(正如您在左连接中所期望的那样)并且您可以在发送之前访问它查询集到模板,使用 F
表达式。
Work-around
注意,您不需要显式定义 through
table 即可工作。
要利用对完整 left-join 数据的访问并将 M:M 关联嵌入查询集中,以便它们可以在模板中重建,您需要两件事:
- 调用
.distinct(join_key_list)
,提供使连接不同的主键
- 添加对
.annotate(**mm_key_values)
的调用,该调用使用 M:M 相关的 table 主键值创建注释(通过 F
表达式)
使用我的前 2 个示例,它看起来像这样:
仅查询柠檬酸盐:
In [13]: pgs = PeakGroup.objects.filter(Q(compounds__name__iexact="citrate")).distinct('name', 'pk', 'compounds__name', 'compounds__pk').annotate(compound=F("compounds__pk"))
...: print(pgs.count())
...: for pg in pgs:
...: for cp in pg.compounds.all():
...: if pg.compound == cp.pk:
...: print(f"{pg.pk} {pg.name} {cp.pk} {cp.name}")
...:
56
4 citrate/isocitrate 12 citrate
11 citrate/isocitrate 12 citrate
18 citrate/isocitrate 12 citrate
25 citrate/isocitrate 12 citrate
32 citrate/isocitrate 12 citrate
...
查询柠檬酸盐或异柠檬酸盐:
In [12]: pgs = PeakGroup.objects.filter(Q(compounds__name__iexact="citrate") | Q(compounds__name__iexact="isocitrate")).distinct('name', 'pk', 'compounds__name', 'compounds__pk').annotate(compound=F("compounds__pk"))
...: print(pgs.count())
...: for pg in pgs:
...: for cp in pg.compounds.all():
...: if pg.compound == cp.pk:
...: print(f"{pg.pk} {pg.name} {cp.pk} {cp.name}")
...:
112
4 citrate/isocitrate 12 citrate
4 citrate/isocitrate 28 isocitrate
11 citrate/isocitrate 12 citrate
11 citrate/isocitrate 28 isocitrate
18 citrate/isocitrate 12 citrate
18 citrate/isocitrate 28 isocitrate
...
很烦人,你必须循环过滤未使用的 M:M 相关记录,但这很有效,因为它允许我:
- 获取“加入”记录的准确计数。
- 使用server-side分页
- 在模板中显示 SQL left-join 的“真实”表示
请注意,我仍然使用 .prefetch_related()
(此处未显示),我向其提供 Prefetch()
对象,其中包含提供给 queryset
选项的原始过滤器的副本,只有过滤器中提供的路径是“re-rooted”^^。这提供了显着的性能提升。
我还编写了一个名为 get_manytomany_rec
的模板标签 (simple_tag
),它采用 M:M table 记录 (pg.compounds.all
) 和注释字段值(例如 pg.compound
)并检索该行的相应复合记录。我这样做的方式是,如果我将 M:M table 设置为 而不是 拆分结果行,它只是 returns 提供的 pgs.compounds.all
记录。模板标签的用法如下所示:
{% get_manytomany_rec pg.compounds.all pg.compound as compounds %}
{% for mcpd in compounds %}<a href="{% url 'compound_detail' mcpd.id %}">{{ mcpd.name }}</a>; {% endfor %}
(注意,我有一个设置,我可以翻转使 M:M 相关 table 显示为 1 行上的 ;
分隔值或使它将 1 行分成多行。)
我 喜欢 有一个 better/simpler 解决方案而不是这个 work-around,但是在有人提供更好的答案之前,这是我能做的最好的.
TL;DR; 包含 through
table(对于 m2m 相关 table 字段)的正确方法是什么Q
表达式?例如。我如何不引用 Q(compounds__name__iexact="citrate")
中的 m2m 字段“化合物”,而是如何引用当你有 ManyToManyField 时自动创建的 table 隐藏字段?我的猜测是 Q(through_peakgroup_compound__name__iexact="citrate")
,但这会引发异常。
长版...
我昨天了解到,您可以在 Q
表达式中的关键路径^中使用 through
表达式。我了解到,当使用 M:M 相关 table 中的字段进行过滤时,生成的查询集更像是一个真正的 SQL 连接。我的意思在下面的示例中得到了更好的解释,但要点是,如果我通过 m2m 关系在字段上使用搜索词,我只会返回与这些搜索词匹配的 m2m 相关 table 记录,而不是每个 linked 记录 table.
我在 shell 中进行了试验,了解到我可以使用 ModelA.m2mModelB.through.filter()
来完成此操作。
例如,我有 56 条 PeakGroup 记录,通过多对多关系 link 到相同的 2 种化合物(柠檬酸盐和异柠檬酸盐)。
这是我不使用时最接近我想要的东西 through
:
仅查询柠檬酸盐:
In [93]: pgs = PeakGroup.objects.filter(Q(compounds__name__iexact="citrate")).distinct()
In [94]: pgs.count()
Out[94]: 56
In [95]: for i in range(0, 56):
...: for compound in pgs[i].compounds.all():
...: print(", ".join(map(lambda s: str(s), [pgs[i].id, pgs[i].name, compound.id, compound.name])))
...:
4, citrate/isocitrate, 12, citrate
4, citrate/isocitrate, 28, isocitrate
11, citrate/isocitrate, 12, citrate
11, citrate/isocitrate, 28, isocitrate
...
请注意,它给了我不需要的异柠檬酸记录,在 SQL 中,这些记录不会包含在左连接的行中。 (我能够通过使用重新根植^^版本的过滤器提供带有查询集的 Prefetch
来解决这个问题,但是它重新引入了下一个案例的问题,所以我不会去到那个。)
查询柠檬酸盐或异柠檬酸盐:
In [90]: pgs = PeakGroup.objects.filter(Q(compounds__name__iexact="citrate") | Q(compounds__name__iexact="isocitrate")).distinct()
In [91]: pgs.count()
Out[91]: 56
In [92]: for i in range(0, 56):
...: for compound in pgs[i].compounds.all():
...: print(", ".join(map(lambda s: str(s), [pgs[i].id, pgs[i].name, compound.id, compound.name])))
...:
4, citrate/isocitrate, 12, citrate
4, citrate/isocitrate, 28, isocitrate
11, citrate/isocitrate, 12, citrate
11, citrate/isocitrate, 28, isocitrate
...
上面的优点在于,我得到了我查询的所有内容,但如果这些显示在模板中的 112 个单独的行上(每个化合物都在显示重复的 PeakGroup 数据的另一行上),我无法对其进行分页.
这是我做使用时得到的(这就是我想要):
仅查询柠檬酸盐:
In [83]: pgs = PeakGroup.compounds.through.objects.filter(Q(compound__name__iexact="citrate"))
In [84]: pgs.count()
Out[84]: 56
In [85]: for i in range(0, 56):
...: print(", ".join(map(lambda s: str(s), [pgs[i].peakgroup.id, pgs[i].peakgroup.name, pgs[i].compound.id, pgs[i].compound.name])))
...:
4, citrate/isocitrate, 12, citrate
11, citrate/isocitrate, 12, citrate
...
^^完美!
查询柠檬酸盐或异柠檬酸盐:
In [57]: pgs = PeakGroup.compounds.through.objects.filter(Q(compound__name__iexact="citrate") | Q(compound__name__iexact="isocitrate"))
In [58]: pgs.count()
Out[58]: 112
In [59]: for i in range(0, 112):
...: print(", ".join(map(lambda s: str(s), [pgs[i].peakgroup.id, pgs[i].peakgroup.name, pgs[i].compound.id, pgs[i].compound.name])))
...:
4, citrate/isocitrate, 12, citrate
4, citrate/isocitrate, 28, isocitrate
11, citrate/isocitrate, 12, citrate
11, citrate/isocitrate, 28, isocitrate
18, citrate/isocitrate, 12, citrate
18, citrate/isocitrate, 28, isocitrate
...
^^完美!
然而,我在 this stack answer 下的评论中了解到,我应该能够在仅提供给单个过滤器的 Q
表达式中完成相同的事情,而无需引用 .compounds.through
。由于仅在 PeakGroup 的一个过滤器中完成所有这些工作将使我的重构工作大大减少,我想学习如何做我被告知是可能的事情。
我开始使用试错法在 shell 中进行实验(因为我在文档中找不到任何描述此功能的内容)。我想表达式可能看起来像这样:
PeakGroup.objects.filter(Q(through_peakgroup_compound__compound__name__exact="citrate") | Q(through_peakgroup_compound__compound__name__exact="isocitrate")).count()
但我一直无法弄清楚如何构建包含直通模型的“路径”...
每次尝试都会导致如下错误:
FieldError: Cannot resolve keyword 'through_peakgroup_compound' into field.
也许在那个 linked 堆栈答案的评论中,有一个错误的传达,这个技巧实际上不可能通过直接应用于 PeakGroup.objects
的单个过滤器中的 Q 表达式实现?那么代替“through_peakgroup_compound__compound__name__exact”的正确“路径”是什么?
模型关系如下:
class PeakGroup(Model):
compounds = models.ManyToManyField(
Compound,
related_name="peak_groups",
help_text="The compound(s) that this PeakGroup is presumed to represent.",
)
class Compound(Model):
name = models.CharField(
max_length=256,
unique=True,
)
动机
我之所以希望可以使用具有复杂 Q 表达式的单个过滤器,是因为我们有一个相当大且复杂的高级搜索界面,它使用 3 个复合视图,每个视图组合了大约十几个模型,包括一些 M:M关系。用户可以使用 and
组和 or
组以及来自任何模型的术语构建复杂查询。目前,结果合并来自那些 M:M 模型的记录,在单行的单元格中使用分隔符,但下一个版本的新要求是拆分那些 M:M 相关模型之一的输出行,因此使用上面的示例,一行将显示“柠檬酸盐”,另一行将在“化合物”列中显示“异柠檬酸盐”,而不是当前在单行上显示“柠檬酸盐;异柠檬酸盐”。如果他们搜索“柠檬酸盐”,则结果中不会包含包含“异柠檬酸盐”的行。
^ - 通过“键路径”,我的意思是在路径中串在一起的外键,就像您将提供给 .filter()
或 Q
表达式的内容,例如 modelBkey__modelCkey__modelCfieldname
的一部分:ModelA.objects.filter(modelBkey__modelCkey__modelCfieldname__exact="searchterm")
或在模板中包含相关字段,如 {{ queryset.modelBkey.modelCkey.modelCfieldname }}
.
^^ - 通过“过滤器的重新根目录版本”,我的意思是,我采用原始 filter/Q 表达式,并将关键路径更改为从与 m2m 相关的 table 开始。它工作得很好,但它并没有解决我的全部问题。
除非有人能够证明,否则似乎不可能包含 hidden through
table(我称之为SQL "linking table") 在 .filter()
或 Q()
表达式中。只有显式定义 through
table似乎能够以这种方式查询,例如:
class PeakGroup(Model):
compounds = models.ManyToManyField(
Compound,
through="Measurement",
related_name="peak_groups",
help_text="The compound(s) that this PeakGroup is presumed to represent.",
)
class Compound(Model):
name = models.CharField(
max_length=256,
unique=True,
)
class Measurement(Model):
peakgroup = ForeignKey(PeakGroup, on_delete=models.CASCADE)
compound = ForeignKey(Compound, on_delete=models.CASCADE)
然后你可以在过滤器和 Q 表达式中包含 table,例如:
PeakGroup.objects.filter(Q(measurement__compound__name="citrate") | Q(measurement__compound__name="isocitrate"))
但是,在这种情况下,您仍然只向模板发送 PeakGroup
条记录,并且必须在嵌套 for
循环,所以这不允许我做我希望它会做的事情。
请注意,在演示我想要的内容时,我有一个我没有意识到的缺陷。我没有意识到 PeakGroup.compounds.through
情况下发送到模板的记录是链接 table 记录(即 Measurement
记录),而不是 PeakGroup
记录正如我所忽略的。这就是为什么这是一个问题...
如果您在视图中有多个链接 table(就像我一样),您将 运行 在下一个 table 遇到同样的问题,(除非您是发送多个查询集(我们现有的代码库不支持)),因此以这种方式使用 through/linking table 不是可扩展的解决方案。它可以用于一个 M:M 关系,但不能再多了。
我确实找到了一个 work-around,它确实解决了整个问题(想要显示和分页一组经过多重链接的查询结果 tables 就好像它是真的 SQL left-join).
使我的 work-around 工作的基本前提是 在 将查询集发送到模板之前,查询集可以访问 full/true 加入数据。它将每个重复的 PeakGroup 记录与单个 Compound 记录链接起来,这是我在模板中需要的。当您将生成的查询集发送到模板时,您只会失去对这些关联的访问权限,这就是为什么每个人总是简单地在 Django 模板中使用嵌套 for
循环来显示 M:M 相关记录。
注意,Django 会返回重复的 PeakGroup 记录,每个记录都连接到单个 Compound
记录(正如您在左连接中所期望的那样)并且您可以在发送之前访问它查询集到模板,使用 F
表达式。
Work-around
注意,您不需要显式定义 through
table 即可工作。
要利用对完整 left-join 数据的访问并将 M:M 关联嵌入查询集中,以便它们可以在模板中重建,您需要两件事:
- 调用
.distinct(join_key_list)
,提供使连接不同的主键 - 添加对
.annotate(**mm_key_values)
的调用,该调用使用 M:M 相关的 table 主键值创建注释(通过F
表达式)
使用我的前 2 个示例,它看起来像这样:
仅查询柠檬酸盐:
In [13]: pgs = PeakGroup.objects.filter(Q(compounds__name__iexact="citrate")).distinct('name', 'pk', 'compounds__name', 'compounds__pk').annotate(compound=F("compounds__pk"))
...: print(pgs.count())
...: for pg in pgs:
...: for cp in pg.compounds.all():
...: if pg.compound == cp.pk:
...: print(f"{pg.pk} {pg.name} {cp.pk} {cp.name}")
...:
56
4 citrate/isocitrate 12 citrate
11 citrate/isocitrate 12 citrate
18 citrate/isocitrate 12 citrate
25 citrate/isocitrate 12 citrate
32 citrate/isocitrate 12 citrate
...
查询柠檬酸盐或异柠檬酸盐:
In [12]: pgs = PeakGroup.objects.filter(Q(compounds__name__iexact="citrate") | Q(compounds__name__iexact="isocitrate")).distinct('name', 'pk', 'compounds__name', 'compounds__pk').annotate(compound=F("compounds__pk"))
...: print(pgs.count())
...: for pg in pgs:
...: for cp in pg.compounds.all():
...: if pg.compound == cp.pk:
...: print(f"{pg.pk} {pg.name} {cp.pk} {cp.name}")
...:
112
4 citrate/isocitrate 12 citrate
4 citrate/isocitrate 28 isocitrate
11 citrate/isocitrate 12 citrate
11 citrate/isocitrate 28 isocitrate
18 citrate/isocitrate 12 citrate
18 citrate/isocitrate 28 isocitrate
...
很烦人,你必须循环过滤未使用的 M:M 相关记录,但这很有效,因为它允许我:
- 获取“加入”记录的准确计数。
- 使用server-side分页
- 在模板中显示 SQL left-join 的“真实”表示
请注意,我仍然使用 .prefetch_related()
(此处未显示),我向其提供 Prefetch()
对象,其中包含提供给 queryset
选项的原始过滤器的副本,只有过滤器中提供的路径是“re-rooted”^^。这提供了显着的性能提升。
我还编写了一个名为 get_manytomany_rec
的模板标签 (simple_tag
),它采用 M:M table 记录 (pg.compounds.all
) 和注释字段值(例如 pg.compound
)并检索该行的相应复合记录。我这样做的方式是,如果我将 M:M table 设置为 而不是 拆分结果行,它只是 returns 提供的 pgs.compounds.all
记录。模板标签的用法如下所示:
{% get_manytomany_rec pg.compounds.all pg.compound as compounds %}
{% for mcpd in compounds %}<a href="{% url 'compound_detail' mcpd.id %}">{{ mcpd.name }}</a>; {% endfor %}
(注意,我有一个设置,我可以翻转使 M:M 相关 table 显示为 1 行上的 ;
分隔值或使它将 1 行分成多行。)
我 喜欢 有一个 better/simpler 解决方案而不是这个 work-around,但是在有人提供更好的答案之前,这是我能做的最好的.