Fix #774 -- Make question options sortable (#786)

* add position field

* add question option sorting logic
* add meta class to question option for sorting
* regenerate migration
* add template content and view mechanics

* Rename migration after rebase & update dependency
This commit is contained in:
Felix Rindt
2018-03-03 20:36:30 +01:00
committed by Raphael Michel
parent e35e264d81
commit 07d8a3d765
6 changed files with 76 additions and 23 deletions
@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.9 on 2018-03-03 16:41
from __future__ import unicode_literals
from django.db import migrations, models
def set_position(apps, schema_editor):
Question = apps.get_model('pretixbase', 'Question')
for q in Question.objects.all():
for i, option in enumerate(q.options.all()):
option.position = i
option.save()
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0083_auto_20180228_2102'),
]
operations = [
migrations.AlterModelOptions(
name='questionoption',
options={'ordering': ('position', 'id'), 'verbose_name': 'Question option', 'verbose_name_plural': 'Question options'},
),
migrations.AddField(
model_name='questionoption',
name='position',
field=models.IntegerField(default=0),
),
migrations.AlterField(
model_name='question',
name='position',
field=models.PositiveIntegerField(default=0, verbose_name='Position'),
),
migrations.RunPython(
set_position,
reverse_code=migrations.RunPython.noop,
),
]
+10 -3
View File
@@ -682,8 +682,9 @@ class Question(LoggedModel):
blank=True, blank=True,
help_text=_('This question will be asked to buyers of the selected products') help_text=_('This question will be asked to buyers of the selected products')
) )
position = models.IntegerField( position = models.PositiveIntegerField(
default=0 default=0,
verbose_name=_("Position")
) )
ask_during_checkin = models.BooleanField( ask_during_checkin = models.BooleanField(
verbose_name=_('Ask during check-in instead of in the ticket buying process'), verbose_name=_('Ask during check-in instead of in the ticket buying process'),
@@ -779,10 +780,16 @@ class Question(LoggedModel):
class QuestionOption(models.Model): class QuestionOption(models.Model):
question = models.ForeignKey('Question', related_name='options') question = models.ForeignKey('Question', related_name='options')
answer = I18nCharField(verbose_name=_('Answer')) answer = I18nCharField(verbose_name=_('Answer'))
position = models.IntegerField(default=0)
def __str__(self): def __str__(self):
return str(self.answer) return str(self.answer)
class Meta:
verbose_name = _("Question option")
verbose_name_plural = _("Question options")
ordering = ('position', 'id')
class Quota(LoggedModel): class Quota(LoggedModel):
""" """
@@ -799,7 +806,7 @@ class Quota(LoggedModel):
Please read the documentation section on quotas carefully before doing Please read the documentation section on quotas carefully before doing
anything with quotas. This might confuse you otherwise. anything with quotas. This might confuse you otherwise.
http://docs.pretix.eu/en/latest/development/concepts.html#restriction-by-number https://docs.pretix.eu/en/latest/development/concepts.html#quotas
The AVAILABILITY_* constants represent various states of a quota allowing The AVAILABILITY_* constants represent various states of a quota allowing
its items/variations to be up for sale. its items/variations to be up for sale.
@@ -50,6 +50,7 @@
<div class="sr-only"> <div class="sr-only">
{{ form.id }} {{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %} {% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field form.ORDER form_group_class="" layout="inline" %}
</div> </div>
<div class="row question-option-row"> <div class="row question-option-row">
<div class="col-xs-10"> <div class="col-xs-10">
@@ -57,6 +58,10 @@
{% bootstrap_field form.answer layout='inline' form_group_class="" %} {% bootstrap_field form.answer layout='inline' form_group_class="" %}
</div> </div>
<div class="col-xs-2 text-right"> <div class="col-xs-2 text-right">
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button> <button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button> <i class="fa fa-trash"></i></button>
</div> </div>
@@ -70,12 +75,17 @@
<div class="sr-only"> <div class="sr-only">
{{ formset.empty_form.id }} {{ formset.empty_form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %} {% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field formset.empty_form.ORDER form_group_class="" layout="inline" %}
</div> </div>
<div class="row question-option-row"> <div class="row question-option-row">
<div class="col-xs-10"> <div class="col-xs-10">
{% bootstrap_field formset.empty_form.answer layout='inline' form_group_class="" %} {% bootstrap_field formset.empty_form.answer layout='inline' form_group_class="" %}
</div> </div>
<div class="col-xs-2 text-right"> <div class="col-xs-2 text-right">
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button> <button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button> <i class="fa fa-trash"></i></button>
</div> </div>
+13 -18
View File
@@ -338,7 +338,7 @@ class QuestionMixin:
formsetclass = inlineformset_factory( formsetclass = inlineformset_factory(
Question, QuestionOption, Question, QuestionOption,
form=QuestionOptionForm, formset=I18nFormSet, form=QuestionOptionForm, formset=I18nFormSet,
can_order=False, can_delete=True, extra=0 can_order=True, can_delete=True, extra=0
) )
return formsetclass(self.request.POST if self.request.method == "POST" else None, return formsetclass(self.request.POST if self.request.method == "POST" else None,
queryset=(QuestionOption.objects.filter(question=self.object) queryset=(QuestionOption.objects.filter(question=self.object)
@@ -358,30 +358,25 @@ class QuestionMixin:
) )
form.instance.delete() form.instance.delete()
form.instance.pk = None form.instance.pk = None
elif form.has_changed():
form.instance.question = obj forms = self.formset.ordered_forms + [
form.save() ef for ef in self.formset.extra_forms
if ef not in self.formset.ordered_forms and ef not in self.formset.deleted_forms
]
for i, form in enumerate(forms):
form.instance.position = i
form.instance.question = obj
created = not form.instance.pk
form.save()
if form.has_changed():
change_data = {k: form.cleaned_data.get(k) for k in form.changed_data} change_data = {k: form.cleaned_data.get(k) for k in form.changed_data}
change_data['id'] = form.instance.pk change_data['id'] = form.instance.pk
obj.log_action( obj.log_action(
'pretix.event.question.option.added' if created else
'pretix.event.question.option.changed', 'pretix.event.question.option.changed',
user=self.request.user, data=change_data user=self.request.user, data=change_data
) )
for form in self.formset.extra_forms:
if not form.has_changed():
continue
if self.formset._should_delete_form(form):
continue
form.instance.question = obj
form.save()
change_data = {k: form.cleaned_data.get(k) for k in form.changed_data}
change_data['id'] = form.instance.pk
obj.log_action(
'pretix.event.question.option.added',
user=self.request.user, data=change_data
)
return True return True
return False return False
+1 -1
View File
@@ -4,7 +4,7 @@ djangorestframework==3.6.*
python-dateutil python-dateutil
pytz pytz
django-bootstrap3==8.2.* django-bootstrap3==8.2.*
django-formset-js-improved==0.5.0.1 django-formset-js-improved==0.5.0.2
django-compressor==2.1.1 django-compressor==2.1.1
django-hierarkey==1.0.*,>=1.0.3 django-hierarkey==1.0.*,>=1.0.3
django-filter==1.0.* django-filter==1.0.*
+1 -1
View File
@@ -69,7 +69,7 @@ setup(
'python-dateutil==2.4.*', 'python-dateutil==2.4.*',
'pytz', 'pytz',
'django-bootstrap3==8.2.*', 'django-bootstrap3==8.2.*',
'django-formset-js-improved==0.5.0.1', 'django-formset-js-improved==0.5.0.2',
'django-compressor==2.1', 'django-compressor==2.1',
'django-hierarkey==1.0.*,>=1.0.2', 'django-hierarkey==1.0.*,>=1.0.2',
'django-filter==1.0.*', 'django-filter==1.0.*',