Примечание: см. Ответы для разрешения ... Короче говоря, нельзя "использовать несколько форм для добавления родительских и дочерних объектов в БД за один коммит", используя один и тот же сеанс базы данных в SQLAlchemy. если между формами есть HTTP-запросы. Подходящий подход для моего варианта использования состоял в том, чтобы сохранить выходные данные моих нескольких форм в сеансе Flask и затем выполнить итерацию по сеансу в одном представлении, чтобы сделать фиксации базы данных.
Оригинальный вопрос:
TL; DR: можно ли использовать форму Flask-WTF для предварительного создания элемента Parent с помощью SQLAlchemy, db.session.flush()
, чтобы получить идентификатор родителя и передать его во вторую форму Flask-WTF для заполнения внешнего ключа Child, а затем зафиксировать Родитель и ребенок в одном db.session.commit()
?
Я создаю веб-приложение Flask, которое позволяет пользователям создавать и управлять конкурентными событиями. Мои модели базы данных включают в себя события и наборы событий. События могут быть дочерними для Eventset, но для Event не обязательно иметь соответствующего родителя Eventset. Однако для ситуаций, когда пользователи хотят создавать наборы событий и соответствующие события одновременно, я хочу включить это с помощью двухэтапной формы (которую я пытаюсь реализовать с помощью двух отдельных форм flask-wtf и представлений Flask).
Первая форма и представление позволяют пользователю создать экземпляр Eventset (). Этот Eventset () добавляется в сеанс базы данных sqlalchemy и сбрасывается, но не фиксируется. Если форма проверяется, приложение перенаправляет на следующее представление, которое позволяет создать событие. Я хочу передать идентификатор ранее созданного Eventset в мою модель Event (), чтобы завершить отношения Parent-Child.
Я пытаюсь сделать это, передав идентификатор, сгенерированный SQLAlchemy для Eventset, на первом этапе через сеанс Flask. ** Я могу успешно добавить Eventset_id в мой сеанс Flask и убедиться, что сеанс SQLAlchemy активен, но любые события, созданные на втором шаге, не распознают сброшенный (но не зафиксированный) набор событий, и в итоге фиксируются с eventset_id = NONE
.
Я хочу избежать фиксации Eventset с первого шага, так как я не хочу, чтобы пользователи непреднамеренно создавали потерянные Eventset, если они не завершили полный процесс установки (т. Е. Создали Eventset и n События).
forms.py
class EventsetsForm(FlaskForm):
name = StringField("Eventset Name", validators=[DataRequired()])
submit = SubmitField('Submit')
class EventForm(FlaskForm):
eventset_id = SelectField('Eventset', validators=[Optional()], coerce=int)
name = StringField("Event Name", validators=[DataRequired()])
submit = SubmitField('Submit')
def __init__(self, *args, **kwargs):
super(EventForm, self).__init__(*args, **kwargs)
self.eventset_id.choices = [(0, "---")]+[(eventset.id, eventset.name)
for eventset in Eventset.query.order_by(Eventset.name).all()]
views.py
nb: вспыхнувшие и распечатанные сообщения помогут мне увидеть, что происходит
@main.route('/eventsets/setup/step_one', methods=['GET', 'POST'])
@login_required
@admin_required
def setup_step_one():
form = EventsetsForm()
if current_user.can(Permission.WRITE) and form.validate_on_submit():
eventset = Eventset(name=form.name.data,
author=current_user._get_current_object())
db.session.add(eventset)
db.session.flush()
session['eventset_id'] = eventset.id
flash('STEP ONE: an eventset named %s has been propped.' % eventset.name)
flash('STEP ONE: The id from session is: %s' % session['eventset_id'])
print('STEP ONE: %s' % session['eventset_id'])
if eventset in db.session:
print('STEP ONE: sqlalchemy object for eventset is: %s' % eventset)
return redirect(url_for('.setup_step_two'))
return render_template('eventset_setup.html', form=form)
@main.route('/eventsets/setup/step_two', methods=['GET', 'POST'])
@login_required
@admin_required
def setup_step_two():
print('Is the db session active? %s' % db.session.is_active)
print('STEP TWO: the eventset id from Flask session should be: %s' % session['eventset_id'])
eventset_id = int(session['eventset_id'])
print('STEP TWO: is the eventset_id in the session an int? %s ' % isinstance(eventset_id, int))
form = EventForm()
form.eventset_id.data = eventset_id
if current_user.can(Permission.WRITE) and form.validate_on_submit():
event = Event(name=form.name.data,
author=current_user._get_current_object(),
description=form.description.data,
location=form.location.data,
scheduled=form.scheduled.data,
eventset_id=form.eventset_id.data,
event_datetime=form.event_datetime.data,
open_datetime=form.open_datetime.data)
db.session.add(event)
db.session.commit()
flash('An event named %s has been created, with eventset_id of %s.' % (event.name, event.eventset_id))
return redirect(url_for('.setup_step_two'))
return render_template('eventset_setup.html', eventset_id=eventset_id, form=form)
eventset_setup.html
{% block page_content %}
<div class="row">
<div class="col-md-4">
{% if session['eventset_id'] != None %}<p>Eventset id should be: {{ session['eventset_id'] }}</p>{% endif %}
{% if flarg != None %}{{ flarg }}{% endif %}
</div>
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
{% endblock %}
Терминальный выход
127.0.0.1 - - [11/Apr/2019 23:11:34] "GET /eventsets/setup/step_one HTTP/1.1" 200 -
STEP ONE: 54
STEP ONE: sqlalchemy object for eventset is: <app.models.Eventset object at 0x103c4dd30>
127.0.0.1 - - [11/Apr/2019 23:11:38] "POST /eventsets/setup/step_one HTTP/1.1" 302 -
Is the db session active? True
STEP TWO: the eventset id from Flask session should be: 54
STEP TWO: is the eventset_id in the session an int? True
127.0.0.1 - - [11/Apr/2019 23:11:38] "GET /eventsets/setup/step_two HTTP/1.1" 200 -
... пока События, созданные в этом потоке, приводят к event.eventset_id == НЕТ
В идеале, я бы хотел, чтобы пользователи могли создавать набор событий и связанное с ним событие с помощью одной фиксации SQLAlchemy (если я получу один набор событий: создание события работает, я могу выяснить, добавив несколько событий). В настоящее время мой код приводит к тому, что значение Eventset.id записывается в сеанс, а события создаются и фиксируются в БД без ожидаемого родителя Eventset. Я настоятельно предпочитаю избегать использования скрытых полей формы для достижения этой цели, и, к сожалению, мои знания Javascript незначительны.