对于Flask和SQLAlchemy以及编码我都是新手,请耐心等待。
我要做的是通过表单将数据发送到数据库。
正常工作直到我想要两个以上具有一对多关系的表,因为在植物中可以累积许多元素,而植物可以具有许多属性,并且烧瓶抛出错误 AttributeError:'list '对象没有属性'_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)中的数据的第二和第三字段时,它现在不起作用。
我去新工厂的路线是:
@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)
我尝试从终端在本地使用它,但是它可以工作,但是我不知道我缺少什么,或者关系模型是否错误,无法从flask应用程序中使用它。
预先感谢您的耐心和帮助。
经过反复试验后,现在似乎可以正常使用(将具有选定字段的工厂添加到数据库,将工厂数据正确呈现到模板中,将工厂正确添加到使用DB浏览器的数据库视图中(对于SQLite),将 QuerySelectMultipleField 更改为 QuerySelectField 之后。但是,我的意思是能够选择并呈现多种选择。
我注意到的另一件事是,在使用 QuerySelectField 时,模板会正确呈现一个下拉列表,但在尝试使用 QuerySelectMultipleField 时em> ,它仅呈现一个包含元素的列表,没有下拉列表。
这是模板的一小部分,具有选择字段 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。这可能与未正确为多次选择编写的模板格式有关吗? 谢谢。
我设法通过遍历这样的表单数据来使 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 App Using WTForms with SelectMultipleField,我应该使用 form.something.data 来获取我已完成但无法使用的项目列表,我只拿一件 请帮忙。 谢谢!
实施 sleblanc的响应后,我现在拥有以下适用于表单并正确显示的代码: ** models.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')可以完成此工作,并且不会因找不到表DNA而产生错误。
** routes.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!
答案 0 :(得分:0)
您已经建立了反向关系模型。
如果我没记错的话,您的植物与DNA和NFN之间存在一对多的关系,这意味着一个“植物”物体将具有多个DNA和多个NFN。按照定义,您的“植物”模型只有一个DNA字段和一个NFN字段,这意味着无法将关联存储在数据库中。
您将需要修改Plants模型以表示该关系。您必须首先确定一个“ DNA”或一个“ NFN”是否可以被多个“植物”共享,或者它们对于每个“植物”实例是否唯一,因为它们涉及不同的方案。
class Plants(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
# second option, exclusive relationship
class DNA(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
plants_id = db.Column(db.ForeignKey(Plants.id), nullable=False)
...
class NFN(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
plants_id = db.Column(db.ForeignKey(Plants.id), nullable=False)
...
# first option, non-exclusive relationship
class Plants(db.Model):
...
dna = relationship("DNA", secondary=plants_dna_table)
nfn = relationship("NFN", secondary=plants_nfn_table)
plants_dna_table = db.Table(
'plants_dna',
db.Column('plants_id', db.ForeignKey('plants.id'), nullable=False),
db.Column('dna_id', db.ForeignKey('dna.id'), nullable=False),
db.UniqueConstraint('plants_id', 'dna_id'),
)
plants_nfn_table = db.Table(
'plants_nfn',
db.Column('plants_id', db.ForeignKey('plants.id'), nullable=False),
db.Column('nfn_id', db.ForeignKey('nfn.id'), nullable=False),
db.UniqueConstraint('plants_id', 'nfn_id'),
)
修改Plants模型并在两者之间建立关系之后,请记住.dna和.nfn属性将具有列表类型。您可以通过append
到列表来为实例添加关系。
我强烈建议您使用FlaskForm.populate_obj:
@app.route("/plants/new/", methods=['GET', 'POST'])
@login_required# User must be logged in to create a new plant
def new_plant():
obj = Plants()
form = NewPlantForm()
if form.validate_on_submit():
form.populate_obj(obj)
print(obj) # debugging
db.session.add(obj)
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)
此外,它还允许您通过添加几行来合并将来的Plant更新视图:
@app.route('/plant/<id>/edit', methods=('GET', 'POST',))
@app.route('/plant/new', methods=('GET', 'POST'))
def create_plant(id=None):
if id is not None:
obj = Plant.query.get(id)
else:
obj = Plant()
form = PlantForm(request.form, obj=obj)
if form.validate_on_submit():
form.populate_obj(obj)
db.session.add(obj)
db.session.commit()
flash('Thank you ! You have successfully added '
'a plant to the database!', 'success')
return redirect(url_for('get_plant', id=obj.id))
else:
image_file = url_for(
'static',
filename='img/plants/default_plant_pic.jpg')
return render_template('plant.html', title='Add new plant',
image_file=image_file,
form=form, obj=obj)