Add FileRequired validator?
Opened this issue · 2 comments
mathause commented
The DataRequired
does not properly work for FileField, because it returns a starlette.datastructures.UploadFile
and is thus not empty (even if no file is uploaded). So it might be worthwhile to add a FileRequired
validator. You can repro this with:
from jinja2 import Template
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.sessions import SessionMiddleware
from starlette.responses import PlainTextResponse, HTMLResponse
from starlette_wtf import StarletteForm, CSRFProtectMiddleware, csrf_protect
from wtforms import FileField
from wtforms.validators import DataRequired
class MyForm(StarletteForm):
photo = FileField(validators=[DataRequired()])
template = Template('''
<html>
<body>
<form method="post" novalidate enctype="multipart/form-data">
{{ form.csrf_token }}
<div>
{{ form.photo() }}
{% if form.photo.errors -%}
<span>{{ form.photo.errors[0] }}</span>
{%- endif %}
</div>
<button type="submit">Submit</button>
</form>
</body>
</html>
''')
app = Starlette(middleware=[
Middleware(SessionMiddleware, secret_key='***REPLACEME1***'),
Middleware(CSRFProtectMiddleware, csrf_secret='***REPLACEME2***')
])
@app.route('/', methods=['GET', 'POST'])
@csrf_protect
async def index(request):
"""GET|POST /: form handler
"""
form = await MyForm.from_formdata(request)
if await form.validate_on_submit():
print(form.photo.data)
# print(form.photo.size)
return PlainTextResponse(f'Data Size: {form.photo.data.size}')
html = template.render(form=form)
return HTMLResponse(html)
mathause commented
The validator could look like this (modeled after the DataRequired
validator):
from wtforms.validators import StopValidation
class FileRequired:
def __init__(self, message=None):
self.message = message
self.field_flags = {"required": True}
def __call__(self, form, field):
if field.data and field.data.size > 0:
return
if self.message is None:
message = field.gettext("This field is required.")
else:
message = self.message
field.errors[:] = []
raise StopValidation(message)
class MyForm(StarletteForm):
photo = FileField(validators=[FileRequired()])
amorey commented
Thanks, this looks very useful! Would you be interested in submitting a PR to add file validators to starlette-wtf like flask-wtf's: https://github.com/wtforms/flask-wtf/blob/main/src/flask_wtf/file.py?