wtf form python google-app-engine validation wtforms

python - ¿Cómo hacer un campo condicionalmente opcional en WTForms?



flask form post (3)

Encontré esta pregunta útil y, según la respuesta de @dcrosta, creé otro validador que es opcional. El beneficio es que puede combinarlo con otros validadores de wtforms. Aquí está mi validador opcional que comprueba otro campo. Debido a que necesitaba verificar el valor del otro campo contra un cierto valor, agregué un cheque personalizado para el valor:

class OptionalIfFieldEqualTo(wtf.validators.Optional): # a validator which makes a field optional if # another field has a desired value def __init__(self, other_field_name, value, *args, **kwargs): self.other_field_name = other_field_name self.value = value super(OptionalIfFieldEqualTo, self).__init__(*args, **kwargs) def __call__(self, form, field): other_field = form._fields.get(self.other_field_name) if other_field is None: raise Exception(''no field named "%s" in form'' % self.other_field_name) if other_field.data == self.value: super(OptionalIfFieldEqualTo, self).__call__(form, field)

La validación de mi formulario está funcionando casi completa, solo tengo 2 casos No sé exactamente cómo resolverlo: 1) El campo de contraseña debería ser obligatorio, pero también tengo la posibilidad de iniciar sesión con una cuenta de Google o Facebook a través de OAuth y luego el nombre se rellena previamente, pero elimino el campo de contraseña completamente del formulario si hay un usuario (google) o un objeto de usuario de Facebook:

<tr><td> <br /> {% if user or current_user %} {% else %} <div class="labelform"> {% filter capitalize %}{% trans %}password{% endtrans %}{% endfilter %}: </div> </td><td> <div class="adinput">{{ form.password|safe }}{% trans %}Choose a password{% endtrans %}</div>{% endif %} </td></tr>

Entonces, para estos usuarios que ya han iniciado sesión y el campo de contraseña no tiene ningún significado, necesito algo de lógica para hacer que ese campo sea condicionalmente opcional. Estaba pensando que podría tener una variable para log_in + un método en mi clase de formulario como este:

class AdForm(Form): logged_in = False my_choices = [(''1'', _(''VEHICLES'')), (''2'', _(''Cars'')), (''3'', _(''Bicycles''))] name = TextField(_(''Name''), [validators.Required(message=_(''Name is required''))], widget=MyTextInput()) title = TextField(_(''title''), [validators.Required(message=_(''Subject is required''))], widget=MyTextInput()) text = TextAreaField(_(''Text''),[validators.Required(message=_(''Text is required''))], widget=MyTextArea()) phonenumber = TextField(_(''Phone number'')) phoneview = BooleanField(_(''Display phone number on site'')) price = TextField(_(''Price''),[validators.Regexp(''/d'', message=_(''This is not an integer number, please see the example and try again'')),validators.Optional()] ) password = PasswordField(_(''Password''),[validators.Optional()], widget=PasswordInput()) email = TextField(_(''Email''), [validators.Required(message=_(''Email is required'')), validators.Email(message=_(''Your email is invalid''))], widget=MyTextInput()) category = SelectField(choices = my_choices, default = ''1'') def validate_name(form, field): if len(field.data) > 50: raise ValidationError(_(''Name must be less than 50 characters'')) def validate_email(form, field): if len(field.data) > 60: raise ValidationError(_(''Email must be less than 60 characters'')) def validate_price(form, field): if len(field.data) > 8: raise ValidationError(_(''Price must be less than 9 integers'')) def validate_password(form, field): if not logged_in and not field: raise ValidationError(_(''Password is required''))

¿Funcionará la contraseña de validación anterior para lograr el efecto deseado? ¿Hay otra manera mejor? Otra forma en la que podría pensar es tener 2 clases de formulario diferentes y en la publicación http yo instanciate la clase de formulario debería ser:

def post(self): if not current_user: form = AdForm(self.request.params) if current_user: form = AdUserForm(self.request.params)

También necesito validación condicional para el campo de categoría, cuando se selecciona una determinada categoría, aparecen más opciones y éstas deberían tener validación solo para una determinada categoría base, por ejemplo. el usuario selecciona "Automóvil" y luego, a través de Ajax, puede elegir los datos de registro y el kilometraje del automóvil, y estos campos son obligatorios ya que se seleccionó la categoría Automóvil.

Entonces, podrían ser dos preguntas, pero ambos casos se relacionan con cómo puedo hacer que un campo sea "condicionalmente opcional" o "condicionalmente requerido".

Mi forma se ve así

Y para un usuario que haya iniciado la sesión, prefino el nombre, la dirección de correo electrónico y el campo de contraseña simplemente no se usa, por lo que el campo de contraseña no es "opcional" ni "requerido", necesitaría algo como "condicionalmente opcional" o "condicionalmente requerido . "

Gracias por cualquier respuesta o comentario.


La respuesta de @dcrosta es genial, pero creo que algunas cosas han cambiado en las formas de trabajo desde esta respuesta. La herencia de DataRequired agrega un atributo required al campo de formulario, por lo que nunca se llama al validador condicional. Hice un cambio menor en la clase de @dcrosta que funciona con wtforms 2.1. Esto solo field_flags para que la validación del navegador no se realice.

from wtforms.validators import DataRequired class RequiredIf(DataRequired): """Validator which makes a field required if another field is set and has a truthy value. Sources: - http://wtforms.simplecodes.com/docs/1.0.1/validators.html - http://.com/questions/8463209/how-to-make-a-field-conditionally-optional-in-wtforms """ field_flags = (''requiredif'',) def __init__(self, other_field_name, message=None, *args, **kwargs): self.other_field_name = other_field_name self.message = message def __call__(self, form, field): other_field = form[self.other_field_name] if other_field is None: raise Exception(''no field named "%s" in form'' % self.other_field_name) if bool(other_field.data): super(RequiredIf, self).__call__(form, field)

Una solución más ideal lograría realizar la validación en el navegador, como el comportamiento actual de DataRequired .


No estoy seguro de que esto se ajuste a sus necesidades, pero he usado un validador personalizado RequiredIf en campos anteriores, lo que hace que un campo sea obligatorio si otro campo tiene un valor en el formulario ... por ejemplo, en una fecha y hora En el escenario de zona horaria, puedo hacer que el campo de zona horaria se requiera para tener un valor si el usuario ha ingresado una fecha y hora.

class RequiredIf(Required): # a validator which makes a field required if # another field is set and has a truthy value def __init__(self, other_field_name, *args, **kwargs): self.other_field_name = other_field_name super(RequiredIf, self).__init__(*args, **kwargs) def __call__(self, form, field): other_field = form._fields.get(self.other_field_name) if other_field is None: raise Exception(''no field named "%s" in form'' % self.other_field_name) if bool(other_field.data): super(RequiredIf, self).__call__(form, field)

El constructor toma el nombre del otro campo que activa este campo, como:

class DateTimeForm(Form): datetime = TextField() timezone = SelectField(choices=..., validators=[RequiredIf(''datetime'')])

Este podría ser un buen punto de partida para implementar el tipo de lógica que necesita.