diff --git a/c3l_membership/forms.py b/c3l_membership/forms.py --- a/c3l_membership/forms.py +++ b/c3l_membership/forms.py @@ -2,10 +2,55 @@ from datetime import date from flask_babel import lazy_gettext from flask_wtf import FlaskForm -from wtforms import BooleanField, DateField, RadioField, StringField, SubmitField +from wtforms import ( + BooleanField, + DateField, + RadioField, + StringField, + SubmitField, + ValidationError, +) from wtforms.validators import Email, InputRequired, Length, Optional +class NotEqualTo: + """ + Compares the values of two fields. + :param fieldname: + The name of the other field to compare to. + :param message: + Error message to raise in case of a validation error. Can be + interpolated with `%(other_label)s` and `%(other_name)s` to provide a + more helpful error. + """ + + def __init__(self, fieldname, message=None): + self.fieldname = fieldname + self.message = message + + def __call__(self, form, field): + try: + other = form[self.fieldname] + except KeyError as exc: + raise ValidationError( + field.gettext("Invalid field name '%s'.") % self.fieldname + ) from exc + if field.data != other.data: + return + + d = { + "other_label": hasattr(other, "label") + and other.label.text + or self.fieldname, + "other_name": self.fieldname, + } + message = self.message + if message is None: + message = field.gettext("Field must not be equal to %(other_name)s.") + + raise ValidationError(message % d) + + class MembershipForm(FlaskForm): username = StringField( @@ -50,6 +95,15 @@ class MembershipForm(FlaskForm): lazy_gettext( "I am a student and would like to have the reduced membership fees." ), + validators=[ + Optional(), + NotEqualTo( + "starving", + lazy_gettext( + "Student and Starving Hacker are mutually exclusive! Please select only one of them." + ), + ), + ], ) starving = BooleanField(