[FIXED] How to properly give sql-alchemy query with relations to WTforms?

Issue

I have a problem that I’m working on several days.

I have a Flask app and in some place I want to edit some data in my DB. Data model I want to edit is similar to(using Flask-SQLAlchemy):

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    first_name = db.Column(db.String(64))
    last_name = db.Column(db.String(64))
    address = db.relationship('Address', backref='user', lazy='dynamic')

class Address(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    street = db.Column(db.String(64))
    region = db.Column(db.String(64))
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

Having defined model, I designed next form:

    class AddUser(FlaskForm):
        first_name = StringField("first_name", validators=[ InputRequired()])
        last_name = StringField("last_name", validators=[ InputRequired()])
        region = StringField("region", validators=[ InputRequired()])

In Flask, edit function looks like this:

@app.route('/edit_user/<int:id>', methods=['GET', 'POST'])
def edit_user(id):
    qry = User.query.filter_by(id=id).first()

    if qry:
        form = AddUser(obj=qry)
        if request.method == 'POST' and form.validate():
            # save edits
            qry.first_name = form.first_name .data
            qry.last_name = form.last_name.data
            qry.address.region = form.region.data
            db.session.commit()
            flash('Socnet updated successfully!')
            return redirect(url_for("view_record", user=form.id.data, level='overview'))
        return render_template('add_user.html', form=form, id=id)
    else:
        return 'Error loading #{id}'.format(id=id)

Unfortunately, the form looks like

First name: My_name
Last name: My_lastname
Region: [<Address 1>]

So it looks like WTForms doesn’t know what to do with relationships. If I write the template manually, I will use it like qry.adrress.region and it would work without any problems, but I really don’t want to build the forms by myself.

This case is not unique. The same issues arises with a table autogeneration module.

I know I miss something valuable. Please, help.

Solution

as an easy solution, if the user can have only one address, change the relationship to one-to-one or modify your user model to include the address

if you want to use one-to-one:
just change the relationship line as follows:

address = db.relationship('Address', backref='user', useList=False)

or one model for both user and address

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    first_name = db.Column(db.String(64))
    last_name = db.Column(db.String(64))
    street = db.Column(db.String(64))
    region = db.Column(db.String(64))

then you form will be as follows:

class AddUser(FlaskForm):
    first_name = StringField("first_name", validators=[ InputRequired()])
    last_name = StringField("last_name", validators=[ InputRequired()])
    street = StringField("street", validators=[ InputRequired()])
    region = StringField("region", validators=[ InputRequired()])

then flask route

@app.route('/edit_user/<int:id>', methods=['GET', 'POST'])
def edit_user(id):
    qry = User.query.filter_by(id=id).first()

    if qry:
        form = AddUser(obj=qry)
        if request.method == 'POST' and form.validate():
            # save edits
            qry.first_name = form.first_name .data
            qry.last_name = form.last_name.data
            qry.street = form.street.data
            qry.region = form.region.data
            db.session.commit()
            flash('Socnet updated successfully!')
            return redirect(url_for("view_record", user=form.id.data, level='overview'))
        return render_template('add_user.html', form=form, id=id)
    else:
        return 'Error loading #{id}'.format(id=id)

Answered By – Elie Saad

Answer Checked By – Timothy Miller (Easybugfix Admin)

Leave a Reply

(*) Required, Your email will not be published