Login

Getting dynamic model choices in newforms

Author:
ubernostrum
Posted:
February 26, 2007
Language:
Python
Version:
Pre .96
Score:
13 (after 13 ratings)

This is an excerpt from the form code used on this site; the tricky bit here is making the choices for the language field get filled in dynamically from Language.objects.all() on each form instantiation, so that new languages can be picked up automatically. It also adds a blank choice at the beginning so that users can't accidentally ignore the field and incorrectly end up with whatever Language was first in the list.

If you use this, always remember that you have to call the superclass __init__ before you set your dynamic choices, and that you need to accept *args and **kwargs so you can pass them to it.

In theory, ModelChoiceField will solve this, but it still seems to be a bit buggy.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
from django import newforms as forms
from models import Language

# I put this on all required fields, because it's easier to pick up
# on them with CSS or JavaScript if they have a class of "required"
# in the HTML. Your mileage may vary.
attrs_dict = { 'class': 'required' }

class AddSnippetForm(forms.Form):
    """
    Form used for adding Snippets.
    
    """
    def __init__(self, *args, **kwargs):
        super(AddSnippetForm, self).__init__(*args, **kwargs)
        self.fields['language'].choices = [('', '----------')] + [(lang.id, lang.name) for lang in Language.objects.all()]
    
    title = forms.CharField(max_length=250, widget=forms.TextInput(attrs=attrs_dict))
    description = forms.CharField(widget=forms.Textarea(attrs=attrs_dict))
    code = forms.CharField(widget=forms.Textarea(attrs=attrs_dict))
    tag_list = forms.CharField(max_length=250, widget=forms.TextInput(attrs=attrs_dict))
    language = forms.ChoiceField(choices=(), widget=forms.Select(attrs=attrs_dict))

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 2 months ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 2 months, 1 week ago
  3. Serializer factory with Django Rest Framework by julio 9 months, 1 week ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 9 months, 4 weeks ago
  5. Help text hyperlinks by sa2812 10 months, 3 weeks ago

Comments

dschein (on March 1, 2007):

It is also easy to create a builder function. You can use the same caching mechanism you do with other functions and even return different forms depending on the arguments.

@memoize_to_cache
def schedule_form(schedule, category):
    def _get_subjects():
        # some time consuming returns your choices
        # ...

    if category == CR:
        class CreditCourseSearchForm (forms.Form):
            subjects = forms.MultipleChoiceField(_get_subjects(), required=False)
            advising_codes = forms.ChoiceField(ADVISING_CODE_CHOICES, required=False)
            # ... 
        return CreditCourseSearchForm
    else:
        class NonCreditCourseSearchForm (forms.Form):
            subjects = forms.MultipleChoiceField(_get_subjects(), required=False)
            learning_community = forms.MultipleChoiceField(COMMUNITIES, required=False)
            # ... 
        return CreditCourseSearchForm

#

wub (on July 23, 2007):

Thank you!!!

I have spent several days trying to figure out how to filter a lookup table based on a value passed at run time. I was on the verge of deciding it could not be done, when I discovered this Snippet.

My only wish now is that I could understand >why< this works, when nothing else I tried did, but fortunately many things that I do not understand work anyway.

#

max (on July 25, 2007):

Thank you for this snippet, one more question:

after

self.fields['language'].choices =

i put

'myfunction()'

, returning a list of choices and between '' because it is defined in another python file.

i got the following error at execution time:

need more than 1 value to unpack

What does it mean ???

Thank you very much

max

#

ben_eliott (on May 8, 2008):

Thank you for this helpful snippet ubernostrum.

If the form is bound and you want to filter the database query based on a value therein you can access it by: self.data.get('fieldname')

#

beuc (on November 3, 2009):

You can also use the 'queryset' field of ModelChoiceField:

class MyForm(forms.ModelForm):
    class Meta:
        model = my_models.MyModel

    def __init__(self, *args, **kwargs):
        dynarg = kwargs.pop('dynarg')
        self.base_fields['myfield'].empty_label = None
        self.base_fields['myfield'].queryset = my_models.MyField.objects.filter(myfilter=dynarg)
        super(MyForm, self).__init__(*args, **kwargs)

#

ashish (on May 10, 2012):

class MinorMessageForm( forms.Form):

#add choice by user

def __init__(self, *args, **kwargs):
    uid = kwargs.pop('uid')
    if uid: #remember to use exception handleing
        userdata = User.objects.get( id=uid)
        udata = userdata.minor_custodian
        l = []
        for u in udata:
             l.append(u.get('minor-name') +' - '+ u.get('custodian-email'))
    super(MinorMessageForm, self).__init__(*args, **kwargs)
    self.fields['for_user'].choices=  [('', '----------')]

its giving me key error at last line : for_user

#

Please login first before commenting.