Creating a form wizard / formset to answer questions from a table and saving them into another (Survey)

Refresh

6 days ago

Views

5 time

0

I am new to Django. I apologize if I don't make much sense. I have an EU project to create a survey tool for adult educators who are assisting migrants and refugees. I would like to use a WizardView/SessionView together with formset to answer a set of 24 questions from a matrix based on Max-Neef's Human Needs theory and Self Determination Theory. The idea is that Survey model will inherit 24 questions with ForeignKey one by one in a WizardView with previous/next/save functions as the user populate reflection field save them into the survey table. I have been pondering how might I save code instead of creating 24 tables for wizard view and use a hybrid of formsets and form_tools.

How might one create such a tool?

Any help will be most appreciated. Thank you in advance.

I tried ModelForms and class based Views following several tutorials on the web and paid websites.

models.py

from django.urls import reverse
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.forms import ModelChoiceField


class QuestionChoiceField(ModelChoiceField):
    def label_from_instance(self, obj):
        return "Question: {}".format(obj.name)

    def formfield_for_foreignkey(self, question, request, **kwargs):
        if question.name == 'question':
            return QuestionChoiceField(queryset=Question.objects.all())
        return super().formfield_for_foreignkey(question, request, **kwargs)


class Question(models.Model):

    LEVELS = (
        ('Self', 'Relationship with myself'),
        ('Peers', 'Relationship with my peers'),
        ('Learners', 'Relationship with my learners'),
        ('Workplace', 'Relationship with my workplace'),
        ('Society', 'Relationship with my community'),
        ('Planet', 'Relationship with the global society'),
    )

    COMPETENCES = (
        ('Life Internal', 'Life Internal'),
        ('Life External', 'Life External'),
        ('Relatedness', 'Relatedness'),
        ('Autonomy', 'Autonomy'),
    )

    level = models.CharField(max_length=64, choices=LEVELS)
    competence = models.CharField(max_length=64, choices=COMPETENCES)
    question = models.TextField(max_length=400)
    objects = models.Manager()

    class Meta:
        ordering = ['competence']
        verbose_name = "Question"
        verbose_name_plural = "Questions"

    def __str__(self):
        return f'{self.question}'


class Survey(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE,
                                 related_name="surveys", verbose_name="Survey Question:")
    reflection = models.TextField(max_length=400)
    started_at = models.DateTimeField(default=timezone.now)
    is_complete = models.BooleanField(verbose_name="Completed?")
    author = models.ForeignKey(User, related_name="surveys", on_delete=models.CASCADE, verbose_name="Author:")
    objects = models.Manager()

    class Meta:
        ordering = ['author']
        verbose_name = "Survey"
        verbose_name_plural = "Surveys"

    def __str__(self):
        return f'{self.author.username}' ': ''\n' \
            f'{self.question}' '\n' \
            f'{self.reflection}'

    def get_indexed_objects(self):
        return Survey.objects.filter(live=True)

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse('Survey-detail', kwargs={'pk': self.pk})

forms.py

from django import forms
from SDAM.models import Survey, Question
from selectable.forms import AutoComboboxSelectWidget
from .lookups import QuestionLookup


class QuestionForm(forms.ModelForm):

    def clean(self):
        super(QuestionForm, self).clean()

    class Meta:
        model = Question
        fields = ['question', 'level', 'competence']
        widgets = {
            'level': AutoComboboxSelectWidget(lookup_class=QuestionLookup),
            'competence': AutoComboboxSelectWidget(lookup_class=QuestionLookup),

        }


class SurveyForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    class Meta:
        model = Survey
        fields = ['question', 'reflection', 'author', 'started_at', 'is_complete']
        widgets = {
            'level': AutoComboboxSelectWidget(lookup_class=QuestionLookup),
            'competence': AutoComboboxSelectWidget(lookup_class=QuestionLookup),
            'question': AutoComboboxSelectWidget(lookup_class=QuestionLookup),
        }

    def clean(self):
        # Super the clean method to maintain main validation and error messages
        super(SurveyForm, self).clean()

        question = self.cleaned_data.get('question')
        author = self.cleaned_data.get('author')
        reflection = self.cleaned_data.get('reflection')
        started_at = self.cleaned_data.get('started_at')
        is_complete = self.cleaned_data.get('is_complete')
        survey = Survey.objects.get(question=question,
                                    reflection=reflection,
                                    author=author, started_at=started_at,
                                    is_complete=is_complete)
        return survey.save()

    def save(self, commit=True):
        instance = super(SurveyForm, self).save(commit=False)

        if commit:
            # save
            instance.save(update_fields=['question', 'reflection', 'author', 'started_at', 'is_complete'])
        return instance

views.py

from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.auth.models import User
from django.utils import timezone
from django.shortcuts import get_object_or_404, render, reverse
from django.views.generic import (CreateView, DeleteView, DetailView, ListView, UpdateView)
from .filters import SurveyFilter
from .forms import SurveyForm
from .models import Survey


"""
@login_required
def home(request):
    context = {
        'surveys': Survey.objects.all()
    }
    return render(request, 'SDAM/survey.html', context)
"""


class SurveyListView(LoginRequiredMixin, ListView):
    model = Survey
    form_class = SurveyForm
    context_object_name = 'survey_list'
    ordering = ['-started_at']
    template_name = "SDAM/survey_list.html"
    paginate_by = 3

    def get_queryset(self, *args, **kwargs):
            return super().get_queryset()

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['is_complete'] = True
        context['surveys'] = Survey.objects.all()
        return context


