somewhat presentable MultiValueField without validation

This commit is contained in:
Lukas Bockstaller
2025-12-05 16:49:04 +01:00
parent 07f38819a6
commit 9743d7ae52
5 changed files with 173 additions and 32 deletions

View File

@@ -34,6 +34,7 @@
# License for the specific language governing permissions and limitations under the License.
import copy
import os
from collections import namedtuple
from decimal import Decimal
from urllib.parse import urlencode
@@ -60,7 +61,7 @@ from pretix.base.forms import I18nFormSet, I18nMarkdownTextarea, I18nModelForm
from pretix.base.forms.widgets import DatePickerWidget
from pretix.base.models import (
Item, ItemCategory, ItemProgramTime, ItemVariation, Order, OrderPosition,
Question, QuestionOption, Quota, SubEvent,
Question, QuestionOption, Quota, SubEvent, Event
)
from pretix.base.models.items import ItemAddOn, ItemBundle, ItemMetaValue
from pretix.base.signals import item_copy_data
@@ -274,6 +275,85 @@ class QuestionOptionForm(I18nModelForm):
'answer',
]
subeventSelectionParts = namedtuple('subeventWidgetParts', ['selection', 'startDateTime', 'endDateTime', 'subevents'])
class SubeventSelectionWidget(forms.MultiWidget):
template_name = 'pretixcontrol/forms/widgets/subeventselection.html'
parts = subeventSelectionParts
def __init__(self, event: Event, status_choices, subevent_choices, *args, **kwargs):
widgets = subeventSelectionParts(
selection=forms.RadioSelect(
choices=status_choices,
),
startDateTime=SplitDateTimePickerWidget(),
endDateTime=SplitDateTimePickerWidget(),
subevents=Select2(
attrs={
'class': 'simple-subevent-choice',
'data-model-select2': 'event',
'data-select2-url': reverse('control:event.subevents.select2', kwargs={
'event': event.slug,
'organizer': event.organizer.slug,
}),
'data-placeholder': pgettext_lazy('subevent', 'All dates')
},
)
)
widgets.subevents.choices = subevent_choices
super().__init__(widgets=widgets, *args, **kwargs)
def decompress(self, value):
if value:
return value
return ['subevent', "", ""]
class SubeventSelectionField(forms.MultiValueField):
def __init__(self, *args, **kwargs):
self.event = kwargs.pop('event')
choices = [
("subevent", _("Subevent")),
("timerange", _("Timerange"))
]
fields = subeventSelectionParts(
selection=forms.ChoiceField(
choices=choices,
required=True,
initial="subevent",
),
startDateTime=SplitDateTimeField(
required=False,
),
endDateTime=SplitDateTimeField(
required=False,
),
subevents=forms.ModelChoiceField(
required=False,
queryset=self.event.subevents,
empty_label = pgettext_lazy('subevent', 'All dates')
)
)
kwargs['widget'] = SubeventSelectionWidget(
event=self.event,
status_choices=choices,
subevent_choices=fields.subevents.widget.choices,
)
super().__init__(
fields=fields, require_all_fields=False, *args, **kwargs
)
def compress(self, data_list):
if not data_list:
return None
return subeventSelectionParts(*data_list)
class QuestionFilterForm(forms.Form):
STATUS_VARIANTS = [
@@ -296,42 +376,36 @@ class QuestionFilterForm(forms.Form):
}
),
required=False,
label=_("Status"),
)
item = forms.ChoiceField(
choices=[],
widget=forms.Select(
attrs={'class': 'form-control'}
),
required=False
)
subevent = forms.ModelChoiceField(
queryset=SubEvent.objects.none(),
required=False,
empty_label=pgettext_lazy('subevent', 'All dates')
label=_("Items")
)
def __init__(self, *args, **kwargs):
self.event = kwargs.pop('event')
super().__init__(*args, **kwargs)
self.initial['status'] = "np"
self.fields['item'].choices = [('', _('All products'))] + [(item.id, item.name) for item in Item.objects.filter(event=self.event)]
if self.event.has_subevents:
self.fields["subevent"].queryset = self.event.subevents.all()
self.fields['subevent'].widget = Select2(
attrs={
'class': 'form-control simple-subevent-choice',
'data-model-select2': 'event',
'data-select2-url': reverse('control:event.subevents.select2', kwargs={
'event': self.event.slug,
'organizer': self.event.organizer.slug,
}),
'data-placeholder': pgettext_lazy('subevent', 'All dates')
}
self.fields['subevent_selection'] = SubeventSelectionField(
event=self.event,
label=_("Subevents"),
help_text=_(" Select the subevents that should be included in the statistics either by subevent or by the timerange in which they occur.")
)
self.fields['subevent'].widget.choices = self.fields['subevent'].choices
else:
del self.fields['subevent']
self.initial['status'] = "np"
self.fields['item'].choices = [('', _('All products'))] + [(item.id, item.name) for item in
Item.objects.filter(event=self.event)]
def clean(self):
super().clean()
import pprint
pprint.pprint(self.cleaned_data)
def order_position_queryset(self):
fdata = self.data

View File

@@ -0,0 +1,38 @@
{% load i18n %}
<div class="subevent-selection col-lg-12">
{% for group_name, group_choices, group-index in widget.subwidgets.0.optgroups %}
{% for selopt in group_choices %}
<div class="radio">
<label class="col-lg-2">
<input type="radio" name="{{ widget.subwidgets.0.name }}" value="{{ selopt.value }}"
{% include "django/forms/widgets/attrs.html" with widget=selopt %} />
{{ selopt.label }}
</label>
{% if selopt.value == "subevent" %}
{% with widget.subwidgets.3 as widget %}
{% include widget.template_name %}
{% endwith %}
{% elif selopt.value == "timerange" %}
{% with widget.subwidgets.1 as widget %}
{% include widget.template_name %}
{% endwith %}
<span class="spacer">{% trans "until" %}</span>
{% with widget.subwidgets.2 as widget %}
{% include widget.template_name %}
{% endwith %}
{% endif %}
</div>
{% endfor %}
{% endfor %}
</div>

View File

@@ -5,6 +5,11 @@
{% load formset_tags %}
{% block title %}{% blocktrans with name=question.question %}Question: {{ name }}{% endblocktrans %}{% endblock %}
{% block inside %}
{% for e in form.errors %}
<div class="alert alert-danger has-error">
{{ e }}
</div>
{% endfor %}
<h1>
{% blocktrans with name=question.question %}Question: {{ name }}{% endblocktrans %}
<a href="{% url "control:event.items.questions.edit" event=request.event.slug organizer=request.event.organizer.slug question=question.pk %}"
@@ -20,17 +25,24 @@
</div>
<form class="panel-body filter-form" action="" method="get">
<div class="row">
<div class="col-lg-2 col-sm-6 col-xs-6">
{{ form.status }}
<div class="col-lg-4 col-sm-6 col-xs-6">
{% bootstrap_label form.status.label %}
{% bootstrap_field form.status layout="inline" %}
</div>
<div class="col-lg-5 col-sm-6 col-xs-6">
{{ form.item }}
<div class="col-lg-8 col-sm-6 col-xs-6">
{% bootstrap_label form.item.label %}
{% bootstrap_field form.item layout="inline" %}
</div>
<div class="col-lg-12 col-sm-6 col-xs-6">
{% bootstrap_label form.subevent_selection.label %}
{{ form.subevent_selection }}
<div class="help-block">
{{ form.subevent_selection.help_text }}
</div>
</div>
<div class="col-lg-5 col-sm-6 col-xs-6">
<p>
{{ form.subevent }}
</p>
</div>
</div>
<div class="text-right">
<button class="btn btn-primary btn-lg" type="submit">

View File

@@ -777,7 +777,10 @@ class QuestionView(EventPermissionRequiredMixin, ChartContainingView, DetailView
def get_context_data(self, **kwargs):
ctx = super().get_context_data()
ctx['items'] = self.object.items.all()
ctx['form'] = QuestionFilterForm(data=self.request.GET, event=self.request.event)
ctx['form'] = QuestionFilterForm(
data=self.request.GET,
event=self.request.event
)
if ctx['form'].is_valid():
opqs = ctx['form'].order_position_queryset()
stats = self.get_answer_statistics(opqs)

View File

@@ -216,6 +216,20 @@ td > .form-group > .checkbox {
.input-group-btn .btn {
padding-bottom: 8px;
}
.subevent-selection{
.splitdatetimerow{
max-width: 500px;
display: inline-block;
}
.spacer{
margin-left: 20px;
margin-right: 20px;
}
.select2{
max-width:500px;
display: inline-block;
}
}
.reldatetime {
input[type=text], select {
display: inline-block;