views:

1018

answers:

2

Let's say I have some contrived models:

class Author(Model):
   name = CharField()

class Book(Model):
   title = CharField()
   author = ForeignKey(Author)

And let's say I want to use a ModelForm for Book:

   class BookForm(ModelForm):
      class Meta:
         model = Book

Simple so far. But let's also say that I have a ton of Authors in my database, and I don't want to have such a long multiple choice field. So, I'd like is to restrict the queryset on the BookForm's ModelMultipleChoiceField author field. Let's also say that the queryset I want can't be chosen until __init__, because it relies on an argument to be passed.

This seems like it might do the trick:

class BookForm(ModelForm):
   class Meta:
      model = Book

   def __init__(self, letter):
      # returns the queryset based on the letter
      choices = getChoices(letter)
      self.author.queryset = choices

Of course, if that just worked I wouldn't be here. That gets me an AttributeError. 'BookForm' object has no attribute 'author'. So, I also tried something like this, where I try to override the ModelForm's default field and then set it later:

class BookForm(ModelForm):
   author = ModelMultipleChoiceField(queryset=Author.objects.all())

   class Meta:
      model = Book

   def __init__(self, letter):
      choices = getChoices(letter)
      self.author.queryset = choices

Which produces the same result.

Anyone know how this is intended to be done?

+4  A: 

Form objects don't have their fields as attributes, you need to look in the "fields" attribute, which is a dictionary:

self.fields['author'].queryset = choices

If you want to fully understand what's going on here, you might be interested in this answer - it's about Models, but Forms work similarly.

Carl Meyer
+2  A: 

Although Carl is correct about the fields, you're also missing a super class call. This is how I do it:

class BookForm(ModelForm):
    author = ModelMultipleChoiceField(queryset=Author.objects.all())

    class Meta:
        model = Book

    def __init__(self, *args, **kwargs):
        letter = kwargs.pop('letter')
        super(BookForm, self).__init__(*args, **kwargs)
        choices = getChoices(letter)
        self.fields['author'].queryset = choices
Paolo Bergantino
Yeah, my real problem area has the super class call, but I skipped it for this example.
Brian
Fair enough. I'll leave this up for any one that wanders into this question for reference's sake, I guess.
Paolo Bergantino
Definitely. It's important to see that the super().__init__() needs to happen before the access to fields.
Brian