There are currently three related objects in my model (there are more, but only three relate to this problem). User, network and email. What I want to do is to have a specific set of networks and allow each user to have an email address on each network (they are a bit more complicated, but I cut them down to what I think is relevant).
class User(UserMixin, db.Model): """ The User object. """ __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True)
My opinion:
@main.route('/edit-profile', methods=['GET', 'POST']) @login_required def edit_profile(): form = EditProfileForm(obj=current_user) form.email.min_entries=Network.query.count() if form.validate_on_submit(): form.populate_obj(current_user) db.session.add(current_user) db.session.commit() flash("Your profile has been updated.") return redirect(url_for('.user', username=current_user.username)) return render_template('edit_profile.html', form=form)
And forms:
class EmailForm(Form): id = HiddenField('Id') address = StringField('Address', validators=[DataRequired(), Email()]) network = QuerySelectField(query_factory=get_networks) class EditProfileForm(Form): username = StringField('Username', validators=[Length(0, 64), Regexp('[A-Za-z0-9_\.\-]'), DataRequired()]) firstname = StringField('First name', validators=[Length(0, 64), DataRequired()]) lastname = StringField('Last name', validators=[Length(0, 64), DataRequired()]) email = ModelFieldList(FormField(EmailForm), model=Email) submit = SubmitField('Submit')
External HTML Form:
{% extends "base.html" %} {% import "bootstrap/wtf.html" as wtf %} {% block title %}Edit Profile{% endblock %} {% block page_content %} <div class="page-header"> <h1>Edit Your Profile</h1> </div> <div class="col-md-8"> {{ wtf.quick_form(form) }} </div> {% endblock %}
Here's what it looks like in Chrome and Firefox:

Therefore, I obviously am doing something wrong, because:
- Subform widgets are not like external forms and
- The subform is stored in the upper part of the external form.
Where did I go wrong with that? I did not try to use wtf.quick_form (), but could not make it look right manually. To do this, I replaced {{wtf.quick_form ()}} as follows:
<label>{{ form.username.label }}</label> {{ form.username }} <label>{{ form.firstname.label }}</label> {{ form.firstname }} <label>{{ form.lastname.label }}</label> {{ form.lastname }} <div data-toggle="fieldset" id="email-fieldset"> {{ form.email.label }} <table class="ui table"> <thead> <th>Network</th> <th>Address</th> <th> {{ form_button(url_for('main.add_email'), icon ('plus')) }} </th> </thead> <tbody> {% for e in form.email %} <tr data-toggle="fieldset-entry"> <td>{{ e.network }}</td> <td>{{ e.address }}</td> <td> {{ form_button(url_for('main.remove_email', id=loop.index), icon ('remove')) }} </td> </tr> {% endfor %} </tbody> </table> </div> {{ form.submit }}
When I do this, it displays in my browser below:

This has the power to be consistent, but this is not the look I want using flash bootstrap. I'm struggling to figure out which approach will lead me to where I want to go easier.
Decision
Changing the html form to this gave me the user interface elements that I was filming. The key was understanding that "class_" could be passed and would be displayed in the output html as "class".
<div class="form-group required"><label class="control-label">{{ form.username.label }}</label> {{ form.username(class_='form-control') }}</div> <div class="form-group required"><label class="control-label">{{ form.firstname.label }}</label> {{ form.firstname(class_='form-control') }}</div> <div class="form-group required"><label class="control-label">{{ form.lastname.label }}</label> {{ form.lastname(class_='form-control') }}</div> <div data-toggle="fieldset" id="email-fieldset" class="form-group"> {{ form.email.label }} <table class="ui table"> <thead> <th>Network</th> <th>Address</th> <th> {{ form_button(url_for('main.add_email'), icon ('plus')) }} </th> </thead> <tbody> {% for e in form.email %} <tr data-toggle="fieldset-entry"> <td>{{ e.network(class_='form-control') }}</td> <td>{{ e.address(class_='form-control') }}</td> <td> {{ form_button(url_for('main.remove_email', id=loop.index), icon ('remove')) }} </td> </tr> {% endfor %} </tbody> </table> </div>
Simplification: 