一、表单和其生成
通过WTForms设置的表单,可以把HTML代码直接封装起来,直接通过它给的类就可以做表单,不需要自己写HTML代码来实现模板的功能。 通常把表单类用一个单独的python文件存起来。
二、表单处理流程
引用书上的流程图: 和模板可以自定义测试器和过滤器一样,表单也可以自定义验证器。
三、文件上传
文件上传主要应该考虑安全问题。
四、富文本编辑器
可以从网络上加载一些富文本编辑器。
五、视图和表单的关系
多表单对应多视图的情况
- 两个表单在一个视图的时候,可设置不同的submit字段,比如submit1和submit2,可以根据submit字段来确定是哪个表单的提交。
- 以上情况也可以在模板里面定向到两个不同的视图函数里面分别解决,那么每个就都可以用名为submit的字段而不用区分。
六、代码及注释
app.py
import os
import uuid
from flask import Flask, render_template, flash, redirect, url_for, request, send_from_directory, session
from flask_ckeditor import CKEditor, upload_success, upload_fail
from flask_dropzone import Dropzone
from flask_wtf.csrf import validate_csrf
from wtforms import ValidationError
from forms import LoginForm, FortyTwoForm, NewPostForm, UploadForm, MultiUploadForm, SigninForm, RegisterForm, SigninForm2, RegisterForm2, RichTextForm
app = Flask(__name__)
app.secret_key = os.getenv('SECRET_KEY', 'secret string')
app.jinja_env.trim_blocks = True
app.jinja_env.lstrip_blocks = True
app.config['UPLOAD_PATH'] = os.path.join(app.root_path, 'uploads')
if not os.path.exists(app.config['UPLOAD_PATH']):
os.makedirs(app.config['UPLOAD_PATH'])
app.config['ALLOWED_EXTENSIONS'] = ['png', 'jpg', 'jpeg', 'gif']
app.config['CKEDITOR_SERVE_LOCAL'] = True
app.config['CKEDITOR_FILE_UPLOADER'] = 'upload_for_ckeditor'
app.config['DROPZONE_ALLOWED_FILE_TYPE'] = 'image'
app.config['DROPZONE_MAX_FILE_SIZE'] = 3
app.config['DROPZONE_MAX_FILES'] = 30
ckeditor = CKEditor(app)
dropzone = Dropzone(app)
@app.route('/', methods=['GET', 'POST'])
def index():
return render_template('index.html')
@app.route('/html', methods=['GET', 'POST'])
def html():
form = LoginForm()
if request.method == 'POST':
username = request.form.get('username')
flash('Welcome home, %s!' % username)
return redirect(url_for('index'))
return render_template('pure_html.html')
@app.route('/basic', methods=['GET', 'POST'])
def basic():
form = LoginForm()
if form.validate_on_submit():
username = form.username.data
flash('Welcome home, %s!' % username)
return redirect(url_for('index'))
return render_template('basic.html', form=form)
@app.route('/bootstrap', methods=['GET', 'POST'])
def bootstrap():
form = LoginForm()
if form.validate_on_submit():
username = form.username.data
flash('Welcome home, %s!' % username)
return redirect(url_for('index'))
return render_template('bootstrap.html', form=form)
@app.route('/custom-validator', methods=['GET', 'POST'])
def custom_validator():
form = FortyTwoForm()
if form.validate_on_submit():
flash('Bingo!')
return redirect(url_for('index'))
return render_template('custom_validator.html', form=form)
@app.route('/uploads/<path:filename>')
def get_file(filename):
return send_from_directory(app.config['UPLOAD_PATH'], filename)
@app.route('/uploaded-images')
def show_images():
return render_template('uploaded.html')
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS']
def random_filename(filename):
ext = os.path.splitext(filename)[1]
new_filename = uuid.uuid4().hex + ext
return new_filename
@app.route('/upload', methods=['GET', 'POST'])
def upload():
form = UploadForm()
if form.validate_on_submit():
f = form.photo.data
filename = random_filename(f.filename)
f.save(os.path.join(app.config['UPLOAD_PATH'], filename))
flash('Upload success.')
session['filenames'] = [filename]
return redirect(url_for('show_images'))
return render_template('upload.html', form=form)
@app.route('/multi-upload', methods=['GET', 'POST'])
def multi_upload():
form = MultiUploadForm()
if request.method == 'POST':
filenames = []
try:
validate_csrf(form.csrf_token.data)
except ValidationError:
flash('CSRF token error.')
return redirect(url_for('multi_upload'))
photos = request.files.getlist('photo')
if not photos[0].filename:
flash('No selected file.')
return redirect(url_for('multi_upload'))
for f in photos:
if f and allowed_file(f.filename):
filename = random_filename(f.filename)
f.save(os.path.join(
app.config['UPLOAD_PATH'], filename
))
filenames.append(filename)
else:
flash('Invalid file type.')
return redirect(url_for('multi_upload'))
flash('Upload success.')
session['filenames'] = filenames
return redirect(url_for('show_images'))
return render_template('upload.html', form=form)
@app.route('/dropzone-upload', methods=['GET', 'POST'])
def dropzone_upload():
if request.method == 'POST':
if 'file' not in request.files:
return 'This field is required.', 400
f = request.files.get('file')
if f and allowed_file(f.filename):
filename = random_filename(f.filename)
f.save(os.path.join(
app.config['UPLOAD_PATH'], filename
))
else:
return 'Invalid file type.', 400
return render_template('dropzone.html')
@app.route('/two-submits', methods=['GET', 'POST'])
def two_submits():
form = NewPostForm()
if form.validate_on_submit():
if form.save.data:
flash('You click the "Save" button.')
elif form.publish.data:
flash('You click the "Publish" button.')
return redirect(url_for('index'))
return render_template('2submit.html', form=form)
@app.route('/multi-form', methods=['GET', 'POST'])
def multi_form():
signin_form = SigninForm()
register_form = RegisterForm()
if signin_form.submit1.data and signin_form.validate():
username = signin_form.username.data
flash('%s, you just submit the Signin Form.' % username)
return redirect(url_for('index'))
if register_form.submit2.data and register_form.validate():
username = register_form.username.data
flash('%s, you just submit the Register Form.' % username)
return redirect(url_for('index'))
return render_template('2form.html', signin_form=signin_form, register_form=register_form)
@app.route('/multi-form-multi-view')
def multi_form_multi_view():
signin_form = SigninForm2()
register_form = RegisterForm2()
return render_template('2form2view.html', signin_form=signin_form, register_form=register_form)
@app.route('/handle-signin', methods=['POST'])
def handle_signin():
signin_form = SigninForm2()
register_form = RegisterForm2()
if signin_form.validate_on_submit():
username = signin_form.username.data
flash('%s, you just submit the Signin Form.' % username)
return redirect(url_for('index'))
return render_template('2form2view.html', signin_form=signin_form, register_form=register_form)
@app.route('/handle-register', methods=['POST'])
def handle_register():
signin_form = SigninForm2()
register_form = RegisterForm2()
if register_form.validate_on_submit():
username = register_form.username.data
flash('%s, you just submit the Register Form.' % username)
return redirect(url_for('index'))
return render_template('2form2view.html', signin_form=signin_form, register_form=register_form)
@app.route('/ckeditor', methods=['GET', 'POST'])
def integrate_ckeditor():
form = RichTextForm()
if form.validate_on_submit():
title = form.title.data
body = form.body.data
flash('Your post is published!')
return render_template('post.html', title=title, body=body)
return render_template('ckeditor.html', form=form)
@app.route('/upload-ck', methods=['POST'])
def upload_for_ckeditor():
f = request.files.get('upload')
if not allowed_file(f.filename):
return upload_fail('Image only!')
f.save(os.path.join(app.config['UPLOAD_PATH'], f.filename))
url = url_for('get_file', filename=f.filename)
return upload_success(url, f.filename)
forms.py
from flask_ckeditor import CKEditorField
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileRequired, FileAllowed
from wtforms import StringField, PasswordField, BooleanField, IntegerField, \
TextAreaField, SubmitField, MultipleFileField
from wtforms.validators import DataRequired, Length, ValidationError, Email
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired(), Length(8, 128)])
remember = BooleanField('Remember me')
submit = SubmitField('Log in')
class FortyTwoForm(FlaskForm):
answer = IntegerField('The Number')
submit = SubmitField()
def validate_answer(form, field):
if field.data != 42:
raise ValidationError('Must be 42.')
class UploadForm(FlaskForm):
photo = FileField('Upload Image', validators=[FileRequired(), FileAllowed(['jpg', 'jpeg', 'png', 'gif'])])
submit = SubmitField()
class MultiUploadForm(FlaskForm):
photo = MultipleFileField('Upload Image', validators=[DataRequired()])
submit = SubmitField()
class NewPostForm(FlaskForm):
title = StringField('Title', validators=[DataRequired(), Length(1, 50)])
body = TextAreaField('Body', validators=[DataRequired()])
save = SubmitField('Save')
publish = SubmitField('Publish')
class SigninForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(1, 20)])
password = PasswordField('Password', validators=[DataRequired(), Length(8, 128)])
submit1 = SubmitField('Sign in')
class RegisterForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(1, 20)])
email = StringField('Email', validators=[DataRequired(), Email(), Length(1, 254)])
password = PasswordField('Password', validators=[DataRequired(), Length(8, 128)])
submit2 = SubmitField('Register')
class SigninForm2(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(1, 24)])
password = PasswordField('Password', validators=[DataRequired(), Length(8, 128)])
submit = SubmitField()
class RegisterForm2(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(1, 24)])
email = StringField('Email', validators=[DataRequired(), Email(), Length(1, 254)])
password = PasswordField('Password', validators=[DataRequired(), Length(8, 128)])
submit = SubmitField()
class RichTextForm(FlaskForm):
title = StringField('Title', validators=[DataRequired(), Length(1, 50)])
body = CKEditorField('Body', validators=[DataRequired()])
submit = SubmitField('Publish')
|