python - 如何在 ModelAdmin.formfield_for_manytomany() 中使用 Django QuerySet.union()?

标签 python django django-admin django-queryset

不确定我在这里做错了什么:
我尝试使用 QuerySet.union() , 在 Django 2.2.10 中,在 ModelAdmin.formfield_for_manytomany() 中组合两个查询集(对于同一模型) .但是,在保存表单时,会选择整个查询集,而不管实际做出的选择如何。
请考虑下面的最小示例,基于标准 Django Article/Publication example .

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)
初始querysetpublications字段使用 formfield_for_manytomany 过滤,如 docs 中所述.
请注意:本例中的实际查询没有意义,它只是返回所有内容,但这并不重要:关键是 QuerySet.union()打乱了选择。如果您删除 union(),它可以正常工作.
这是我添加新 Article 时发生的情况在管理员中,不选择任何出版物:
在“保存”之前(未选择任何内容)
enter image description here
“保存”后(选择所有内容)
enter image description here
无论我做什么,每次保存表单时都会自动选择所有选项。
我在用 QuerySet.union()鉴于 restrictions 错误的方式,或者这是预期的行为在 QuerySet.union() 返回的查询集上?

最佳答案

正如@tom-carrick 指出的那样,似乎 QuerySet返回者 QuerySet.union()无法过滤。我想这可以从 documentation 的以下摘录中得到暗示。 :

In addition, only LIMIT, OFFSET, COUNT(*), ORDER BY, and specifying columns (i.e. slicing, count(), order_by(), and values()/values_list()) are allowed on the resulting 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 的信息,请参阅 source ),并且无论实际的子选择如何,它始终返回完整的查询集。
根据实际用例,可能有使用 union() 的方法,如 tom-carrick's answer 中所述.
但是,至少有一种方法可以解决 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)
同样,这个人为示例中的实际查询没有意义,但这在这里并不重要。
就数据库访问而言,这可能不是最有效的解决方案。

关于python - 如何在 ModelAdmin.formfield_for_manytomany() 中使用 Django QuerySet.union()?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62711963/

相关文章:

python - Wagtail 0.8.6 - 管理员 - NoneType' 对象没有属性 'allowed_subpage_types'

Django,将员工从登录重定向到管理站点

python - botocore.exceptions.ProfileNotFound 当代码在 AWS elastic beanstalk 上运行时,但在本地没问题

python - Nltk 斯坦福 pos 标记器错误 : Java command failed

python - 成功单元测试pyinotify?

python - 安装 django-haystack

python - Django 两个多域一关联表

python - 如何编译使用boto访问S3的python代码?

javascript - 使用 JavaScript 在多个 HTML 输入字段值之间进行动态线性插值

django - 如何正确扩展 django admin/base.html 模板?