wtforms/wtforms-sqlalchemy

QuerySelectField doesn't take the given/default value from database

Licceeee opened this issue · 0 comments

I use a wtform for both adding and editing an object and I use the queryselectfield for the foreign keys. The queryselectfield always takes the first value in the row. The correct value is actually passed right as far as I understand, it just doesn't take it for some reason.

Model e.g.:

    class State(db.Model):
        __tablename__ = 'state'
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        country_id = db.Column(db.Integer, db.ForeignKey('country.id'))
        name = db.Column(db.String(150))
        factor = db.Column(db.Float)
        time_created = db.Column(DateTime(timezone=True),
                                 server_default=func.now())
        time_updated = db.Column(DateTime(timezone=True), onupdate=func.now())
    
        def __init__(self, country_id, name, factor):
            self.country_id = country_id
            self.name = name
            self.factor = factor
    
        def __repr__(self):
            return '{} - {}'.format(self.country_id, self.name)

Model Country

    class Country(db.Model):
        __tablename__ = 'country'
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        name = db.Column(db.String(150))
        shortcut = db.Column(db.String(3))
        states = db.relationship('State', backref='country', lazy=True)
        time_created = db.Column(DateTime(timezone=True),
                                 server_default=func.now())
        time_updated = db.Column(DateTime(timezone=True), onupdate=func.now())
    
        def __init__(self, name, shortcut):
            self.name = name
            self.shortcut = shortcut
    
        def __repr__(self):
            return '{} - {} - {}'.format(self.id, self.name, self.shortcut)

View:

    @admin_blueprint.route('/admin/state/edit/<int:state_id>',
                           methods=['GET', 'POST'])
    @login_required
    def edit_state(state_id):
        """Edit state"""
        obj = State.query.filter_by(id=state_id).first_or_404()
        form = StateForm(obj=obj)
        if request.method == 'POST' and form.validate_on_submit:
            obj.country_id = form.country_id.data.id
            obj.name = form.name.data
            obj.factor = form.factor.data
            db.session.commit()
            log_entry_edit(State, obj)
            return redirect(url_for('admin.list_states'))
        return render_template('admin/state_form.html', form=form,
                               title='Edit State')

Form:

    def countries():
        return Country.query

    class StateForm(FlaskForm):
        name = StringField('State Name')
        factor = FloatField('Factor')
        country_id = QuerySelectField('Country', query_factory=countries)
        submit = SubmitField('Save')

Template:

        <div>
             {{ form.country_id.label }} 
             {{ form.country_id(class="form-control form-group") }}
             <output style="color: red;"  for="country_id" id="selected-country_id">
                  ACTUAL ID: {{ form.country_id.data }}
             </output>
        </div>
        {% if form.country_id.errors %}
            <ul class="errors">
                {% for error in country_id.errors %}
                    <li>{{ error }}</li>
                {% endfor %}
            </ul>
        {% endif %}

As u can see in following image, the form takes the first value and it will also overwrite the db if I don't select again the value I wanted in the first place.
The actual correct value arrives to the form though (ID in red):

screenshot of template output

I honestly can't see what I'm doing wrong. Does anyone have a clue?