简体   繁体   中英

How to use populate_obj in Flask / WTForms with a FormField?

I have a User with a Location. Just as proof of concept the Location is a FormField within the CombinedForm which should be stored as a User Model. Eventually I want to have a fairly large number of nested forms so I would really like form.populate_obj(Model) to take care of processing the data. However, I must be doing something wrong. Here is my code:

# - - - Models - - -
class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer(), primary_key=True)
    username = db.Column(db.String(40))
    location = db.relationship('Location', backref='user')

class Location(db.Model):
    __tablename__ = 'locations'
    id = db.Column(db.Integer(), primary_key=True)
    user_id = db.Column(db.Integer(), db.ForeignKey('users.id'))
    descr = db.Column(db.String(50))

# - - - Forms - - -
class LocationForm(NoCsrfForm):
    descr = StringField('Location Name')

class CombinedForm(Form):
    username = StringField('User', validators=[DataRequired()])
    location = FormField(LocationForm)  # , default=lambda: Location())
    submit = SubmitField('Submit')

# - - - Routes - - -
@app.route('/', methods=['GET', 'POST'])
def index():
    user = User(username="not in db")
    form = CombinedForm(obj=user)
    if form.validate_on_submit():
        form.populate_obj(user)
        db.session.add(user)
        db.session.commit()
    return render_template('multi.html', form=form)

When I add user.location = [Location(descr="Test")] to the index function I can render the field using {{ form.location }} in my view, but changes to the field in the form don't have an effect on the model, since populate_obj does not populate the Location object with the POST data. When the FormField is inside a FieldList, populating it works.

What am I missing?

I've been unable to find a working FormField example without FieldList.

I've spent a lot of time on this, even made an example when I thought I had figured it out, but I was wrong, at least when using FormField with populate_list without FieldList. If there is a better approach to processing data from 2-3 Models in one Form please let me know. I'm going crazy so I would really appreciate some help. Thanks for your time.

It seems like I got really mixed up in my head and got the relationships wrong. The example below works.

Note that if you do pass an object, like a User instance, to the form, it hast to already have the location populated. If you pass a User without a location, populate_obj fails to find the new location from the Form on submission.

Is there a way around that?

This should work as a FormField proof of concept:

# - - - Models - - -
class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer(), primary_key=True)
    username = db.Column(db.String(40))
    location_id = db.Column(db.Integer, db.ForeignKey('locations.id'))


class Location(db.Model):
    __tablename__ = 'locations'
    id = db.Column(db.Integer(), primary_key=True)
    descr = db.Column(db.String(50))
    users = db.relationship('User', backref='location')


# - - - Forms - - -
class LocationForm(NoCsrfForm):
    descr = StringField('Location Name')


class CombinedForm(Form):
    username = StringField('User', validators=[DataRequired()])
    location = FormField(LocationForm, default=lambda: Location())
    submit = SubmitField('Submit')


# - - - Routes - - -
@app.route('/', methods=['GET', 'POST'])
def index():
    user = User(username="Def")
    form = CombinedForm()  # don't put a user obj in unless it has a location!
    if form.validate_on_submit():
        form.populate_obj(user)
        db.session.add(user)
        db.session.commit()
    return render_template('multi.html', form=form)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM