Hello there,
I am currently building a django website, one of the apps is a advanced poll tool. My aim is to make it similiar to Google/Microsoft forms.
My current problem is that my create view uses formsets but I use several formsets for my choices (per question one) and also they are associated with one poll which is created in the same request and therefore the same page.
Relevant template code:
{{ question_formset.management_form }}
{% for question_form in question_formset %}
<div class="card shadow my-4">
<div class="card-header">
<div class="row my-0">
<div class="col">
{% include "base_inputs/input_field.html" with field=question_form.text field_with_filters=question_form.text|add_class:"my-1 mx-sm-2" %}
</div>
<div class="row col-auto">
<label for="{{question_form.type.id_for_label}}" class = "col-auto col-form-label">{{question_form.type.label}}</label>
<div class="col-auto">
{% include "base_inputs/input_field.html" with field=question_form.type field_with_filters=question_form.type|add_class:"custom-select my-1 mr-sm-2" %}
</div>
</div>
</div>
</div>
{{choice_formset.management_form}}
{% for choice_form in choice_formset %}
<div class="row card-body">
<div class="col-md-6">
{% include "base_inputs/input_field.html" with field=choice_form.text field_with_filters=choice_form.text %}
</div>
</div>
{% endfor %}
<div class="card-footer">
{% if question_formset.can_delete %}
<div class="custom-control custom-switch form-group">
{% include "base_inputs/input_field.html" with field=question_form.DELETE field_with_filters=question_form.DELETE|attr:"class:custom-control-input" %}
<label for="{{question_form.DELETE.id_for_label}}" class="custom-control-label">{{question_form.DELETE.label}}</label>
</div>
{% endif %}
</div>
</div>
{% endfor %}
Sidenote: Yes I am using widget_tweaks because I think it is a bit more intuitive and gives me better control over what I want the front-end to look like.
In short my choice_formset is looped over and placed after each question so it is inserted multiple times and therfore there is multiple times the same code.
My question is how do I properly save the data on the server when I get all question with their associated choices in one post request?
I have some ideas but I don’t think they are the proper way of handling this. My first idea is to just leave the template as is and looping over the resulting list in the view and manually saving the data.
Another idea would be to use ajax and saving the question and choices everytime something is changed but I am not very familiar to ajax and would like to have another solution.
Here is my current template fully rendered (yes I am german):
I intent to add some JQuery code and buttons to let the user add questions and choices depending on the question type.
The rest of the source code that might be useful
models.py
class Poll (models.Model):
creator = models.ForeignKey(SiteUser, on_delete = models.CASCADE, related_name = 'polls', verbose_name = 'Ersteller')
creation_date = models.DateTimeField(auto_now_add = True, verbose_name = 'Erstellungsdatum')
start_date = models.DateTimeField(verbose_name = 'Startdatum')
end_date = models.DateTimeField(verbose_name = 'Enddatum')
title = models.CharField(max_length = 150, verbose_name = 'Titel')
token = models.CharField('Token fĂĽr url', max_length = 32)
multiple_votes = models.BooleanField('Mehrmals Abstimmen')
class Meta:
verbose_name = "Poll"
verbose_name_plural = "Polls"
def __str__(self):
return f'{self.title}'
class Question (models.Model):
poll = models.ForeignKey(Poll, on_delete = models.CASCADE, related_name = 'question', verbose_name = 'zugehörige Umfrage')
text = models.CharField(max_length = 128, verbose_name = 'Frage')
required = models.BooleanField(verbose_name = 'benötigt', default = False)
LITTLETEXT = 'text'
TEXT = 'textarea'
DATEFIELD = 'date'
CHECKBOX = 'checkbox'
RADIO = 'radio'
SELECT = 'select'
TYPE_CHOICES = [
(TEXT, 'Langer Text'),
(LITTLETEXT, 'Kurzer Text'),
(DATEFIELD, 'Datum'),
(CHECKBOX, 'Mehrfachauswahl'),
(RADIO, 'Einzelauswahl'),
(SELECT, 'Dropdown'),
]
type = models.CharField('Fragetyp', max_length = 150, choices = TYPE_CHOICES)
class Meta:
verbose_name = "Question"
verbose_name_plural = "Questions"
def __str__(self):
return f'{self.text}'
class Choice (models.Model):
question = models.ForeignKey(Question, on_delete = models.CASCADE, related_name = 'choices', verbose_name = 'zugehörige Frage')
text = models.CharField(max_length = 128, verbose_name = 'Option')
class Meta:
verbose_name = "Choice"
verbose_name_plural = "Choices"
def __str__(self):
return f'{self.text}'
views.py so far
def create(request):
QuestionInlineFormset = inlineformset_factory(Poll, Question, fields = ('text', 'type', 'required',), max_num = 100, can_delete = False,)
ChoiceInlineFormset = inlineformset_factory(Question, Choice, fields = ('text',), max_num = 100, extra = 1, can_delete = False,)
if request.method == 'POST':
form = PollCreationForm(request.POST)
print(request.POST)
if form.is_valid():
form.save()
messages.success(request, 'Umfrage erfolgreich erstellt.')
else:
form = PollCreationForm()
context = {
'poll_form' : form,
'question_formset': QuestionInlineFormset,
'choice_formset': ChoiceInlineFormset,
}
return render(request, 'polls/polls_create.html', context)