forked from beba/foerderbarometer
refactor(forms): unify forms with BaseApplicationForm and CommonOrderMixin for consistent fields and order
This commit is contained in:
parent
eea08e6075
commit
98d1ae9284
|
|
@ -1,9 +1,10 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.forms import ModelForm, ChoiceField, RadioSelect, BooleanField
|
from django.forms import ModelForm, ChoiceField, RadioSelect, BooleanField, CharField, EmailField
|
||||||
from django.contrib.admin.widgets import AdminDateWidget
|
from django.contrib.admin.widgets import AdminDateWidget
|
||||||
from django.forms.renderers import DjangoTemplates
|
from django.forms.renderers import DjangoTemplates
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
from django import forms
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
TYPE_CHOICES,
|
TYPE_CHOICES,
|
||||||
|
|
@ -33,20 +34,61 @@ class FdbForm(ModelForm):
|
||||||
|
|
||||||
|
|
||||||
class ProjectForm(FdbForm):
|
class ProjectForm(FdbForm):
|
||||||
|
|
||||||
# start = DateField(widget=AdminDateWidget())
|
# start = DateField(widget=AdminDateWidget())
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Project
|
model = Project
|
||||||
exclude = ('pid', 'project_of_year', 'finance_id','granted', 'granted_date', 'realname', 'email',\
|
exclude = ('pid', 'project_of_year', 'finance_id', 'granted', 'granted_date', 'realname', 'email', \
|
||||||
'end_mail_send', 'status', 'persons', 'survey_mail_date', 'mail_state')
|
'end_mail_send', 'status', 'persons', 'survey_mail_date', 'mail_state')
|
||||||
widgets = {'start': AdminDateWidget(),
|
widgets = {'start': AdminDateWidget(),
|
||||||
'end': AdminDateWidget(),}
|
'end': AdminDateWidget(), }
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
js = ('dropdown/js/otrs_link.js',)
|
js = ('dropdown/js/otrs_link.js',)
|
||||||
|
|
||||||
|
|
||||||
|
class CommonOrderMixin(forms.Form):
|
||||||
|
"""
|
||||||
|
Ensures a consistent field order for all forms that inherit from this mixin.
|
||||||
|
|
||||||
|
The goal is to always render:
|
||||||
|
- "realname" and "email" fields at the top,
|
||||||
|
- all other fields in the middle (in the order Django provides),
|
||||||
|
- "check" (terms/privacy checkbox) at the bottom.
|
||||||
|
|
||||||
|
This keeps the UX consistent across all forms without repeating field_order logic.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Fields that should always appear first in the form
|
||||||
|
field_order_head = ('realname', 'email')
|
||||||
|
|
||||||
|
# Fields that should always appear last in the form
|
||||||
|
field_order_tail = ('check',) # rename if your checkbox field is called differently
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Override the default form initialization to reorder fields.
|
||||||
|
The method:
|
||||||
|
1. Collects all existing field names.
|
||||||
|
2. Puts the 'head' fields first (if they exist in this form).
|
||||||
|
3. Keeps all other fields in the middle.
|
||||||
|
4. Puts the 'tail' fields last (if they exist in this form).
|
||||||
|
"""
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
existing = list(self.fields.keys())
|
||||||
|
|
||||||
|
# Select only fields that actually exist in this form
|
||||||
|
head = [f for f in self.field_order_head if f in self.fields]
|
||||||
|
tail = [f for f in self.field_order_tail if f in self.fields]
|
||||||
|
|
||||||
|
# All other fields that are not explicitly in head or tail
|
||||||
|
middle = [f for f in existing if f not in (*head, *tail)]
|
||||||
|
|
||||||
|
# Apply the new order to the form fields
|
||||||
|
self.order_fields(head + middle + tail)
|
||||||
|
|
||||||
|
|
||||||
class ExternForm(FdbForm):
|
class ExternForm(FdbForm):
|
||||||
|
|
||||||
choice = ChoiceField(choices=TYPE_CHOICES.items(), widget=RadioSelect,
|
choice = ChoiceField(choices=TYPE_CHOICES.items(), widget=RadioSelect,
|
||||||
|
|
@ -79,7 +121,33 @@ HOTEL_CHOICES = {'TRUE': mark_safe('Hotelzimmer benötigt'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class TravelForm(FdbForm):
|
class BaseApplicationForm(FdbForm):
|
||||||
|
"""
|
||||||
|
Base form for all external applications.
|
||||||
|
|
||||||
|
- Adds standard fields that must appear in every form:
|
||||||
|
* realname (applicant's full name)
|
||||||
|
* email (contact address for notifications)
|
||||||
|
* check (confirmation of data protection and funding policy)
|
||||||
|
- Ensures consistency across all application types.
|
||||||
|
"""
|
||||||
|
realname = CharField(
|
||||||
|
label="Realname",
|
||||||
|
required=True,
|
||||||
|
help_text="Bitte gib deinen Vor- und Nachnamen ein."
|
||||||
|
)
|
||||||
|
email = EmailField(
|
||||||
|
label="E-Mail-Adresse",
|
||||||
|
required=True,
|
||||||
|
help_text="Bitte gib deine E-Mail-Adresse ein, damit dich <br> Wikimedia Deutschland bei Rückfragen oder für <br> die Zusage kontaktieren kann."
|
||||||
|
)
|
||||||
|
check = BooleanField(required=True,
|
||||||
|
label=format_html(
|
||||||
|
"Ich stimme den <a href='{}' target='_blank' rel='noopener'>Datenschutzbestimmungen</a> und der<br> <a href='{}' target='_blank' rel='noopener'>Richtlinie zur Förderung der Communitys</a> zu",
|
||||||
|
settings.DATAPROTECTION, settings.FOERDERRICHTLINIEN))
|
||||||
|
|
||||||
|
|
||||||
|
class TravelForm(BaseApplicationForm, CommonOrderMixin):
|
||||||
# TODO: add some javascript to show/hide other-field
|
# TODO: add some javascript to show/hide other-field
|
||||||
|
|
||||||
# this is the code, to change required to false if needed
|
# this is the code, to change required to false if needed
|
||||||
|
|
@ -107,7 +175,7 @@ class TravelForm(FdbForm):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class LibraryForm(FdbForm):
|
class LibraryForm(BaseApplicationForm, CommonOrderMixin):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Library
|
model = Library
|
||||||
|
|
@ -144,7 +212,7 @@ class HonoraryCertificateForm(FdbForm):
|
||||||
js = ('dropdown/js/otrs_link.js',)
|
js = ('dropdown/js/otrs_link.js',)
|
||||||
|
|
||||||
|
|
||||||
class IFGForm(FdbForm):
|
class IFGForm(BaseApplicationForm, CommonOrderMixin):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = IFG
|
model = IFG
|
||||||
fields = ['cost', 'url', 'notes']
|
fields = ['cost', 'url', 'notes']
|
||||||
|
|
@ -174,7 +242,7 @@ class CheckForm(FdbForm):
|
||||||
# NUTZUNGSBEDINGUNGEN))
|
# NUTZUNGSBEDINGUNGEN))
|
||||||
|
|
||||||
|
|
||||||
class LiteratureForm(CheckForm):
|
class LiteratureForm(BaseApplicationForm, CommonOrderMixin):
|
||||||
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_LITERATURSTIPENDIUM
|
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_LITERATURSTIPENDIUM
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
@ -192,7 +260,7 @@ ADULT_CHOICES = {'TRUE': mark_safe('Ich bin volljährig.'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class EmailForm(CheckForm):
|
class EmailForm(BaseApplicationForm, CommonOrderMixin):
|
||||||
|
|
||||||
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_EMAIL_SERVICE
|
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_EMAIL_SERVICE
|
||||||
|
|
||||||
|
|
@ -216,7 +284,7 @@ class EmailForm(CheckForm):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BusinessCardForm(CheckForm):
|
class BusinessCardForm(BaseApplicationForm, CommonOrderMixin):
|
||||||
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_VISITENKARTEN
|
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_VISITENKARTEN
|
||||||
# this is the code, to change required to false if needed
|
# this is the code, to change required to false if needed
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
@ -232,7 +300,7 @@ class BusinessCardForm(CheckForm):
|
||||||
js = ('dropdown/js/businessCard.js',)
|
js = ('dropdown/js/businessCard.js',)
|
||||||
|
|
||||||
|
|
||||||
class ListForm(CheckForm):
|
class ListForm(BaseApplicationForm, CommonOrderMixin):
|
||||||
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_MAILINGLISTEN
|
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_MAILINGLISTEN
|
||||||
class Meta:
|
class Meta:
|
||||||
model = List
|
model = List
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue