Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use django formsets to create a survey app with questions and answers?

I have a requirement to create a survey form. Different types of users will have different set of questions to answer in the survey.

models.py

from django.contrib.auth.models import Group, User

from django.db import models

ANSWER_CHOICES = (
    ('0', 'No'),
    ('1', 'Yes')
)


class Survey(models.Model):
    name = models.CharField(max_length=100)
    group = models.ForeignKey(Group)

    def __str__(self):
        return self.name


class Feature(models.Model):
    name = models.CharField(max_length=150)
    survey = models.ForeignKey(Survey)

    def __str__(self):
        return self.name


class Rating(models.Model):
    rating = models.IntegerField(choices=ANSWER_CHOICES)
    feature = models.ForeignKey(Feature)
    rating_for = models.ForeignKey(User, related_name='rated_user')
    rating_by = models.ForeignKey(User, related_name='rated_by')

    def __str__(self):
        return str.format("%s - %s", self.feature, self.rating)

The answer(rating) for each question(feature) is either a yes or a no represent by radio buttons. Once the user submits the form, its saves the answers in the rating table.

What is the django way of achieving this?

Thanks


forms.py

from django import forms
from django.forms import modelformset_factory, TextInput

from survey.models import Rating, Feature


class FeatureForm(forms.ModelForm):
    class Meta:
        model = Feature


class RatingForm(forms.ModelForm):
    class Meta:
    model = Rating


 FeatureFormSetBase = modelformset_factory(Feature, extra=0,
                                      widgets={'name':    TextInput(attrs={'readonly': True, })}
                                      )


class FeatureFormSet(FeatureFormSetBase):
    def add_fields(self, form, index):
        super(FeatureFormSet, self).add_fields(form, index)
        form.fields['is_checked'] = forms.BooleanField(required=False)

survey.html

 <form action="." method="post" accept-charset="utf-8">
        <ul class="list-group">
            {% for form in formset.forms %}
                <li class="list-group-item">
                    <div class="hidden"> {{ form.survey }}</div>
                    <span> {{ form.name }} </span>
                    <span> {{ form.id }} </span>
                    <span> {{ form.is_checked }} </span>
                </li>
            {% endfor %}

        </ul>
        <p>
            {{ formset.management_form }}
            {% csrf_token %}
            <button type="submit" name="action" value="save" class="btn btn-success">SAVE</button>
        </p>
    </form>

That's the code I have and it works now but I am not sure if its the best way to solve this problem? I have a lot of code in views.py to parse the post data...is there a cleaner way to achieve this?

view.py

def questionnaire(request):
q = Feature.objects.filter(survey__group=request.user.groups.all()[0]).select_related('survey')
if request.method == 'POST':
    action = request.POST.get('action')
    formset = FeatureFormSet(request.POST, queryset=q)

    if formset.is_valid():
        # iterate over all forms in the formset
        for form in formset.forms:
            if action == u'save':
                answer = form.cleaned_data.get('is_checked')
                print("Saving answer...")
                rating = Rating()
                rating.rating = answer
                rating.feature = form.cleaned_data.get('id')
                rating.rating_for = request.user
                rating.rating_by = request.user
                rating.save()

        redirect('useraccount_dashboard')

else:
    formset = FeatureFormSet(queryset=q)

return render_to_response('account_survey.html', {'formset': formset}, context_instance=RequestContext(request))

UPDATED views.py

def questionnaire(request, instance_id):
try:
    instance = SurveyInstance.objects.get(pk=instance_id, completed=False)
except SurveyInstance.DoesNotExist:
    raise Http404

if instance.rating_by != request.user:
    return HttpResponse('Unauthorized', status=401)

q = Feature.objects.filter(survey=instance.survey)
if request.method == 'POST':
    action = request.POST.get('action')
    formset = FeatureFormSet(request.POST, queryset=q)

    if formset.is_valid():
        # iterate over all forms in the formset
        for form in formset.forms:
            if action == u'save':
                answer = form.cleaned_data.get('is_checked')
                print("Saving answer...")
                rating = Rating()
                rating.rating = answer
                rating.feature = form.cleaned_data.get('id')
                rating.survey_instance = instance
                rating.save()

        instance.completed = True
        instance.save()
        messages.add_message(request, messages.INFO, 'Thank you for your feedback.')
        return redirect('useraccount_dashboard')

else:
    formset = FeatureFormSet(queryset=q)

return render_to_response('account_survey.html', {'formset': formset}, context_instance=RequestContext(request))
like image 889
Chirdeep Tomar Avatar asked Aug 02 '15 16:08

Chirdeep Tomar


1 Answers

Try using form.save() instead of creating the Rating object, filling in it's fields and then saving.

like image 168
jacekbj Avatar answered Oct 14 '22 09:10

jacekbj