Search code examples
pythonflaskflask-sqlalchemyflask-wtforms

How to collect WTForms data through loop without explicitly stating the name of each field?


I am following CoreyMSchafer's Flask-blog tutorial. Here, I can create, update and delete posts using WTForms and SQLAlchemy. However, to do that, I have to explicitly mention the name of the form fields. For example, to update a post (assuming a post only has title and content):

@app.route("/post/<int:post_id>/update", methods=['GET', 'POST'])
def update_post(post_id):
    post = Post.query.get_or_404(post_id)
    form = PostForm()
    if form.validate_on_submit():
        post.title = form.title.data
        post.content = form.content.data
        db.session.commit()
        return redirect(url_for('post', post_id=post.id))

    elif request.method == 'GET':
        form.title.data = post.title
        form.content.data = post.content
        return render_template('new_post.html', form=form)

where the model is

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title= db.Column(db.String, nullable=False)
    content= db.Column(db.String)

and the WTForm is

class SystemForm(FlaskForm):
    title= StringField('Title', validators=[DataRequired()])
    content= StringField('Content')
    submit = SubmitField('Submit')

However, what if my form has hundreds of fields (such as a star which has hundreds of parameters and let's say we want a post to store all these parameters for a star)!? Do I have to explicitly mention them in models, routes, forms - everywhere? or is it possible to create the model/form with all the fields and then somehow loop through the fields to avoid typing each of them in routes?


Solution

  • form.data returns all of the form fields and its values in dict object where dict keys is field name.

    But: you may use form.populate_obj(post) method for update an object (field names in form and model must be equal)

    if form.validate_on_submit():
        form.populate_obj(post)
        db.session.commit()
        return redirect(url_for('post', post_id=post.id))