or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

csrf-protection.mdfile-upload.mdform-handling.mdindex.mdrecaptcha.md

form-handling.mddocs/

0

# Form Handling

1

2

Flask-integrated form processing with automatic request data binding, validation, and CSRF token management. Flask-WTF extends WTForms with Flask-specific features for seamless web form handling.

3

4

## Capabilities

5

6

### FlaskForm Class

7

8

Enhanced WTForms Form class with Flask integration, providing automatic data binding from Flask request context and built-in CSRF protection.

9

10

```python { .api }

11

class FlaskForm(Form):

12

def __init__(self, formdata=_Auto, **kwargs):

13

"""

14

Initialize Flask-integrated form.

15

16

Args:

17

formdata: Form data source (_Auto for automatic Flask request binding,

18

None to disable, or custom MultiDict)

19

**kwargs: Additional WTForms Form arguments

20

"""

21

22

def is_submitted(self) -> bool:

23

"""

24

Check if form was submitted via POST, PUT, PATCH, or DELETE request.

25

26

Returns:

27

True if current request method indicates form submission

28

"""

29

30

def validate_on_submit(self, extra_validators=None) -> bool:

31

"""

32

Validate form only if it was submitted.

33

Shortcut for: form.is_submitted() and form.validate()

34

35

Args:

36

extra_validators: Additional validators to run

37

38

Returns:

39

True if form was submitted and validation passed

40

"""

41

42

def hidden_tag(self, *fields) -> Markup:

43

"""

44

Render form's hidden fields including CSRF token.

45

46

Args:

47

*fields: Specific fields to render (renders all hidden fields if none specified)

48

49

Returns:

50

HTML markup for hidden fields

51

"""

52

```

53

54

### Form Base Class

55

56

Re-exported WTForms Form class for cases where Flask integration is not needed.

57

58

```python { .api }

59

class Form:

60

"""

61

Base WTForms Form class without Flask integration.

62

Use FlaskForm for Flask applications.

63

"""

64

```

65

66

## Usage Examples

67

68

### Basic Form Creation

69

70

```python

71

from flask_wtf import FlaskForm

72

from wtforms import StringField, TextAreaField, SubmitField

73

from wtforms.validators import DataRequired, Length

74

75

class ContactForm(FlaskForm):

76

name = StringField('Name', validators=[DataRequired(), Length(min=2, max=50)])

77

email = StringField('Email', validators=[DataRequired(), Email()])

78

message = TextAreaField('Message', validators=[DataRequired(), Length(min=10)])

79

submit = SubmitField('Send Message')

80

```

81

82

### Automatic Data Binding

83

84

FlaskForm automatically binds to Flask request data:

85

86

```python

87

@app.route('/contact', methods=['GET', 'POST'])

88

def contact():

89

form = ContactForm() # Automatically binds to request.form, request.files, or request.json

90

91

if form.validate_on_submit():

92

# Form was submitted and validation passed

93

name = form.name.data

94

email = form.email.data

95

message = form.message.data

96

97

# Process form data

98

send_email(name, email, message)

99

flash('Message sent successfully!')

100

return redirect(url_for('contact'))

101

102

# GET request or validation failed

103

return render_template('contact.html', form=form)

104

```

105

106

### Custom Data Sources

107

108

```python

109

from werkzeug.datastructures import MultiDict

110

111

# Disable automatic binding

112

form = ContactForm(formdata=None)

113

114

# Custom data source

115

custom_data = MultiDict([('name', 'John'), ('email', 'john@example.com')])

116

form = ContactForm(formdata=custom_data)

117

118

# JSON data handling (automatic for requests with content-type: application/json)

119

@app.route('/api/contact', methods=['POST'])

120

def api_contact():

121

form = ContactForm() # Automatically binds to request.json if content-type is JSON

122

if form.validate():

123

return {'status': 'success'}

124

return {'status': 'error', 'errors': form.errors}, 400

125

```

126

127

### Template Integration

128

129

