Add _none-option to ModelChoiceField and filters for organizer and event-permission in event-typeahead (#6224)

* Add optional filters for organizer and event-permission on event-typeahead

* include _none option only if no search query given

Co-authored-by: luelista <weller@rami.io>

* allow _none in Select2, add ModelChoiceFieldWithNone

* fix flake8

---------

Co-authored-by: luelista <weller@rami.io>
This commit is contained in:
Richard Schreiber
2026-06-02 12:23:25 +02:00
committed by GitHub
parent 375c42dff5
commit d555b23275
4 changed files with 84 additions and 22 deletions

View File

@@ -461,3 +461,31 @@ class SalesChannelCheckboxSelectMultiple(forms.CheckboxSelectMultiple):
**super().create_option(name, value, label, selected, index, subindex, attrs), **super().create_option(name, value, label, selected, index, subindex, attrs),
"plugin_missing": plugin and plugin not in self.event.get_plugins(), "plugin_missing": plugin and plugin not in self.event.get_plugins(),
} }
class ModelChoiceIteratorWithNone(forms.models.ModelChoiceIterator):
# see django.forms.models.ModelChoiceIterator for original implementation
def __iter__(self):
if self.field.empty_label is not None:
yield ("", self.field.empty_label)
if self.field.none_label is not None:
yield ("_none", self.field.none_label)
queryset = self.queryset
# Can't use iterator() when queryset uses prefetch_related()
if not queryset._prefetch_related_lookups:
queryset = queryset.iterator()
for obj in queryset:
yield self.choice(obj)
class ModelChoiceFieldWithNone(forms.ModelChoiceField):
iterator = ModelChoiceIteratorWithNone
def __init__(self, *args, **kwargs):
self.none_label = kwargs.pop("none_label", None)
super().__init__(*args, **kwargs)
def to_python(self, value):
if value == "_none":
return value
return super().to_python(value)

View File

@@ -29,17 +29,30 @@ class Select2Mixin:
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def options(self, name, value, attrs=None): def options(self, name, value, attrs=None):
if value and value[0]: if not value or not value[0]:
for i, selected in enumerate(self.choices.queryset.filter(pk__in=value)): return
yield self.create_option( has_none = "_none" in value
None, if has_none:
self.choices.field.prepare_value(selected), value = [v for v in value if v != "_none"]
self.choices.field.label_from_instance(selected), yield self.create_option(
True, None,
i, "_none",
subindex=None, self.choices.field.none_label,
attrs=attrs True,
) 0,
subindex=None,
attrs=attrs
)
for i, selected in enumerate(self.choices.queryset.filter(pk__in=value)):
yield self.create_option(
None,
self.choices.field.prepare_value(selected),
self.choices.field.label_from_instance(selected),
True,
i + (1 if has_none else 0),
subindex=None,
attrs=attrs
)
return return
def optgroups(self, name, value, attrs=None): def optgroups(self, name, value, attrs=None):

View File

@@ -145,11 +145,21 @@ def event_list(request):
if 'can_copy' in request.GET: if 'can_copy' in request.GET:
qs = EventWizardCopyForm.copy_from_queryset(request.user, request.session) qs = EventWizardCopyForm.copy_from_queryset(request.user, request.session)
else: else:
qs = request.user.get_events_with_any_permission(request) permission = request.GET.get('permission')
if permission:
qs = request.user.get_events_with_permission(permission, request)
else:
qs = request.user.get_events_with_any_permission(request)
name_slug_q = Q(name__icontains=i18ncomp(query)) | Q(slug__icontains=query)
organizer = request.GET.get('organizer')
if organizer:
qs = qs.filter(organizer__slug=organizer)
else:
name_slug_q |= Q(organizer__name__icontains=i18ncomp(query)) | Q(organizer__slug__icontains=query)
qs = qs.filter( qs = qs.filter(
Q(name__icontains=i18ncomp(query)) | Q(slug__icontains=query) | name_slug_q
Q(organizer__name__icontains=i18ncomp(query)) | Q(organizer__slug__icontains=query)
).annotate( ).annotate(
min_from=Min('subevents__date_from'), min_from=Min('subevents__date_from'),
max_from=Max('subevents__date_from'), max_from=Max('subevents__date_from'),
@@ -162,10 +172,19 @@ def event_list(request):
total = qs.count() total = qs.count()
pagesize = 20 pagesize = 20
offset = (page - 1) * pagesize offset = (page - 1) * pagesize
results = []
if page == 1 and 'include_none' in request.GET and not query:
results.append({
'id': "_none",
'text': _("No event"),
'name': _("No event"),
'type': "event",
})
results += [
serialize_event(e) for e in qs.select_related('organizer')[offset:offset + pagesize]
]
doc = { doc = {
'results': [ 'results': results,
serialize_event(e) for e in qs.select_related('organizer')[offset:offset + pagesize]
],
'pagination': { 'pagination': {
"more": total >= (offset + pagesize) "more": total >= (offset + pagesize)
} }

View File

@@ -639,11 +639,13 @@ var form_handlers = function (el) {
).append(" ").append($("<div>").text(res.organizer).html()) ).append(" ").append($("<div>").text(res.organizer).html())
); );
} }
$ret.append( if (res.date_range) {
$("<span>").addClass("event-daterange").append( $ret.append(
$("<span>").addClass("fa fa-calendar fa-fw") $("<span>").addClass("event-daterange").append(
).append(" ").append(res.date_range) $("<span>").addClass("fa fa-calendar fa-fw")
); ).append(" ").append(res.date_range)
);
}
return $ret; return $ret;
}, },
}).on("select2:select", function () { }).on("select2:select", function () {