python - 如何在 ModelAdmin.formfield_for_manytomany() 中使用 Django QuerySet.union()?
问题描述
不知道我在这里做错了什么:
我尝试在 Django 2.2.10 中使用QuerySet.union()在ModelAdmin.formfield_for_manytomany()
. 但是,保存表单时,会选择整个查询集,而不管实际所做的选择如何。
请考虑以下基于标准 Django文章/出版物示例的最小示例。
from django.db import models
from django.contrib import admin
class Publication(models.Model):
pass
class Article(models.Model):
publications = models.ManyToManyField(to=Publication, blank=True)
class ArticleAdmin(admin.ModelAdmin):
def formfield_for_manytomany(self, db_field, request, **kwargs):
if db_field.name == 'publications':
# the following query makes no sense, but it shows an attempt to
# combine two separate QuerySets using QuerySet.union()
kwargs['queryset'] = Publication.objects.all().union(
Publication.objects.all())
return super().formfield_for_manytomany(db_field, request, **kwargs)
admin.site.register(Publication)
admin.site.register(Article, ArticleAdmin)
如文档中所述,queryset
该字段的初始publications
值使用 过滤。formfield_for_manytomany
请注意:这个例子中的实际查询没有意义,它只是返回所有内容,但这并不重要:关键是QuerySet.union()
搞砸了选择。如果你删除它,它可以正常工作union()
。
当我Article
在管理员中添加一个新的而不选择任何出版物时,会发生以下情况:
在“保存”之前(未选择任何内容)
在“保存”之后(所有内容都被选中)
无论我做什么,每次保存表单时都会自动选择所有选项。
鉴于对返回的查询集的限制,我是否使用QuerySet.union()
了错误的方式,或者这是预期的行为?QuerySet.union()
解决方案
正如@tom-carrick 指出的那样,似乎无法过滤QuerySet
返回的a。我想这是由以下文档QuerySet.union()
摘录暗示的:
此外,结果中仅允许使用
LIMIT
、OFFSET
、COUNT(*)
、ORDER BY
和指定列(即切片、count()
、order_by()
和values()
/values_list()
)QuerySet
。
如果您使用的是 Django 3.0,则调用filter()
的结果QuerySet.union()
将引发异常,并显示一条非常清晰的消息:
django.db.utils.NotSupportedError: Calling QuerySet.filter() after union() is not supported.
但是,如果您使用的是 Django 2.2,则不会引发异常:在这种情况下,它只会返回完整的查询集,而不管过滤器参数如何。这里有一个小测试来说明这一点(在 Django 2.2 中):
# using Django 2.2.10
class PublicationTests(TestCase):
def test_union_filter(self):
for i in range(2):
Publication.objects.create()
queryset_union = Publication.objects.filter(id=1).union(
Publication.objects.filter(id=2))
self.assertEqual(2, len(queryset_union))
for obj in queryset_union.all():
self.assertIn(obj, queryset_union.filter(id=1))
self.assertIn(obj, queryset_union.filter())
self.assertIn(obj, queryset_union.filter(id=0))
因此,这一定是当我们使用限制QuerySet.union()
查询集时ModelAdmin
发生的filter()
情况查询集,无论实际的子选择如何。QuerySet.union()
ModelMultipleChoiceField
根据实际用例,可能有一些方法可以使用union()
,如tom-carrick 的回答中所述。
但是,至少有一种方法可以解决QuerySet.union()
这种情况下施加的限制,那就是从查询集联合创建一个新的查询集:
这是ArticleAdmin
原始示例的修改版本:
class ArticleAdmin(admin.ModelAdmin):
def formfield_for_manytomany(self, db_field, request, **kwargs):
if db_field.name == 'publications':
queryset_union = Publication.objects.all().union(
Publication.objects.all())
kwargs['queryset'] = Publication.objects.filter(id__in=queryset_union)
return super().formfield_for_manytomany(db_field, request, **kwargs)
同样,这个人为示例中的实际查询没有意义,但这在这里并不重要。
就数据库访问而言,这可能不是最有效的解决方案。
推荐阅读
- scala - 将常量传递给超级构造函数
- javascript - 仅在隐藏的 div (display:none) 处于活动状态时运行脚本
- javascript - 如何使 Web Worker 异步?
- asp.net-core-mvc - ASP.NET Core 自定义用户角色实现
- javascript - CSS z-index 不适用于父元素具有变换的元素:translate3d
- sql - sql 逻辑 A 和(B 或 C)
- angular - 实现 ngx-gallery 时无法读取未定义的属性“替换”
- python - 在 PySpark 中使用 rdd.map 解压和编码字符串
- bash - 解读单词挑战 - 改进我的 bash 解决方案
- r - 在 sparklyr 中更改 JVM 时区