```html

130

<!-- Basic form template -->

131

<!DOCTYPE html>

132

<html>

133

<head>

134

<title>Contact Form</title>

135

</head>

136

<body>

137

<form method="POST">

138

{{ form.hidden_tag() }} <!-- Includes CSRF token and other hidden fields -->

139

140

<div>

141

{{ form.name.label(class="form-label") }}

142

{{ form.name(class="form-control") }}

143

{% for error in form.name.errors %}

144

<div class="error">{{ error }}</div>

145

{% endfor %}

146

</div>

147

148

<div>

149

{{ form.email.label(class="form-label") }}

150

{{ form.email(class="form-control") }}

151

{% for error in form.email.errors %}

152

<div class="error">{{ error }}</div>

153

{% endfor %}

154

</div>

155

156

<div>

157

{{ form.message.label(class="form-label") }}

158

{{ form.message(class="form-control", rows="4") }}

159

{% for error in form.message.errors %}

160

<div class="error">{{ error }}</div>

161

{% endfor %}

162

</div>

163

164

{{ form.submit(class="btn btn-primary") }}

165

</form>

166

</body>

167

</html>

168

```

169

170

### CSRF Token Management

171

172

```python

173

# CSRF is automatically enabled for FlaskForm

174

class MyForm(FlaskForm):

175

name = StringField('Name')

176

177

# CSRF can be disabled for specific forms

178

class Meta:

179

csrf = False

180

181

# Custom CSRF configuration per form

182

class SecureForm(FlaskForm):

183

data = StringField('Data')

184

185

class Meta:

186

csrf = True

187

csrf_secret = 'form-specific-secret'

188

csrf_time_limit = 1800 # 30 minutes

189

csrf_field_name = 'security_token'

190

```

191

192

### Multi-part Forms (File Uploads)

193

194

```python

195

from flask_wtf.file import FileField

196

from wtforms import StringField

197

198

class UploadForm(FlaskForm):

199

title = StringField('Title', validators=[DataRequired()])

200

file = FileField('File')

201

202

@app.route('/upload', methods=['GET', 'POST'])

203

def upload():

204

form = UploadForm() # Automatically handles request.files

205

206

if form.validate_on_submit():

207

title = form.title.data

208

file = form.file.data # Werkzeug FileStorage object

209

210

if file:

211

filename = secure_filename(file.filename)

212

file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))

213

214

return redirect(url_for('success'))

215

216

return render_template('upload.html', form=form)

217

```

218

219

### Form Validation Patterns

220

221

```python

222

# Custom validation

223

class RegistrationForm(FlaskForm):

224

username = StringField('Username', validators=[DataRequired()])

225

password = PasswordField('Password', validators=[DataRequired()])

226

confirm = PasswordField('Confirm Password', validators=[DataRequired()])

227

228

def validate_username(self, field):

229

"""Custom field validator (method naming convention)"""

230

if User.query.filter_by(username=field.data).first():

231

raise ValidationError('Username already exists.')

232

233

def validate(self, extra_validators=None):

234

"""Custom form-level validation"""

235

if not super().validate(extra_validators):

236

return False

237

238

if self.password.data != self.confirm.data:

239

self.confirm.errors.append('Passwords must match.')

240

return False

241

242

return True

243

244

# Using with extra validators

245

@app.route('/register', methods=['GET', 'POST'])

246

def register():

247

form = RegistrationForm()

248

249

# Additional runtime validators

250

extra_validators = {'username': [Length(min=3, max=20)]}

251

252

if form.validate_on_submit(extra_validators):

253

# Registration logic

254

return redirect(url_for('login'))

255

256

return render_template('register.html', form=form)

257

```

258

259

## Configuration

260

261

### Form Meta Configuration

262

263

```python

264

from wtforms.meta import DefaultMeta

265

266

class CustomForm(FlaskForm):

267

class Meta(DefaultMeta):

268

# CSRF settings

269

csrf = True

270

csrf_secret = 'custom-csrf-key'

271

csrf_field_name = 'csrf_token'

272

csrf_time_limit = 3600

273

274

# Localization

275

locales = ['en_US', 'en']

276

277

def get_translations(self, form):

278

# Custom translation logic

279

return super().get_translations(form)

280

```

281

282

### Request Data Handling

283

284

Flask-WTF automatically handles different request data sources:

285

286

1. **Form Data**: `request.form` (application/x-www-form-urlencoded)

287

2. **File Data**: Combined `request.files` and `request.form` (multipart/form-data)

288

3. **JSON Data**: `request.json` (application/json)

289

290

The data source is automatically selected based on the request content type and HTTP method.