将Django ModelChoiceField queryset限制为选定项

将Django ModelChoiceField queryset限制为选定项,django,django-forms,validation,manytomanyfield,Django,Django Forms,Validation,Manytomanyfield,这是我挣扎了一天的事情 我有一个消息模型,收件人是用户模型的ManyToManyField 然后是一个用于编写消息的表单。由于有成千上万的用户,在表单中显示多选小部件中的选项并不方便,这是默认行为。相反,使用FcbkComplete jquery插件,我将recipients字段设置为一个输入字段,用户在其中键入收件人,然后它就可以工作了 但是 虽然在表单页面上不可见,但所有用户列表都会呈现在选择字段的页面中,这是我不希望看到的,原因很明显 我试图覆盖ModelChoiceField的行为操纵验

这是我挣扎了一天的事情

我有一个消息模型,
收件人
用户
模型的
ManyToManyField

然后是一个用于编写消息的表单。由于有成千上万的用户,在表单中显示多选小部件中的选项并不方便,这是默认行为。相反,使用FcbkComplete jquery插件,我将recipients字段设置为一个输入字段,用户在其中键入收件人,然后它就可以工作了

但是

虽然在表单页面上不可见,但所有用户列表都会呈现在选择字段的页面中,这是我不希望看到的,原因很明显

我试图覆盖ModelChoiceField的行为操纵验证和queryset,我玩MultipleEchoice小部件,等等。但它们都不起作用,感觉也不自然


那么,什么(最好的)方法可以避免在客户端拥有完整的选项列表,但仍然能够根据查询集进行验证呢?

您看到了吗?我从来没有用过它,但它就在我的脑子里,当我遇到一个问题,比如你想解决的问题时…

我会尝试两种方法中的一种(这两种方法都可能不好!我只是在这里大声思考):

  • 将字段的queryset设置为空(queryset=Model.objects.none()),并让jquery工具使用ajax视图来选择/搜索用户。使用clean_field函数手动验证用户是否有效

  • 这将是我的首选选择:编辑模板,使其不在字段的queryset中循环-因此html在select标记中有0个选项。也就是说,不使用form.as_p()方法或其他任何方法


  • 有一件事我不确定,那就是#2是否仍然会命中数据库,拉出5k+对象,只是不在html中显示它们。我认为不应该,但是。。。一点也不确定

    如果您不关心建议,并且可以使用ID,Django Admin会为这些情况提供一个属性

    您还可以制作一个小部件,它使用用户名而不是ID,并返回一个有效的用户。以下几行中的某一行:

    # I haven't tested this code. It's just for illustration purposes
    class RawUsernameField(forms.CharField):
      def clean(self, value):
        try:
          return User.objects.get(username=value)
        except User.DoesNotExist:
          rause forms.ValidationError(u'Invalid Username')
    

    我通过覆盖
    forms.modelmultipleechoicefield
    的默认小部件来解决这个问题。新窗口小部件仅返回所选字段,而不是整个选项列表:

    class SelectMultipleUserWidget(forms.SelectMultiple):
        def render_options(self, choices, selected_choices):
            choices = [c for c in self.choices if str(c[0]) in selected_choices]
            self.choices = choices
            return super(SelectMultipleUserWidget, 
                         self).render_options([], selected_choices)
    
    class ComposeForm(forms.Form):
        recipients = forms.ModelMultipleChoiceField(queryset=User.objects.all(),
                                                    widget=SelectMultipleUserWidget)
        ...
    

    对于1,在clean_字段()之前调用clean(),并引发
    无效选项
    异常。
    clean
    也应该被覆盖,但它是一个选项。对于2,我不想弄乱模板,因为我有站点范围的字段和表单模板。但这也是一种选择。我仍然觉得应该有一种更干净、不那么刻薄、更像djangoish的方式。谢谢。您的问题的唯一可能的解决方案(假设queryset过滤器不够)是必须使用javascript和ajax视图。在我看来,这已经有相当多的漏洞了——你只能期望django是自一致的,它不能满足使用javascript的所有可能性。我想在调用super()之前,我应该重写clean()方法使其工作:/mmm hacky!另一个选项是将字段更改为CharField,使页面上的javascript返回逗号分隔的用户ID字符串-验证/清理没有问题,并重写save()以执行m2m保存。使用CharField会导致fcbkcomplete出现问题,因为它需要一个select字段。我可以使用其他内容来代替fcbkcomplete,但由于我需要显示用户名并发布用户id,使用select字段似乎更为自然。我仍然希望有一个干净干净利落的方法:/@user608133:从模板中删除选项并不能避免对数据库造成影响,因为表单字段在queryset上执行list(),从而强制对查询进行求值。此表单是为最终用户提供的,因此原始\u id\u字段不合适。对于第二种方法,自定义字段应该继承自ChoiceField,因为我需要将用户id和名称保留为select widget options的键和值对,因为名称会显示,id会发布。但对于ChoiceField,应该在初始化期间定义一个查询集,并导致其他问题,这些问题可以通过黑客的方式避免。非常感谢。