9

I've got some <selects> that I need to populate with some choices that depend on the currently logged in user. I don't think this is possible (or easy) to do from inside the form class, so can I just leave the choices blank and set them in the view instead? Or what approach should I take?

mpen
  • 272,448
  • 266
  • 850
  • 1,236

4 Answers4

5

Not sure if this is the best answer, but in the past I have set the choices of a choice field in the init of the form - you could potentially pass your choices to the constructor of your form...

Matthew J Morrison
  • 4,343
  • 3
  • 28
  • 45
  • Or just the user object, and the rest I could do from inside init. But how do you set the choices from inside init? Do you do `self.field = ...` like normal, or can you explicitly modify just the choices attribute? You have a small code snippet handy? – mpen Aug 12 '10 at 01:28
  • 2
    you can do something like self.fields['field_name'].choices = choice_tuple – Matthew J Morrison Aug 12 '10 at 01:40
2

You could build your form dynamically in you view (well, actually i would rather keep the code outside the view in it's own function and just call it in the view but that's just details)

I did it like this in one project:

user_choices = [(1, 'something'), (2, 'something_else')]
fields['choice'] = forms.ChoiceField(
    choices=user_choices,
    widget=forms.RadioSelect,
)
MyForm = type('SelectableForm', (forms.BaseForm,), { 'base_fields': fields })
form = MyForm()

Obviously, you will want to create the user_choices depending on current user and add whatever field you need along with the choices, but this is a basic principle, I'll leave the rest as the reader exercise.

Davor Lucic
  • 28,970
  • 8
  • 66
  • 76
1

Considering that you have included the user as a parameter, I would solve this using a custom tag.

In your app/templatetags/custom_tags.py something like this:

@register.simple_tag
def combo(user, another_param):
    objects = get_objects(user, another_param)
    str = '<select name="example" id="id_example">'
    for object in objects:
        str += '<option value="%s">%s</option>' % (object.id, object.name)
    str += '</select>'
    return mark_safe(str)

Then in your template:

{% load custom_tags %}
{% special_select user another_param %}

More about custom tags http://docs.djangoproject.com/en/dev/howto/custom-template-tags/

juanefren
  • 2,818
  • 6
  • 32
  • 41
  • 1
    ...what? Are you suggesting I add the extra fields from inside the *template*? That won't even validate, because they aren't apart of the valid `choices`. I think the template is the last place you want to be doing this... that's how I ran into this snag in the first place. – mpen Aug 12 '10 at 01:43
  • 1
    This would work, but I would personally avoid doing it for a few reasons. It isn't DRY - this tag redefines what the existing Select widget already does, it isn't reusable - this couples a specific process of gathering data with a specific presentation of that data, and finally it doesn't leverage the django forms, which I personally love for handling data entry. But still, a viable solution. – Matthew J Morrison Aug 12 '10 at 01:49
  • 1
    @Mark I forgot to say this solution is not based in django-forms, you should get the value from the POST manually. – juanefren Aug 12 '10 at 02:10
  • I'm afraid that's even worse. I need to access this field from inside the `clean` methods so that I can do some conditional validation w/ the other fields. Also, that makes this solution hackable and unvalidated... anyone can modify the HTML and insert their own options and then my app would just explode unless it's validated against the `choices` which Django does for you automatically. – mpen Aug 12 '10 at 03:14
0

Django create dynamic forms - It works !!

Forms.py


class MyForm(forms.Form):

         """ Initialize form values from views"""

        select=forms.BooleanField(label='',required=False)

        field_1=forms.CharField(label='',widget=forms.TextInput(attrs= \

                    {'size':'20','readonly':'readonly',}))

        field_2=forms.ChoiceField(widget=forms.Select(), \

                    choices=((test.id,test.value) for test in test.objects.all()))



        def __init__(self, *args, **kwargs):

           super(MyForm, self).__init__(*args, **kwargs)

           input=kwargs.get('initial',{})

           get_field_1_initial_input_from_views=re.sub("\[|\]|u'|'","",str (input.values()))

           # override field_2 choices based on field_1 input

           try:

               # filter choices

               self.fields[‘field_2'].choices=((test.id,test.value) for test in test.objects.filter ( --------)))

           except:

               pass

Views.py


def views_function(request,val):

        """Dynamically generate input data for formset."""

        Initial_data=[]
        initial_data.append({'field_1':data.info})

        #Initializing formset

        MyFormSet=formset_factory(MyForm,extra=0)

        formset=MyFormSet(initial=initial_data)

        context={'formset':formset}

        if request.method == 'POST':

           formset=MyFormSet(request.POST,request.FILES)

           if formset.is_valid():

               # You can work with the formset dictionary elements in the views function (or) pass it to
               #Forms.py script through an instance of MyForm

               return HttpResponse(formset.cleaned_data)

       return render_to_response(‘test.html', context,context_instance=RequestContext(request))
mpen
  • 272,448
  • 266
  • 850
  • 1,236