159 lines
4.8 KiB
Python
159 lines
4.8 KiB
Python
import warnings
|
|
|
|
from flask import current_app, request
|
|
from flask import session
|
|
from jinja2 import Markup
|
|
from werkzeug.datastructures import CombinedMultiDict, ImmutableMultiDict
|
|
from werkzeug.utils import cached_property
|
|
from wtforms import Form
|
|
from wtforms.meta import DefaultMeta
|
|
from wtforms.widgets import HiddenInput
|
|
|
|
from ._compat import FlaskWTFDeprecationWarning, string_types, text_type
|
|
from .csrf import _FlaskFormCSRF
|
|
|
|
try:
|
|
from .i18n import translations
|
|
except ImportError:
|
|
translations = None # babel not installed
|
|
|
|
|
|
SUBMIT_METHODS = set(('POST', 'PUT', 'PATCH', 'DELETE'))
|
|
_Auto = object()
|
|
|
|
|
|
class FlaskForm(Form):
|
|
"""Flask-specific subclass of WTForms :class:`~wtforms.form.Form`.
|
|
|
|
If ``formdata`` is not specified, this will use :attr:`flask.request.form`
|
|
and :attr:`flask.request.files`. Explicitly pass ``formdata=None`` to
|
|
prevent this.
|
|
"""
|
|
|
|
class Meta(DefaultMeta):
|
|
csrf_class = _FlaskFormCSRF
|
|
csrf_context = session # not used, provided for custom csrf_class
|
|
|
|
@cached_property
|
|
def csrf(self):
|
|
return current_app.config.get('WTF_CSRF_ENABLED', True)
|
|
|
|
@cached_property
|
|
def csrf_secret(self):
|
|
return current_app.config.get(
|
|
'WTF_CSRF_SECRET_KEY', current_app.secret_key
|
|
)
|
|
|
|
@cached_property
|
|
def csrf_field_name(self):
|
|
return current_app.config.get('WTF_CSRF_FIELD_NAME', 'csrf_token')
|
|
|
|
@cached_property
|
|
def csrf_time_limit(self):
|
|
return current_app.config.get('WTF_CSRF_TIME_LIMIT', 3600)
|
|
|
|
def wrap_formdata(self, form, formdata):
|
|
if formdata is _Auto:
|
|
if _is_submitted():
|
|
if request.files:
|
|
return CombinedMultiDict((
|
|
request.files, request.form
|
|
))
|
|
elif request.form:
|
|
return request.form
|
|
elif request.get_json():
|
|
return ImmutableMultiDict(request.get_json())
|
|
|
|
return None
|
|
|
|
return formdata
|
|
|
|
def get_translations(self, form):
|
|
if not current_app.config.get('WTF_I18N_ENABLED', True):
|
|
return None
|
|
|
|
return translations
|
|
|
|
def __init__(self, formdata=_Auto, **kwargs):
|
|
csrf_enabled = kwargs.pop('csrf_enabled', None)
|
|
|
|
if csrf_enabled is not None:
|
|
warnings.warn(FlaskWTFDeprecationWarning(
|
|
'"csrf_enabled" is deprecated and will be removed in 1.0. '
|
|
'Set "meta.csrf" instead.'
|
|
), stacklevel=3)
|
|
kwargs['meta'] = kwargs.get('meta') or {}
|
|
kwargs['meta'].setdefault('csrf', csrf_enabled)
|
|
|
|
super(FlaskForm, self).__init__(formdata=formdata, **kwargs)
|
|
|
|
def is_submitted(self):
|
|
"""Consider the form submitted if there is an active request and
|
|
the method is ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
|
|
"""
|
|
|
|
return _is_submitted()
|
|
|
|
def validate_on_submit(self):
|
|
"""Call :meth:`validate` only if the form is submitted.
|
|
This is a shortcut for ``form.is_submitted() and form.validate()``.
|
|
"""
|
|
return self.is_submitted() and self.validate()
|
|
|
|
def hidden_tag(self, *fields):
|
|
"""Render the form's hidden fields in one call.
|
|
|
|
A field is considered hidden if it uses the
|
|
:class:`~wtforms.widgets.HiddenInput` widget.
|
|
|
|
If ``fields`` are given, only render the given fields that
|
|
are hidden. If a string is passed, render the field with that
|
|
name if it exists.
|
|
|
|
.. versionchanged:: 0.13
|
|
|
|
No longer wraps inputs in hidden div.
|
|
This is valid HTML 5.
|
|
|
|
.. versionchanged:: 0.13
|
|
|
|
Skip passed fields that aren't hidden.
|
|
Skip passed names that don't exist.
|
|
"""
|
|
|
|
def hidden_fields(fields):
|
|
for f in fields:
|
|
if isinstance(f, string_types):
|
|
f = getattr(self, f, None)
|
|
|
|
if f is None or not isinstance(f.widget, HiddenInput):
|
|
continue
|
|
|
|
yield f
|
|
|
|
return Markup(
|
|
u'\n'.join(text_type(f) for f in hidden_fields(fields or self))
|
|
)
|
|
|
|
|
|
def _is_submitted():
|
|
"""Consider the form submitted if there is an active request and
|
|
the method is ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
|
|
"""
|
|
|
|
return bool(request) and request.method in SUBMIT_METHODS
|
|
|
|
|
|
class Form(FlaskForm):
|
|
"""
|
|
.. deprecated:: 0.13
|
|
Renamed to :class:`~flask_wtf.FlaskForm`.
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
warnings.warn(FlaskWTFDeprecationWarning(
|
|
'"flask_wtf.Form" has been renamed to "FlaskForm" '
|
|
'and will be removed in 1.0.'
|
|
), stacklevel=3)
|
|
super(Form, self).__init__(*args, **kwargs)
|