{"library":"flask-wtf","title":"Flask-WTF","description":"Flask-WTF is a Flask extension (current version 1.2.2) that simplifies form handling by integrating the WTForms library. It provides robust features such as CSRF protection, form validation, file upload support, and reCAPTCHA integration. Maintained by the Pallets organization, it generally sees regular updates, though specific release cadences can vary.","status":"active","version":"1.2.2","language":"en","source_language":"en","source_url":"https://github.com/pallets-eco/flask-wtf","tags":["Flask","WTForms","forms","validation","CSRF"],"install":[{"cmd":"pip install Flask-WTF","lang":"bash","label":"Install stable version"}],"dependencies":[{"reason":"Core web framework integration.","package":"Flask","optional":false},{"reason":"Underlying form validation and rendering library.","package":"WTForms","optional":false},{"reason":"Used for secure token signing, including CSRF tokens.","package":"itsdangerous","optional":false},{"reason":"Provides an Email validator, required for 'email' extra.","package":"email-validator","optional":true}],"imports":[{"note":"`Form` was deprecated and removed in favor of `FlaskForm` in versions >= 1.0.0. `FlaskForm` is a Flask-specific subclass of WTForms' `Form` that includes CSRF protection.","wrong":"from flask_wtf import Form","symbol":"FlaskForm","correct":"from flask_wtf import FlaskForm"},{"note":"Since version 0.9.0, fields (e.g., `StringField`, `PasswordField`) must be imported directly from `wtforms`, not `flask_wtf`.","wrong":"from flask_wtf import StringField","symbol":"StringField","correct":"from wtforms import StringField"},{"note":"Validators are imported directly from `wtforms.validators`.","symbol":"DataRequired","correct":"from wtforms.validators import DataRequired"}],"quickstart":{"code":"import os\nfrom flask import Flask, render_template, request, redirect, url_for\nfrom flask_wtf import FlaskForm\nfrom wtforms import StringField, SubmitField\nfrom wtforms.validators import DataRequired\n\napp = Flask(__name__)\napp.config['SECRET_KEY'] = os.environ.get('FLASK_SECRET_KEY', 'a_very_secret_key_for_dev')\n\nclass MyForm(FlaskForm):\n    name = StringField('Name', validators=[DataRequired()])\n    submit = SubmitField('Submit')\n\n@app.route('/', methods=['GET', 'POST'])\ndef index():\n    form = MyForm()\n    if form.validate_on_submit():\n        name = form.name.data\n        print(f\"Form submitted with name: {name}\")\n        # In a real app, save 'name' to a database, etc.\n        return redirect(url_for('success', name=name))\n    return render_template('index.html', form=form)\n\n@app.route('/success/<name>')\ndef success(name):\n    return f'Hello, {name}! Your form was submitted successfully.'\n\nif __name__ == '__main__':\n    # Example template content (save as templates/index.html)\n    # <form method=\"POST\" action=\"/\">\n    #     {{ form.hidden_tag() }}\n    #     <p>\n    #         {{ form.name.label }}\n    #         {{ form.name(size=30) }}\n    #         {% if form.name.errors %}\n    #             <ul class=\"errors\">\n    #                 {% for error in form.name.errors %}\n    #                     <li>{{ error }}</li>\n    #                 {% endfor %}\n    #             </ul>\n    #         {% endif %}\n    #     </p>\n    #     <p>{{ form.submit() }}</p>\n    # </form>\n    app.run(debug=True)","lang":"python","description":"This quickstart demonstrates a basic Flask application using Flask-WTF to create, render, and validate a simple form. It includes defining a `FlaskForm` subclass, rendering it in an HTML template, and processing its submission with `validate_on_submit()`."},"warnings":[{"fix":"Change `from flask_wtf import Form` to `from flask_wtf import FlaskForm`.","message":"The base form class `flask_wtf.Form` was deprecated and removed. Applications should now import and inherit from `flask_wtf.FlaskForm`.","severity":"breaking","affected_versions":">=1.0.0 (Introduced in 0.9.0 with `FlaskForm`, `Form` removed in 1.0.0)"},{"fix":"Import fields directly from `wtforms` (e.g., `from wtforms import StringField`).","message":"WTForms fields (e.g., `StringField`, `IntegerField`) are no longer imported from `flask_wtf`.","severity":"breaking","affected_versions":">=0.9.0"},{"fix":"Set a secure `SECRET_KEY` in your Flask app's configuration, ideally loaded from an environment variable: `app.config['SECRET_KEY'] = os.environ.get('FLASK_SECRET_KEY', 'fallback-secret-key')`.","message":"CSRF protection requires `app.config['SECRET_KEY']` to be set to a strong, random string. Without it, Flask-WTF's CSRF features will not work, or forms will fail validation.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Add `enctype=\"multipart/form-data\"` to your HTML `<form>` tag when handling file uploads.","message":"For forms that include `FileField` for file uploads, the HTML form tag must include `enctype=\"multipart/form-data\"` to ensure file data is correctly sent to the server.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Use `form.validate_on_submit()` for handling form submissions (typically POST). For displaying an initial form (GET), simply create the form instance without calling `validate_on_submit()`.","message":"The `form.validate_on_submit()` method only validates if the request method is POST, PUT, PATCH, or DELETE. It will always return `False` for GET requests, which can be a common source of confusion.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-05T00:00:00.000Z","next_check":"2026-07-04T00:00:00.000Z"}