Skip to content
Snippets Groups Projects
Commit ab09b7be authored by Tom Teichler's avatar Tom Teichler :beers:
Browse files

Rewrite registration

parent 6cdc1928
No related branches found
No related tags found
1 merge request!1Reformat and cleanup
Pipeline #55773 failed
import re
from collections import OrderedDict
from django import forms
from django.forms import fields
from django.utils.translation import ugettext_lazy as _
import phonenumbers
from allauth.account.views import SignupForm
from django_select2.forms import ModelSelect2MultipleWidget, ModelSelect2Widget
from django_starfield import Stars
from material import Fieldset, Layout, Row
from phonenumber_field.formfields import PhoneNumberField
from aleksis.core.forms import AccountRegisterForm
from aleksis.core.mixins import ExtensibleForm
from aleksis.core.models import Group, Person
from aleksis.core.util.core_helpers import get_site_preferences
from .models import Event, EventRegistration, FeedbackAspect, Voucher
......@@ -33,84 +30,12 @@ LICENCE_CHOICES = [
(
"CC-BY-SA-4.0+",
_(
"Creative Commons with attribution and distribution only"
"Creative Commons with attribution and distribution only "
"under the same conditions, 4.0 or later"
),
),
]
NEWSLETTER_CHOICES = get_site_preferences()["paweljong__newsletter_choices"].split(",")
def is_phonenumber(number):
try:
phonenumbers.parse(number, "DE")
except BaseException:
raise forms.ValidationError(_("%s is not a valid phone number") % number)
def is_valid_voucher_for(event_cn):
def _is_valid_voucher(code):
if Voucher.objects.filter(code=code, event_cn=event_cn, used=False).count() == 0:
raise forms.ValidationError(_("The voucher is invalid!"))
return _is_valid_voucher
def clean_phonenumber(field):
def _clean_phonenumber(obj):
value = obj.cleaned_data[field]
if value:
pn = phonenumbers.parse(value, "DE")
value = phonenumbers.format_number(pn, phonenumbers.PhoneNumberFormat.E164)
return value
return _clean_phonenumber
class EventAdditionalSurveyForm(forms.Form):
def __init__(self, event, *args, **kwargs):
super().__init__(*args, **kwargs)
self.add_fields_from_ldap(event)
def add_fields_from_ldap(self, event, before=None):
new_fields = OrderedDict()
for field in event.registration_fields:
label, help_text, *choices = field.split("|")
var = re.sub(r"[^A-Za-z0-9]|^(?=\d)", "_", label)
if choices:
choices_map = [
(re.sub(r"[^A-Za-z0-9]|^(?=\d)", "_", choice), choice) for choice in choices
]
field_attr = forms.ChoiceField(
label=label,
help_text=help_text,
choices=choices_map,
required=False,
)
else:
field_attr = forms.CharField(label=label, help_text=help_text, required=False)
new_fields[var] = field_attr
if before:
before_field_index = list(self.fields.keys()).index(before)
field_names = list(self.fields.keys())
new_field_order = (
field_names[: before_field_index - 1]
+ list(new_fields.keys())
+ field_names[before_field_index - 1 :]
)
self.fields.update(new_fields)
if before:
self.order_fields(new_field_order)
class EventFeedbackForm(ExtensibleForm):
class Meta:
......@@ -135,7 +60,7 @@ class EventFeedbackForm(ExtensibleForm):
required=False,
label=_("Comment for the team"),
help_text=_(
"This comment is for the team only. You can write down everything you"
"This comment is for the team only. You can write down everything you "
"would like to give us as feedback here."
),
widget=forms.Textarea,
......@@ -145,8 +70,8 @@ class EventFeedbackForm(ExtensibleForm):
required=False,
label=_("Comment for the website"),
help_text=_(
"This comment is for the report on our website. Tell in detail about what"
"you experienced, what you liked, what you learned and everything else"
"This comment is for the report on our website. Tell in detail about what "
"you experienced, what you liked, what you learned and everything else "
"you can think of."
),
widget=forms.Textarea,
......@@ -163,7 +88,7 @@ class EventFeedbackForm(ExtensibleForm):
widget=forms.ClearableFileInput(attrs={"multiple": True}),
required=False,
help_text=_(
"If you want to contribute photos to the report, you can upload them here. You can"
"If you want to contribute photos to the report, you can upload them here. You can "
"select multiple files in most file selection dialogs with CTRL + click."
),
)
......@@ -175,7 +100,7 @@ class EventFeedbackForm(ExtensibleForm):
help_text=_("If you upload photos, choose a license here."),
)
def __init__(self, event, *args, **kwargs):
def __init__(self, event, request, *args, **kwargs):
super(EventFeedbackForm, self).__init__(*args, **kwargs)
self._event = event
......@@ -189,27 +114,40 @@ class EventFeedbackForm(ExtensibleForm):
self.add_node_to_layout(node)
class EditEventForm(forms.ModelForm):
class EditEventForm(ExtensibleForm):
"""Form to create or edit an event."""
layout = Layout(
Fieldset(
_("Base data"),
"group",
"linked_group",
Row("display_name", "description"),
Row("place", "published"),
Fieldset(_("Date data"), Row("date_event", "date_registration", "date_retraction")),
Fieldset(_("Event details"), Row("cost", "max_participants")),
Fieldset(_("Event details"), Row("cost", "max_participants"), "information"),
Fieldset(_("Feedback aspects"), "feedback_aspects"),
),
)
class Meta:
model = Event
exclude = []
fields = [
"linked_group",
"display_name",
"description",
"place",
"published",
"date_event",
"date_registration",
"date_retraction",
"cost",
"max_participants",
"feedback_aspects",
"information",
]
widgets = {
"group": ModelSelect2Widget(
search_fields=["name__icontains", "short_name__icontains"],
"linked_group": ModelSelect2Widget(
search_fields=["name__icontains"],
attrs={"data-minimum-input-length": 0, "class": "browser-default"},
),
"feedback_aspects": ModelSelect2MultipleWidget(
......@@ -225,6 +163,16 @@ class EditVoucherForm(forms.ModelForm):
class Meta:
model = Voucher
exclude = ["code", "used_person_uid", "used", "deleted"]
widgets = {
"event": ModelSelect2Widget(
search_fields=["display_name__icontains"],
attrs={"data-minimum-input-length": 0, "class": "browser-default"},
),
"person": ModelSelect2Widget(
search_fields=["first_name__icontains", "last_name__icontains"],
attrs={"data-minimum-input-length": 0, "class": "browser-default"},
),
}
help_texts = {
"event": _("Event the voucher is valid for."),
"person": _("Person the voucher is valid for."),
......@@ -250,30 +198,16 @@ class GenerateListForm(forms.Form):
landscape = forms.BooleanField(
label=_("Landscape"),
help_text=_("Select if output should be in landscape."),
required=False,
)
class RegisterEventForm(forms.ModelForm):
"""Form to register for an event."""
class RegisterEventGuardians(ExtensibleForm):
class Meta:
model = EventRegistration
fields = []
layout = Layout(
Fieldset(
_("Address data"),
Row("street", "housenumber"),
Row("postal_code", "place"),
),
Fieldset(
_("Contact details"),
Row("mobile_number", "email"),
),
Fieldset(
_("Personal data"),
Row("date_of_birth", "sex"),
),
Fieldset(
_("School details"),
Row("school", "school_place", "school_class"),
),
Fieldset(
_("Guardians personal data"),
Row("guardian_first_name", "guardian_last_name"),
......@@ -282,73 +216,12 @@ class RegisterEventForm(forms.ModelForm):
_("Guardians contact details"),
Row("guardian_email", "guardian_mobile_number"),
),
Fieldset(
_("General event information"),
Row("event", "person"),
Row("comment", "channel"),
),
Fieldset(
_("Financial data"),
"voucher_code",
Row("iban", "donation", "accept_sepa"),
),
Fieldset(
_("Declaration of consent"),
Row("accept_terms", "accept_data", "accept_general_terms"),
),
)
class Meta:
model = EventRegistration
exclude = ["date_registred", "voucher"]
help_texts = {
"voucher": _(
"If you have a voucher for the event, enter the code here."
"It will be charged automatically."
),
"donation": (
"Our association would like to offer all children and young"
"people the opportunity to participate in our events. Often,"
"however, the family fee cannot be paid. We therefore have a"
"budget from which we can promote participation after we have"
"carefully examined the necessity and eligibility. We rely on"
"donations for this budget. If you would like to donate a voluntary"
"additional amount for this budget, please indicate this here. We do"
"not permanently save whether and if so in what amount donations are"
"made and also not within the association, e.g. passed on to leisure supervisors."
),
"accept_sepa": _(
"Parents: I authorize the creditor e.V., Rochusstr. 2-4, 53123 Bonn with"
"creditor ID DE70FZT00001497650, to collect the participant fee from my account"
"once using the SEPA core direct debit. At the same time, I instruct my bank to"
"redeem the SEPA core direct debit withdrawn from my account by e.V."
),
"iban": _(
"If your parents want to pay by SEPA direct debit,"
"please let them fill out this field."
),
"accept_terms": _(
"Parents: My child filled out the registration form together with me, but myself,"
"and I agree to the participation, the terms of use and the terms and conditions."
"I am aware that the registration is binding and that withdrawal is only possible"
"in exceptional cases with a valid reason. In addition, I agree to pay the"
"participation fee in advance and agree to the reimbursement guidelines"
"mentioned above."
),
"accept_data": _(
"I consent to the processing of my data as stated in the"
"terms of use and all the data provided is correct. If I am under the"
"age of 16, my parents also agree to this and I can prove this on request"
"(e.g. by making contact with my parents)."
),
"accept_general_terms": _("I agree with the" "AGB and have read them."),
"channel": _("How did you find out about the event?"),
}
guardian_first_name = forms.CharField(
label=_("Guardians first name"),
help_text=_(
"Please enter the first name of the legal guardian who will fill in the registration"
"Please enter the first name of the legal guardian who will fill in the registration "
"with you and who can be reached during the event in an emergency."
),
)
......@@ -356,15 +229,15 @@ class RegisterEventForm(forms.ModelForm):
guardian_last_name = forms.CharField(
label=_("Guardians last name"),
help_text=_(
"Please enter the last name of the legal guardian who will fill in the registration"
"Please enter the last name of the legal guardian who will fill in the registration "
"with you and who can be reached during the event in an emergency."
),
)
guardian_mobile_number = forms.CharField(
guardian_mobile_number = PhoneNumberField(
label=_("Guardians mobile number"),
help_text=_(
"We need the mobile phone number for emergencies if we"
"We need the mobile phone number for emergencies if we "
"urgently need to reach your parents during the event."
),
)
......@@ -373,12 +246,49 @@ class RegisterEventForm(forms.ModelForm):
label=_("Guardians email address"),
)
voucher_code = forms.CharField(
label=_("Voucher code"),
help_text=_("If you have a voucher code, type it in here."),
class RegisterEventContactDetails(ExtensibleForm):
class Meta:
model = EventRegistration
fields = []
layout = Layout(
Fieldset(
_("Personal data"),
Row("first_name", "additional_name", "last_name"),
Row("date_of_birth", "sex"),
),
Fieldset(
_("Address data"),
Row("street", "housenumber"),
Row("postal_code", "place"),
),
Fieldset(
_("Contact details"),
Row("mobile_number", "email"),
),
Fieldset(
_("School details"),
Row("school", "school_place", "school_class"),
),
)
first_name = forms.CharField(
label=_("First name"),
disabled=True,
)
additional_name = forms.CharField(
label=_("Additional names"),
help_text=_("Please enter any additional names."),
required=False,
)
last_name = forms.CharField(
label=_("Last name"),
disabled=True,
)
street = forms.CharField(
label=_("Street"),
)
......@@ -395,15 +305,14 @@ class RegisterEventForm(forms.ModelForm):
label=_("Place"),
)
mobile_number = forms.CharField(
mobile_number = PhoneNumberField(
label=_("Mobile number"),
required=False,
help_text=_(
"Your mobile number helps us to reach you in an emergency during the event, e.g."
"if you are alone with your group at a conference or similar. If you don't have a"
"Your mobile number helps us to reach you in an emergency during the event, e.g. "
"if you are alone with your group at a conference or similar. If you don't have a "
"cell phone, you can leave the field blank."
),
validators=[is_phonenumber],
)
date_of_birth = forms.DateField(
......@@ -413,9 +322,10 @@ class RegisterEventForm(forms.ModelForm):
sex = forms.ChoiceField(
label=_("Sex"),
help_text=_(
"For various reasons, e.g. because we have to keep gender segregation during the night"
"for legal reasons, we need to know if you are a boy or a girl. With some names this is"
"not always immediately recognizable, so we ask you to indicate it here."
"For various reasons, e.g. because we have to keep gender segregation during the night "
"for legal reasons, we need to know if you are a boy or a girl. "
" With some names this is not always immediately recognizable, so we ask you to "
"indicate it here."
),
choices=Person.SEX_CHOICES,
initial=None,
......@@ -423,6 +333,11 @@ class RegisterEventForm(forms.ModelForm):
email = forms.EmailField(
label=_("Email address"),
help_text=_(
"If you don't have an email address, leave this field empty. You can "
"register an email address in the next step."
),
required=False,
)
school = forms.CharField(
......@@ -440,14 +355,114 @@ class RegisterEventForm(forms.ModelForm):
help_text=_("Please enter the class you are going to (e.g. 8a)."),
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["event"].disabled = True
self.fields["person"].disabled = True
self.fields["accept_terms"].required = True
self.fields["accept_general_terms"].required = True
self.fields["accept_data"].required = True
class RegisterEventAdditional(ExtensibleForm):
layout = Layout(
Fieldset(
_("Medical information / intolerances"),
Row("medical_information"),
),
Fieldset(
_("Other remarks"),
Row("comment"),
),
)
class Meta:
model = EventRegistration
fields = ["medical_information", "comment"]
def __init__(self, event, *args, **kwargs):
super(RegisterEventAdditional, self).__init__(*args, **kwargs)
for _field in event.linked_group.additional_fields.all():
field = getattr(fields, _field.field_type)(
required=False,
)
self.fields[_field.title] = field
node = Fieldset(f"{_field.title}", f"{_field.title}")
self.add_node_to_layout(node)
class RegisterEventFinancial(ExtensibleForm):
"""Form to register for an event."""
layout = Layout(
Fieldset(
_("Financial data"),
"voucher_code",
Row("iban", "donation", "accept_sepa"),
),
)
voucher_code = forms.CharField(
label=_("Voucher code"),
help_text=_("If you have a voucher code, type it in here."),
required=False,
)
class Meta:
model = EventRegistration
fields = ["voucher_code", "iban", "donation", "accept_sepa"]
help_texts = {
"voucher": _(
"If you have a voucher for the event, enter the code here. "
"It will be charged automatically."
),
"donation": (
"Our association would like to offer all children and young "
"people the opportunity to participate in our events. Often, "
"however, the family fee cannot be paid. We therefore have a "
"budget from which we can promote participation after we have "
"carefully examined the necessity and eligibility. We rely on "
"donations for this budget. If you would like to donate a voluntary "
"additional amount for this budget, please indicate this here. We do "
"not permanently save whether and if so in what amount donations are "
"made and also not within the association, e.g. passed on to leisure supervisors."
),
"accept_sepa": _(
"Parents: I authorize the creditor e.V., Rochusstr. 2-4, 53123 Bonn with "
"creditor ID DE70FZT00001497650, to collect the participant fee from my account "
"once using the SEPA core direct debit. At the same time, I instruct my bank to "
"redeem the SEPA core direct debit withdrawn from my account by e.V."
),
"iban": _(
"If your parents want to pay by SEPA direct debit, "
"please let them fill out this field."
),
}
class RegisterEventConsent(ExtensibleForm):
layout = Layout(
Fieldset(
_("Declaration of consent"),
Row("accept_terms", "accept_data", "accept_general_terms"),
),
)
class Meta:
model = EventRegistration
fields = ["accept_terms", "accept_data", "accept_general_terms"]
help_texts = {
"accept_terms": _(
"Parents: My child filled out the registration form together with me, but myself, "
"and I agree to the participation, the terms of use and the terms and conditions. "
"I am aware that the registration is binding and that withdrawal is only possible "
"in exceptional cases with a valid reason. In addition, I agree to pay the "
"participation fee in advance and agree to the reimbursement guidelines "
"mentioned above."
),
"accept_data": _(
"I consent to the processing of my data as stated in the "
"terms of use and all the data provided is correct. If I am under the "
"age of 16, my parents also agree to this and I can prove this on request "
"(e.g. by making contact with my parents)."
),
"accept_general_terms": _("I agree with the AGB and have read them."),
}
class EditEventRegistrationForm(forms.ModelForm):
......@@ -456,7 +471,7 @@ class EditEventRegistrationForm(forms.ModelForm):
Fieldset(
_("General event information"),
Row("event", "person"),
Row("comment", "channel"),
Row("comment"),
),
Fieldset(
_("Financial data"),
......@@ -477,42 +492,41 @@ class EditEventRegistrationForm(forms.ModelForm):
"It will be charged automatically."
),
"donation": (
"Our association would like to offer all children and young"
"people the opportunity to participate in our events. Often,"
"however, the family fee cannot be paid. We therefore have a"
"budget from which we can promote participation after we have"
"carefully examined the necessity and eligibility. We rely on"
"donations for this budget. If you would like to donate a voluntary"
"additional amount for this budget, please indicate this here. We do not"
"permanently save whether and if so in what amount donations are made"
"and also not within the association, e.g. passed on to leisure supervisors."
"Our association would like to offer all children and young "
"people the opportunity to participate in our events. Often, "
"however, the family fee cannot be paid. We therefore have a "
"budget from which we can promote participation after we have "
"carefully examined the necessity and eligibility. We rely on "
"donations for this budget. If you would like to donate a voluntary "
"additional amount for this budget, please indicate this here. We do not "
"permanently save whether and if so in what amount donations are made "
"and also not within the association, e.g. passed on to leisure supervisors. "
),
"accept_sepa": _(
"Parents: I authorize the creditor e.V., Rochusstr. 2-4, 53123 Bonn with"
"creditor ID DE70FZT00001497650, to collect the participant fee from my account"
"once using the SEPA core direct debit. At the same time, I instruct my bank"
"Parents: I authorize the creditor e.V., Rochusstr. 2-4, 53123 Bonn with "
"creditor ID DE70FZT00001497650, to collect the participant fee from my account "
"once using the SEPA core direct debit. At the same time, I instruct my bank "
"to redeem the SEPA core direct debit withdrawn from my account by e.V."
),
"iban": _(
"If your parents want to pay by SEPA direct debit,"
"If your parents want to pay by SEPA direct debit, "
"please let them fill out this field."
),
"accept_terms": _(
"Parents: My child filled out the registration form together with me, but myself,"
"and I agree to the participation, the terms of use and the terms and conditions."
"I am aware that the registration is binding and that withdrawal is only possible"
"in exceptional cases with a valid reason. In addition, I agree to pay the"
"participation fee in advance and agree to the reimbursement"
"Parents: My child filled out the registration form together with me, but myself, "
"and I agree to the participation, the terms of use and the terms and conditions. "
"I am aware that the registration is binding and that withdrawal is only possible "
"in exceptional cases with a valid reason. In addition, I agree to pay the "
"participation fee in advance and agree to the reimbursement "
"guidelines mentioned above."
),
"accept_data": _(
"I consent to the processing of my data as stated in the"
"terms of use and all the data provided is correct. If I am under"
"the age of 16, my parents also agree to this and I can prove this on"
"I consent to the processing of my data as stated in the "
"terms of use and all the data provided is correct. If I am under "
"the age of 16, my parents also agree to this and I can prove this on "
"request (e.g. by making contact with my parents)."
),
"accept_general_terms": _("I agree with the" "AGB and have read them."),
"channel": _("How did you find out about the event?"),
}
exclude = []
......@@ -523,6 +537,35 @@ class EditFeedbackAspectForm(forms.ModelForm):
exclude = []
date_of_birth = forms.DateField(label=_("Date of birth"))
extend_register_form = Fieldset(date_of_birth)
AccountRegisterForm.add_node_to_layout(extend_register_form)
class RegisterEventAccount(SignupForm, ExtensibleForm):
"""Form to register new user accounts."""
class Meta:
model = Person
fields = []
layout = Layout(
Fieldset(
_("Base data"),
Row("first_name", "last_name"),
),
Fieldset(
_("Account data"),
"username",
Row("email", "email2"),
Row("password1", "password2"),
),
)
first_name = forms.CharField(label=_("First name"))
last_name = forms.CharField(label=_("Last name"))
password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
password2 = forms.CharField(label=_("Password (again)"), widget=forms.PasswordInput)
def save(self, request):
adapter = get_adapter(request)
user = adapter.new_user(request)
adapter.save_user(request, user, self)
self.custom_signup(request, user)
setup_user_email(request, user, [])
return user
from django.urls import path
from aleksis.apps.postbuero.forms import MailAddForm
from . import views
from .forms import (
RegisterEventAccount,
RegisterEventAdditional,
RegisterEventConsent,
RegisterEventContactDetails,
RegisterEventFinancial,
RegisterEventGuardians,
)
register_forms = [
("email", MailAddForm),
("register", RegisterEventAccount),
("contact_details", RegisterEventContactDetails),
("guardians", RegisterEventGuardians),
("additional", RegisterEventAdditional),
("financial", RegisterEventFinancial),
("consent", RegisterEventConsent),
]
condition_dict = {
"email": views.is_email_needed,
"register": views.is_person_anonymous,
}
urlpatterns = [
path("event/<int:id_>/edit", views.edit_event, name="edit_event_by_id"),
path("event/<int:id_>/feedback", views.feedback_event, name="feedback_event_by_id"),
path("event/<int:id_>/register", views.register_event, name="register_event_by_id"),
path("events/create", views.edit_event, name="create_event"),
path("events/manage", views.ManageEvents.as_view(), name="manage_events"),
path("events/", views.events, name="events"),
path("vouchers/create", views.edit_voucher, name="create_vouchers"),
path("vouchers/<int:id_>/delete", views.delete_voucher, name="delete_voucher_by_id"),
path("vouchers/<int:id_>/edit", views.edit_voucher, name="edit_voucher_by_id"),
path("vouchers/<int:id_>/print", views.print_voucher, name="print_voucher_by_id"),
path("event/<int:pk>/edit", views.EditEventView.as_view(), name="edit_event_by_pk"),
path("event/<int:pk>/feedback", views.feedback_event, name="feedback_event_by_pk"),
path(
"event/<int:pk>/register",
views.RegisterEventWizardView.as_view(register_forms, condition_dict=condition_dict),
name="register_event_by_id",
),
path("event/<slug:slug>", views.EventFullView.as_view(), name="event_by_name"),
path("misc/set_email_needed/<int:pk>", views.set_email_needed, name="set_email_needed"),
path("events/create", views.CreateEventView.as_view(), name="create_event"),
path("events/manage", views.manage_events, name="manage_events"),
path("vouchers/create", views.VoucherCreateView.as_view(), name="create_vouchers"),
path(
"vouchers/<int:pk>/delete", views.VoucherDeleteView.as_view(), name="delete_voucher_by_pk"
),
path("vouchers/<int:pk>/edit", views.VoucherEditView.as_view(), name="edit_voucher_by_pk"),
path("vouchers/<int:pk>/print", views.print_voucher, name="print_voucher_by_pk"),
path("vouchers/", views.vouchers, name="vouchers"),
path("event/lists/generate", views.generate_lists, name="generate_lists"),
path("event/registrations/list", views.registrations, name="registrations"),
path("event/registrations/<int:id_>", views.registration, name="registration_by_id"),
path(
"event/registrations/<int:id_>/edit",
views.edit_registration,
name="edit_registration_by_id",
"event/registrations/<int:pk>",
views.EventRegistrationDetailView.as_view(),
name="registration_by_pk",
),
path(
"event/registrations/<int:pk>/edit",
views.EventRegistrationEditView.as_view(),
name="edit_registration_by_pk",
),
path(
"event/registrations/<int:pk>/delete",
views.EventRegistrationDeleteView.as_view(),
name="delete_registration_by_pk",
),
path(
"event/registrations/<int:id_>/delete",
views.delete_registration,
name="delete_registration_by_id",
"event/feedback_aspects/list",
views.FeedbackAspectListView.as_view(),
name="feedback_aspects",
),
path("event/feedback_aspects/list", views.feedback_aspects, name="feedback_aspects"),
path(
"event/feedback_aspects/create",
views.edit_feedback_aspect,
views.FeedbackAspectCreateView.as_view(),
name="create_feedback_aspect",
),
path(
"event/feedback_aspects/<int:id_>/edit",
views.edit_feedback_aspect,
name="edit_feedback_aspect_by_id",
"event/feedback_aspects/<int:pk>/edit",
views.FeedbackAspectEditView.as_view(),
name="edit_feedback_aspect_by_pk",
),
]
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment