Skip to content

Instantly share code, notes, and snippets.

@doobeh
Created March 4, 2020 22:52
Show Gist options
  • Save doobeh/aa0e07892922cfa32123a6926c7720a5 to your computer and use it in GitHub Desktop.
Save doobeh/aa0e07892922cfa32123a6926c7720a5 to your computer and use it in GitHub Desktop.
nested form object population
<form method="post" action="">
{{ form.hidden_tag() }}
{{ form.id }}
{{ form.name }}
{% for entry in form.players %}
{{ entry() }}
{% endfor %}
<input type="submit"/>
</form>
from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, FormField, FieldList, IntegerField
app = Flask(__name__)
app.secret_key = "SCRATCH"
# Our pretend object data (I'd use SQLAlchemy to do this in reality).
class TeamObj(object):
id = 0
name = ""
class PlayerObj(object):
id = 0
name = ""
pa = PlayerObj()
pa.name = "Carol"
pa.id = 1
pb = PlayerObj()
pb.name = "Jane"
pb.id = 2
# SQLAlchemy might be: team = Team.query.get(2)
team = TeamObj()
team.name = "Bombers"
team.id = 2
team.players = [pa, pb]
class PlayerForm(FlaskForm):
name = StringField("Player Name")
id = IntegerField("id")
class TeamForm(FlaskForm):
id = IntegerField("Team ID")
name = StringField("Team Name")
players = FieldList(
FormField(PlayerForm, default=PlayerObj), min_entries=7, max_entries=7
)
@app.route("/", methods=["post", "get"])
def home():
results = []
form = TeamForm(obj=team)
if form.validate_on_submit():
results = []
for idx, data in enumerate(form.players.data):
results.append(data)
results.append(form.name.data)
return render_template("results.html", results=results)
print(form.errors)
return render_template("home.html", form=form)
if __name__ == "__main__":
app.run(debug=True)
<ul>
{% for line in results %}
<li>{{ line }}</li>
{% endfor %}
</ul>
@ethagnawl
Copy link

This is useful. Thanks for putting it together so quickly!

I just now worked out what I was trying to do earlier and it might make a useful addition to your example. Either way, I'm curious to know what you think and if there are any downsides to this approach. (Mass assignment is one.)

Brought over into the context of your example, the most basic version would look something like the following:

for player in form.players:
    player_obj = PlayerObj()
    for k, v in player_form.data.items():
        player_form.populate_obj(player_obj, k)
        results.append(player_obj)
        # db.session.add(player_obj)

@doobeh
Copy link
Author

doobeh commented Mar 5, 2020

Yeah, I think I end up in the same place pretty much-- feels like there's improvements to be had though, will be pondering it for a while 😄 🤔

if form.validate_on_submit():
    team = Team(name=form.name.data)
    for player_data in form.players.data:
        # maybe some validation here to trim any 'default' entries.
        if len(player_data["name"]):
            p = Player(
                name=player_data["name"],
                age=player_data["age"],
                team=team
            )
    db.session.add(team)
    db.session.commit()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment