开发者

Limiting Django ModelChoiceField queryset to selected items

Here is what I've been struggling for a day...

I have a Message model in which recipients is a ManyToManyField to the User model.

Then there is a form for composing messages. As there are thousands of users, it is not convenient to display the options in a multiple select widget in the form, which is the default behavior. Instead, using FcbkCo开发者_JAVA技巧mplete jquery plugin, I made the recipients field look like an input field where the user types the recipients, and it WORKS.

But...

Although not visible on the form page, all the user list is rendered into the page in the select field, which is something I don't want for obvious reasons.

I tried overriding the ModelChoiceField's behavior manipulating validation and queryset, I played with the MultipleChoice widget, etc. But none of them worked and felt natural.

So, what is the (best) way to avoid having the whole list of options on the client side, but still be able to validate against a queryset?


Have you seen django-ajax-selects? I've never used it, but it's in my mental grab bag for when I come across a problem like what it sounds like you're trying to solve...


I would be trying one of two ways (both of which might be bad! I'm really just thinking out aloud here):

  1. Setting the field's queryset to be empty (queryset = Model.objects.none()) and having the jquery tool use ajax views for selecting/searching users. Use a clean_field function to manually validate the users are valid.

  2. This would be my preferred choice: edit the template to not loop through the field's queryset - so the html would have 0 options inside the select tags. That is, not using form.as_p() method or anything.

One thing I'm not sure about is whether #2 would still hit the database, pulling out the 5k+ objects, just not displaying them in the html. I don't think it should, but... not sure, at all!


If you don't care about suggestions, and is OK to use the ID, Django Admin comes with a raw_id_field attribute for these situations.

You could also make a widget, that uses the username instead of the ID and returns a valid user. Something among the lines of:

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


I solve this by overriding the forms.ModelMultipleChoiceField's default widget. The new widget returns only the selected fields, not the entire list of options:

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)
    ...
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