Формы WT:установка значения по умолчанию из поля SQLAlchemy с отношением
-
21-12-2019 - |
Вопрос
Здесь, на SO, есть много вопросов с названиями, которые звучат похоже на то, что я собираюсь описать, но, насколько я могу судить по буквально многочасовым исследованиям, этот вопрос уникален.Итак, поехали!
Я пишу свое первое приложение Flask.Я использую SQLAlchemy для уровня модели и WTForms для обработки форм.Приложение будет легким персональным финансовым менеджером, который я, вероятно, не буду использовать в серьезном бизнесе.У меня есть одна таблица для списка всех транзакций, а другая для всех категорий расходов (продукты, одежда и т.д.).В таблице транзакций есть столбец ("категория"), который ссылается на таблицу категорий.В представлении я представляю список категорий с элементом.
Моя проблема заключается в том, что при редактировании транзакции я не могу понять, как сообщить WTForms установить элементу определенное предопределенное значение.(Да, я знаю, что вы можете установить значение по умолчанию во время определения формы, это не то, о чем я спрашиваю.)
Модель выглядит следующим образом (с удаленными нерелевантными полями):
class Category(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), nullable=False, unique=True)
# ...
class Trans(db.Model):
# ...
category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
category = db.relationship('Category',
backref=db.backref('trans', lazy='dynamic'))
# ...
forms.py:
def category_choices():
return [('0', '')] + [(c.id, c.name) for c in Category.query.all()]
class TransactionForm(Form):
# ...
category = SelectField('Category', coerce=int, validators=[InputRequired()])
# ...
Маршрут (СООБЩЕНИЕ еще не реализовано):
@app.route('/transactions/edit/<trans_id>', methods=['GET', 'POST'])
def trans_edit(trans_id):
transaction = Trans.query.get(trans_id)
form = forms.TransactionForm(obj=transaction)
form.category.choices = forms.category_choices()
form.category.default = str(transaction.category.id)
#raise Exception # for debugging
return render_template('trans.html',
title='Edit Transaction',
form=form)
И, наконец, шаблон (Jinja2):
{{ form.category(class='trans-category input-medium') }}
Как вы можете видеть в маршруте, я установил form.category.default из transaction.category.id, но это не работает.Я думаю, моя проблема в том, что я устанавливаю "по умолчанию" после создания формы.Что я скорее вынужден делать, потому что модель берется из базы данных через SQLAlchemy.Основная причина, по-видимому, заключается в том, что form.category - это объект (из-за отношения), с которым WTForms, похоже, не может легко справиться.Не мог же я быть первым, кто столкнулся с этим...Нужно ли мне переработать модель, чтобы она была более совместима с WTForms?Какие у меня есть варианты?
Спасибо!
Решение
Я упоминал об этом в своем комментарии.Похоже, вам может быть полезно использовать расширение SQLAlchemy от WTForm.Это создаст выпадающий список категорий в вашей форме перевода.
Вариант использования в моем примере немного отличается.Я связываю запись в блоге с темой.То есть многие посты разделяют одну тему.Я полагаю, что в вашем случае многие транзакции относятся к одной категории.
Форма
from wtforms.ext.sqlalchemy.fields import QuerySelectField #import the ext.
def enabled_topics(): # query the topics (a.k.a categories)
return Topic.query.all()
class PostForm(Form): # create your form
title = StringField(u'title', validators=[DataRequired()])
body = StringField(u'Text', widget=TextArea())
topic = QuerySelectField(query_factory=enabled_topics, allow_blank=True)
Модели Важной частью здесь является а) проверка правильности определения взаимосвязи и б) добавление темы в ваш init, поскольку вы используете ее для создания новой записи.
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80))
body = db.Column(db.Text)
# one-to-many with Topic
topic = db.relationship('Topic', backref=db.backref('post', lazy='dynamic'))
def __init__(self, title, body, topic):
self.title = title
self.body = body
self.topic = topic
class Topic(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
def __init__(self, name):
self.name = name
Вид Здесь нет ничего особенного.Просто обычное представление, которое генерирует форму и обрабатывает отправленные результаты.
@app.route('/create', methods=['GET', 'POST'])
@login_required
def create_post():
form = PostForm()
if form.validate_on_submit():
post = Post(title=form.title.data, body=form.body.data,
topic=form.topic.data)
db.session.add(post)
db.session.commit()
Topic.update_counts()
flash('Your post has been published.')
return redirect(url_for('display_post', url=url))
posts = Post.query.all()
return render_template('create_post.html', form=form, posts=posts)
шаблон Здесь тоже нет ничего особенного.Просто не забудьте отобразить поле в шаблоне так, как вы бы отображали базовое поле.Никакого сложного цикла не требуется, поскольку расширения WTForms Sqlalchemy делают все это за вас.
{% extends "base.html" %}
{% block title %}Create/Edit New Post{% endblock %}
{% block content %}
<H3>Create/Edit Post</H3>
<form action="" method=post>
{{form.hidden_tag()}}
<dl>
<dt>Title:
<dd>{{ form.title }}
<dt>Post:
<dd>{{ form.body(cols="35", rows="20") }}
<dt>Topic:
<dd>{{ form.topic }}
</dl>
<p>
<input type=submit value="Publish">
</form>
{% endblock %}
Вот и все!Теперь в моей форме публикации есть выпадающий список тем.Используя вашу терминологию, когда вы загружаете транзакцию, категория по умолчанию для этой транзакции будет выделена в выпадающем списке.Правильный способ заявить об этом - сказать, что категория, связанная с транзакцией, загружается через отношение, определенное в модели trans.
Также обратите внимание, что существует также расширение multisellect SQLAlchemy на случай, если одна транзакция имеет много категорий по умолчанию.
Теперь моя проблема заключается в том, как справляться с отношениями "многие ко многим"....Я пытаюсь передать строку тегов, которые хранятся в таблице "многие ко многим", в поле TextArea.Для этого нет расширения SQLAlchemy!
Я опубликовал этот вопрос здесь