因为做“新增”和“更新”操作时的内容都是类似的,所以可以定义一个FlaskForm类然后在“新增”和“更新”模板中都使用该类进行渲染:
MovieForm(FlaskForm):
class MovieForm(FlaskForm):
tag_list = Tag.query.all()
title = StringField(
validators=[DataRequired('请输入片名')],
render_kw={
'class': "form-control",
'id': "title",
'placeholder': "请输入片名"
}
)
url = FileField(
validators=[DataRequired('请选择文件')],
)
info = TextAreaField(
validators=[DataRequired('请输入简介')],
render_kw={
'class': "form-control",
'rows': 10,
'placeholder':'请输入影片简介'
}
)
tag = SelectField(
label='影片标签',
validators=[DataRequired('请选择影片标签')],
coerce=int,
render_kw={
'class': "form-control",
}
)
submit = SubmitField(
'确定',
render_kw={
'class': "btn btn-primary"
}
)
“新增”模板(代码片段):
<form role="form" method="post" enctype="multipart/form-data">
<div class="box-body">
<div class="form-group">
<label for="title">影片片名</label>
{{form.title}}
</div>
<div class="form-group">
<label for="url">影片文件</label>
{{form.url}}
</div>
<div class="form-group">
<label for="info">影片介绍</label>
{{form.info}}
</div>
<div class="form-group">
<label for="tagid">影片标签</label>
{{form.tag}}
</div>
</div>
<div class="box-footer">
{{form.csrf_token}}
{{form.submit}}
</div>
</form>
“编辑”模板(代码片段):
<form role="form" method="post" enctype="multipart/form-data">
<div class="box-body">
<div class="form-group">
<label for="title">影片片名</label>
{{form.title(value=movie.title)}}
</div>
<div class="form-group">
<label for="url">影片文件</label>
{{form.url}}
</div>
<div class="form-group">
<label for="info">影片介绍</label>
{{form.info}}
</div>
<div class="form-group">
<label for="tagid">影片标签</label>
{{form.tag}}
</div>
</div>
<div class="box-footer">
{{form.csrf_token}}
{{form.submit}}
</div>
</form>
如果使用同一个MovieForm渲染模板时,会有一个问题,在“新增”模板中影片文件是必须上传的(required),但是在更新的时候,这一项并不应该是必须修改项,所以在做更新时应该去除掉required。
比较有效的方式是,再写一个类继承自MovieForm,然后子类覆盖掉父类的url属性:
EditMovieForm(MovieForm):
class EditMovieForm(MovieForm):
url = FileField()
渲染“编辑”页面时的form使用EditMovieForm对象
另外在做“更新”提交时,路由中的代码写法也要注意(代码片段):
@admin_blu.route('/movie/edit/<int:id>', methods=['GET','POST'])
@login_required
def movie_edit(id):
movie = Movie.query.get_or_404(id)
form = EditMovieForm()
form.tag.choices = [(x.id, x.name) for x in Tag.query.all()]
if request.method=='GET':
form.star.data = movie.star
form.info.data = movie.info
form.tag.data = movie.tag_id
if form.validate_on_submit():
data = form.data
movie.tag_id = data['tag']
movie.info = data['info']
movie.title = data['title']
if data['url']:
file_url = get_name(form.url.data.filename)
form.url.data.save(join(app.config['UPLOAD_DIR'], file_url))
movie.url = file_url
db.session.add(movie)
db.session.commit()
flash('更改{}电影成功'.format(movie.title))
return redirect(url_for('admin.movielist',page=1))
return render_template('admin/movieedit.html', movie=movie, form=form)
这段代码中有几个需要注意的点:
1. 以GET的方式进入编辑页面,编辑后点击确定按钮以POST的方式提交数据。无论是GET还是POST方式,都会触发movie_edit方法,并且方法都是从第一行开始执行。
2. 无论以哪种方式进行请求都涉及到了tag,tag是SelectField,必须提供choices。而且为了让choices能够及时反应出数据库中tag的变化,因此应该把choices的赋值放到路由中进行,保证每次请求看到的tag内容都是最新的。
3. 以GET和POST发起请求时,都会创建一个EditMovieForm对象。不同的是,GET请求时创建出的form对象是空的,没有任何内容,所以渲染模板时的数据除了以JinJa2的表达式提供外,有些数据需要以form.field.data的形式在路由中手动赋值填充到表单域中。无论以哪种方式,此时form表单中的内容都来自数据库中已保存的那个movie的信息。POST请求时创建出的form对象是有用户在点击“确定”按钮时表格中所填写的最新的数据的,所以绝对不能以form.field.data形式再去赋值了,而是应该用form.field.data中的内容对movie对象的相关属性进行赋值,赋值完毕后,movie对象拿到了表达中用户提交的最新数据,提交到数据库中进行保存完成数据的更新。