Я новичок и во Flask, и в SQLAlchemy, и в кодировании, поэтому наберитесь терпения, пожалуйста.
Я пытаюсь отправить данные через форму в базу данных.
Работал нормально, пока ятребуется еще две таблицы с отношением один ко многим, так как в растении может накапливаться много элементов, и растение может иметь много свойств , а ошибка при вызове колбы AttributeError: объект списка не имеет атрибута'_sa_instance_state' .
Форма:
from flask_wtf import FlaskForm
from wtforms.ext.sqlalchemy.fields import QuerySelectMultipleField
from wtforms import StringField, PasswordField, SubmitField,BooleanField, TextAreaField
#Query for Dynamic Nutrient Accumulator Model
def enabled_dna():
return DNA.query.all()
#Query for Nitrogen Fixers Nursing Model
def enabled_nfn():
return NFN.query.all()
class NewPlantForm(FlaskForm):
common_name = StringField('Common Name', render_kw={"placeholder": "Common name"},
validators=[DataRequired(), Length(min=2, max=40)])
botanical_name = StringField('Botanical Name', render_kw={"placeholder": "Botanical name"},
validators=[DataRequired(), Length(min=2, max=80)])
short_description = TextAreaField('Short Description', render_kw={"placeholder": "Please add a short description"},
validators=[DataRequired()])
medicinal = TextAreaField('Medicinal Use', render_kw={"placeholder": "Medicinal use"},
validators=[DataRequired()])
dna = QuerySelectMultipleField('Select Element',query_factory=enabled_dna,allow_blank=True)
nfn = QuerySelectMultipleField('Select Property',query_factory=enabled_nfn,allow_blank=True)
submit = SubmitField('Add plant')
. Файл models.py выглядит следующим образом:
#Plants Table
class Plants(db.Model):
id = db.Column(db.Integer, primary_key=True)
common_name = db.Column(db.String(40), nullable=False)
botanical_name = db.Column(db.String(80), unique=True, nullable=False)
short_description = db.Column(db.Text, nullable=False)
medicinal = db.Column(db.Text, nullable=False)
image_file = db.Column(db.String(20), default='default_plant_pic.jpg')
date_added = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
dna_id = db.Column(db.Integer, db.ForeignKey('DNA.id'))
dna = db.relationship('DNA', backref=db.backref('plant_dna', lazy='dynamic')) # Dynamic_Nutrient_Accumulated
nfn_id = db.Column(db.Integer, db.ForeignKey('NFN.id'))
nfn = db.relationship('NFN', backref=db.backref('plant_nfn', lazy='dynamic')) # Nitrogen_Fixers_Nursing
def __repr__(self):
return f"Plants('{self.common_name}', '{self.botanical_name}', '{self.short_description}'," \
f" '{self.medicinal}', '{self.dna}', '{self.nfn}' )"
#Dynamic_Nutrient_Accumulated
class DNA(db.Model):
id = db.Column(db.Integer, primary_key=True)
element = db.Column(db.String(15))
def __repr__(self):
return '[ {}]'.format(self.element)
#Nitrogen_Fixers_Nursing
class NFN(db.Model):
id = db.Column(db.Integer, primary_key=True)
plant_extra = db.Column(db.String(40))
def __repr__(self):
return '[ {}]'.format(self.plant_extra)
Маршрут и форма работали нормально сформа, содержащая поля только для одной таблицы.Однако теперь это не работает, пока я добавил второе и третье поля, содержащие данные из других таблиц (form.dna.data и form.nfn.data).
Мой маршрут для New Plant:
@app.route("/plants/new/", methods=['GET', 'POST'])
@login_required# User must be logged in to create a new plant
def new_plant():
form = NewPlantForm()
if form.validate_on_submit():
new_plant = Plants(common_name=form.common_name.data,
botanical_name=form.botanical_name.data,
short_description=form.short_description.data,
medicinal=form.medicinal.data,
dna=form.dna.data,
nfn=form.nfn.data,
author=current_user)
db.session.add(new_plant)
db.session.commit()
flash('Thank you ! You have successfully added a plant '
'to the database!', 'success')
return redirect(url_for('plants'))
image_file = url_for('static', filename='img/plants/default_plant_pic.jpg')
return render_template('new_plant.html', title='Add new plant',
image_file=image_file, form=form)
И маршрут для отображения информации о растениях:
@app.route("/plants")
def plants():
plants = Plants.query.all()
return render_template('plants.html', title= 'Plants Database', plants=plants)
Я пытался работать с этим локально из терминала, и это работает, но я не знаю, что мне не хватаетили если модель отношений неверна, чтобы заставить ее работать из приложения фляги.
Заранее спасибо за терпение и помощь.
Обновление
После проб и ошибок, теперь этоКажется, все это работает (добавьте завод с выбранными полями в БД, правильно отобразите данные завода в шаблон, завод будет корректно добавлен в просмотр БД с помощью браузера БД для SQLite) после того, как я изменил QuerySelectMultipleField до QuerySelectField .Однако я хотел иметь возможность выбирать и отображать несколько вариантов.
Еще одна вещь, которую я заметил, заключается в том, что при использовании QuerySelectField шаблон правильно отображаетвыпадающий, но при попытке использовать QuerySelectMultipleField , он отображает только список с элементами, без выпадающего списка.
Вот небольшая часть шаблона с полем выбора form.dna и form.nfn :
<div class="form-group">
{{ form.dna(class="form-control form-control-sm") }}
</div>
<div class="form-group">
{{ form.nfn(class="form-control form-control-sm")}}
</div>
Я использую Bootstrap.Может ли это быть связано с неправильным написанием шаблонов для множественного выбора?Спасибо.
Update2
Мне удалось заставить QuerySelectMultipleField работать, просматривая данные формы следующим образом:
@app.route("/plants/new/", methods=['GET', 'POST'])
@login_required# User must be logged in to create a new plant
def new_plant():
form = NewPlantForm()
if form.validate_on_submit():
new_plant = Plants(common_name = form.common_name.data, botanical_name = form.botanical_name.data,
short_description = form.short_description.data, medicinal=form.medicinal.data,
author=current_user)
**for dna_element in form.dna.data:
new_plant.dna = dna_element
for nfn_element in form.nfn.data:
new_plant.nfn = nfn_element**
db.session.add(new_plant)
db.session.commit()
flash(f'Thank you ! You have successfully added a plant to the database!', 'success')
return redirect(url_for('plants'))
image_file = url_for('static', filename='img/plants/default_plant_pic.jpg')
return render_template('new_plant.html', title='Add new plant',
image_file=image_file, form=form)
Я больше не получаю сообщение об ошибке AttributeError: у объекта 'list' нет атрибута '_sa_instance_state' и установка успешно добавлена в базу данных, однако, когда я смотрю в базу данных, я могуубедитесь, что был выбран только один вариант, а не несколько вариантов.Из того, что я прочитал здесь: Приложение Flask Используя WTForms с SelectMultipleField , я должен был использовать form.something.data , чтобы получить список элементов, которые я сделал, но до сих пор нетне работает, и я получаю только один предмет.Пожалуйста помоги.Спасибо!
Обновление 3 и решение проблемы
После реализации ответа sleblanc у меня теперь есть следующий код, который работает с формой и правильно отображает: ** модели.py: **
plants_dna_table = db.Table(
'plants_dna',
db.Column('plants_id', db.Integer, db.ForeignKey('plants.id'), nullable=False),
db.Column('dna_id', db.Integer, db.ForeignKey('DNA.id'), nullable=False),
db.UniqueConstraint('plants_id', 'dna_id'))
plants_nfn_table = db.Table(
'plants_nfn',
db.Column('plants_id', db.Integer, db.ForeignKey('plants.id'), nullable=False),
db.Column('nfn_id', db.Integer, db.ForeignKey('NFN.id'), nullable=False),
db.UniqueConstraint('plants_id', 'nfn_id'))
#Plants Table
class Plants(db.Model):
id = db.Column(db.Integer, primary_key=True)
common_name = db.Column(db.String(40), nullable=False)
botanical_name = db.Column(db.String(80), unique=True, nullable=False)
short_description = db.Column(db.Text, nullable=False)
medicinal = db.Column(db.Text, nullable=False)
image_file = db.Column(db.String(20), default='default_plant_pic.jpg')
date_added = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
dna = db.relationship('DNA', secondary = plants_dna_table) # Dynamic_Nutrient_Accumulated
nfn = db.relationship('NFN', secondary = plants_nfn_table) # Nitrogen_Fixers_Nursing
def __repr__(self):
return f"Plants('{self.common_name}', '{self.botanical_name}', '{self.short_description}'," \
f" '{self.medicinal}', '{self.dna}', '{self.nfn}' )"
#Dynamic_Nutrient_Accumulated
class DNA(db.Model):
id = db.Column(db.Integer, primary_key=True)
element = db.Column(db.String(15))
def __repr__(self):
return '{}'.format(self.element)
#Nitrogen_Fixers_Nursing
class NFN(db.Model):
id = db.Column(db.Integer, primary_key=True)
plant_extra = db.Column(db.String(40))
def __repr__(self):
return '{}'.format(self.plant_extra)
db.ForeignKey ('DNA.id') , отображаемое заглавными буквами, выполняет работу и не получит ошибку, так как не найдет ДНК таблицы.
** rout.py: **
#Route for users to add a plant to the database
@app.route("/plants/new/", methods=['GET', 'POST'])
@login_required# User must be logged in to create a new plant
def new_plant():
form = NewPlantForm()
if form.validate_on_submit():
new_plant = Plants(common_name = form.common_name.data, botanical_name = form.botanical_name.data,
short_description = form.short_description.data, medicinal=form.medicinal.data,
author=current_user)
for dna_element in form.dna.data:
new_plant.dna.append(dna_element)
for nfn_element in form.nfn.data:
new_plant.nfn.append(nfn_element)
print(new_plant)
db.session.add(new_plant)
db.session.commit()
flash(f'Thank you ! You have successfully added a plant to the database!', 'success')
return redirect(url_for('plants'))
image_file = url_for('static', filename='img/plants/default_plant_pic.jpg')
return render_template('new_plant.html', title='Add new plant',
image_file=image_file, form=form)
Спасибо @sleblanc!