Как использовать populate_obj в Flask / WTForms с FormField?

У меня есть пользователь с местоположением. Так же, как подтверждение концепции, Location - это FormField в CombinedForm, который должен храниться как пользовательская модель. В конце концов я хочу иметь достаточно большое количество вложенных форм, поэтому мне бы очень хотелось, чтобы form.populate_obj(Model) позаботился об обработке данных. Тем не менее, я должен делать что-то не так. Вот мой код:

# - - - 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)

Когда я добавлю user.location = [Location(descr="Test")] к функции индекса я могу сделать поле с помощью {{ form.location }} на мой взгляд, но изменения в поле формы не влияют на модель, так как populate_obj не заполняет объект Location данными POST. Когда FormField находится внутри FieldList, его заполнение работает.

Что мне не хватает?

Я не смог найти работающий пример FormField без FieldList.

Я потратил много времени на это, даже сделал пример, когда мне показалось, что я понял это, но я ошибался, по крайней мере, когда использовал FormField с populate_list без FieldList. Если есть лучший подход к обработке данных из 2-3 моделей в одной форме, пожалуйста, дайте мне знать. Я схожу с ума, поэтому я был бы очень признателен за помощь. Спасибо за ваше время.

1 ответ

Кажется, я действительно запутался в голове и неправильно понял отношения. Пример ниже работает.

Обратите внимание, что если вы передаете объект, например, экземпляр пользователя, в форму, то уже должно быть заполнено местоположение. Если вы передадите пользователя без местоположения, populate_obj не сможет найти новое местоположение из формы при отправке.

Есть ли способ обойти это?

Это должно работать как подтверждение концепции FormField:

# - - - 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)
Другие вопросы по тегам