class UserSurveyListView(LoginRequiredMixin, ListView):
    model = Survey
    context_object_name = 'user_surveys'
    template_name = 'SDAM/user_surveys.html'  # <app>/<model>_<viewtype>.html
    paginate_by = 3

    def get_queryset(self):
        author = get_object_or_404(User, username=self.kwargs.get('author'))
        return Survey.objects.filter(author=author, is_complete=True).order_by('-started_at')

    def get_context_data(self, **kwargs):
        context = super(UserSurveyListView, self).get_context_data(**kwargs)
        context['user_surveys'] = Survey.objects.all()
        context['is_complete'] = True
        context['filter'] = SurveyFilter(self.request.GET, queryset=self.queryset())
        return context


class SurveyDetailView(LoginRequiredMixin, DetailView):
    model = Survey
    template_name = "SDAM/survey_detail.html"
    queryset = Survey.objects.all()

    def get_object(self):
        obj = super().get_object()
        # Record the last accessed date
        obj.last_accessed = timezone.now()
        obj.save()
        return obj


class SurveyCreateView(LoginRequiredMixin, CreateView):
    model = Survey
    fields = ['question', 'reflection', 'started_at', 'is_complete']

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)


class SurveyUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
    model = Survey
    fields = ['reflection', 'started_at', 'is_complete']
    template_name = "SDAM/survey_update.html"

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)

    def test_func(self):
        survey = self.get_object()
        if self.request.user == survey.author:
            return True
        return False


class SurveyDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
    model = Survey
    success_url = '/sdam/'

    def test_func(self):
        survey = self.get_object()
        if self.request.user == survey.author:
            return True
        return False

lookups.py

from selectable.base import LookupBase
from selectable.registry import registry
from .models import Survey, Question


class QuestionLookup(LookupBase):
    model = Question
    search_fields = ('level', 'competence', 'question',)

    def get_query(self, request, term):
        data = ['Level', 'Competence', 'Question']
        return [x for x in data if x.startswith(term)]


class SurveyLookup(LookupBase):
    model = Survey
    search_fields = ('question', 'reflection', 'author', 'started_at', 'is_complete',)

    def get_query(self, request, term):
        data = ['Question', 'Reflection', 'Author', 'Started_at', 'Is_complete']
        return [x for x in data if x.startswith(term)]


registry.register(QuestionLookup)
registry.register(SurveyLookup)

filters.py

import django_filters
from .models import Survey, Question


class SurveyFilter(django_filters.FilterSet):

    class Meta:
        model = Survey
        fields = ("question", "reflection", "author", "started_at", "is_complete",)


class QuestionFilter(django_filters.FilterSet):

    class Meta:
        model = Question
        fields = ("competence", "level", "question",)

admin.py

from ckeditor.widgets import CKEditorWidget
from selectable.forms import AutoComboboxSelectWidget, AutoCompleteSelectField, AutoCompleteSelectMultipleWidget
from django.forms import ModelChoiceField
from django.contrib import admin
from django import forms
from SDAM.models import Survey, Question
from .lookups import QuestionLookup, SurveyLookup


class QuestionChoiceField(ModelChoiceField):
    def label_from_instance(self, obj):
        return "Question: {}".format(obj.name)

    def formfield_for_foreignkey(self, question, request, **kwargs):
        if question.name == 'question':
            return QuestionChoiceField(queryset=Question.objects.all())
        return super().formfield_for_foreignkey(question, request, **kwargs)


class QuestionAdminForm(forms.ModelForm):
    question = forms.CharField(widget=CKEditorWidget())

    class Meta:
        model = Question
        fields = ['level', 'competence', 'question']

        widgets = {
            'level': AutoComboboxSelectWidget(lookup_class=QuestionLookup),
            'competence': AutoComboboxSelectWidget(lookup_class=QuestionLookup),
        }


class SurveyAdminForm(forms.ModelForm):
    reflection = forms.CharField(widget=CKEditorWidget())

    class Meta:
        model = Survey
        fields = ['question', 'reflection', 'author', 'started_at', 'is_complete']
        widgets = {
            'question': AutoCompleteSelectField(lookup_class=SurveyLookup),
            'level': AutoComboboxSelectWidget(lookup_class=QuestionLookup),
            'competence': AutoComboboxSelectWidget(lookup_class=QuestionLookup),
        }


@admin.register(Question)
class QuestionAdmin(admin.ModelAdmin):
    list_display = ['question', 'competence', 'level', "id"]
    list_filter = ['competence', 'level']
    fieldsets = [
        ("Question details", {"fields": [
         "competence", "level", "question"]}),
    ]

    def question(self):
        return f'{self.question}'


@admin.register(Survey)
class SurveyAdmin(admin.ModelAdmin):
    list_display = ["author", "question", "reflection", "started_at", "is_complete"]
    list_filter = ["author", "question", "is_complete"]
    fieldsets = [
        ("Reflection Details", {"fields": ["question", "reflection", "author", "started_at", "is_complete"]}),
    ]

    def survey_author(self):
        return f'{self.author.username}'

urls.py

from django.urls import path

from .views import (
    SurveyListView,
    UserSurveyListView,
    SurveyDetailView,
    SurveyCreateView,
    SurveyUpdateView,
    SurveyDeleteView,
)
# from . import views


urlpatterns = [
    path('sdam/', SurveyListView.as_view(), name='Survey-list'),
    path('user/<str:username>', UserSurveyListView.as_view(), name='User-surveys'),
    path('sdam/<int:pk>/', SurveyDetailView.as_view(), name='Survey-detail'),
    path('sdam/new/', SurveyCreateView.as_view(), name='Survey-create'),
    path('sdam/<int:pk>/update/', SurveyUpdateView.as_view(), name='Survey-update'),
    path('sdam/<int:pk>/delete/', SurveyDeleteView.as_view(), name='Survey-delete'),
]

0 answers