dushyant88
dushyant88

Reputation: 53

Using muliple forms in the same view

I am trying to learn django framework with few sample applications. Currently, I am working on a feedback/survey application. It uses the following models:

class Survey(models.Model):
    title = models.CharField(_(max_length=255)
    slug = models.SlugField(_(max_length=255, unique=True)
    description= models.TextField(blank=True)


class Question(models.Model):
    survey = models.ForeignKey(Survey, related_name='questions')
    question = models.TextField()

class Answer(models.Model):
    question = models.ForeignKey(Question, related_name='answers')
    answer = models.TextField()

Basically a survey will contain questions and their answers will be saved in the answer.

Now what I don't understand is how to create a form which will display all the questions of a survey when the view is called. I tried to create a form like this

class BaseAnswerForm(Form):
    answer = None
    def __init__(self, question,*args, **kwdargs):
        self.question = question
        #self.answer = None
        super(BaseAnswerForm, self).__init__(*args, **kwdargs)
        answer = self.fields['answer']
        answer.label = question.question

    def save(self, commit=True):
        ans = self.answer
        if ans is None:
            ans = Answer()
        ans.question = self.question
        ans.answer = self.cleaned_data['answer']
        if commit: ans.save()
        return ans

class TextAnswerForm(BaseAnswerForm):
    answer = CharField()

def forms_for_survey(survey, request):
    if request.POST:
        post = request.POST
    else:
        post = None
    return [TextAnswerForm(q,data=post)
            for q in survey.questions.all()]

view for this is like

def show_questions(request, slug):
    survey = get_object_or_404(Survey.objects, slug=slug)
    forms = forms_for_survey(survey, request)
    context = {
        'survey':survey,
        'forms':forms,
        }
    if (request.POST and all(form.is_valid() for form in forms)):
        for form in forms:
            form.save()
        return HttpResponseRedirect(reverse('show_surveys',))
    return render_to_response(
        'feedback/show_questions.html',
        context,
        context_instance = RequestContext(request)
        )

What this does is that, it generates the form as I want, but all the answers are saved from the last answer field. Please help me out, will this be easier using formsets, Can you tell me how it can implemented easier. Thanks

Upvotes: 3

Views: 200

Answers (3)

Jonny Buchanan
Jonny Buchanan

Reputation: 62813

The prefix argument is there to prevent naming conflict issues when repeatedly using the same form (FormSets use it) or using forms which happen to have clashing field names - give each Form a unique prefix, which will be prepended to each generated field name. Using the Question's id would be ideal:

return [TextAnswerForm(q,data=post, prefix='q_%s' % q.pk)
        for q in survey.questions.all()]

Upvotes: 1

Robert Zaremba
Robert Zaremba

Reputation: 8481

you can't get from request object, how many form there was in client www page.
It is allowed to use multiple form in html document - but the only difference is that the POST/GET data contains only the fields from submitted form. So put all your input data in one form and then The simplest solution is to write something like this in your template

<form action="your_viw" method="post">
    {% for q in questions %}
        {{q.question}}<input name="q_{{q.id}}" type="text" />
    {% endfor %}
</form>

and in your view

def show_questions(request, slug):
    survey = get_object_or_404(Survey.objects, slug=slug)
    context = {
        'survey':survey,
        'questions': survey.questions_set,
        }
    fields=[(int(name[2:]), val) for name, val in request.POST.items() if name.startswith('q_')]   # (question id, answer) list
    if fields:
         #validate fields
         # rest of work...
    return ...

Sorry if there is some misspelled word.

Upvotes: 1

Daniel Hepper
Daniel Hepper

Reputation: 29967

The reason is probably that the names of the form fields clash.

You could work around that issue, but did you already have a look at the Formsets Documentation?

A formset is a layer of abstraction to working with multiple forms on the same page.

Upvotes: 2

Related Questions