如何将 SQLAlchemy 表单 "db.session.commit()" 修复为错误的 parent object?
How to fix SQLAlchemy form "db.session.commit()" to the wrong parent object?
我的 SQLAlchemy 表单将 db.session.commit()
提交给 .first()
parent?
我有一个“one-to-many”的情况,我可以将新的 child 添加到特定的 parent object,但是由于某些原因,当我提交更改时child,它会自动提交到 .first()
parent。不确定我是如何陷入这种情况的,我认为这只是我代码中某处的错误输入。我想提交对
的更改
如何将 db.session.commit()
提交到正确的 parent?
app.py
# Parent
class botList(db.Model):
__tablename__ = 'botlist'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(200), nullable=False)
channel = db.Column(db.String(200), nullable=False)
bots = db.Column(db.Integer, nullable=False)
status = db.Column(db.String(200), nullable=False)
igUsername = db.Column(db.String(200), nullable=True)
igPassword = db.Column(db.String(200), nullable=True)
ytUsername = db.Column(db.String(200), nullable=True)
ytPassword = db.Column(db.String(200), nullable=True)
scrapingAccounts = db.relationship("scrapingAccount", backref="owner", lazy='dynamic')
def __repr__(self):
return '<Username %r>' % self.id
# Child
class scrapingAccount(db.Model):
__tablename__ = 'scrapingaccount'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(200), nullable=False)
username = db.Column(db.String(200), nullable=False)
owner_id = db.Column(db.Integer, db.ForeignKey("botlist.id"))
@app.route('/update/<int:id>', methods=['GET', 'POST'])
def updateBot(id):
bot_to_update = botList.query.get_or_404(id)
if request.method == "POST":
if request.form.get("updateBotButton"):
bot_to_update.username = request.form['username']
bot_to_update.channel = request.form['channel']
bot_to_update.bots = request.form['bots']
bot_to_update.status = request.form['status']
bot_to_update.igUsername = request.form['igUsername']
bot_to_update.igPassword = request.form['igPassword']
bot_to_update.ytUsername = request.form['ytUsername']
bot_to_update.ytPassword = request.form['ytPassword']
try:
db.session.commit()
return redirect('#')
except:
return "There was a problem updating that bot."
elif request.form.get("addAccountButton"):
if request.method == "POST":
name = request.form['addigname']
username = request.form['addiguser']
new_account = scrapingAccount(name=name, username=username)
try:
db.session.add(new_account)
db.session.commit()
return redirect('#')
except:
return "There was a problem adding an account."
else:
return redirect('#')
elif request.form.get("updateAccountButton"):
if request.method == "POST":
account = scrapingAccount.query.filter_by(owner_id=bot_to_update.id, id=request.form['accountid']).first_or_404()
account.name = request.form['igname']
account.username = request.form['iguser']
try:
db.session.commit()
return redirect('#')
except:
return "There was a problem updating an account."
else:
return redirect('#')
else:
return render_template("update.html", bot=bot_to_update)
update.html
{% for account in bot.scrapingAccounts %}
<form action="/update/{{bot.id}}" method="POST">
<input type="hidden" name="accountid" value="{{ account.id }}"/>
<input type="text" name="igname" id="igname" placeholder="eg. Jane Doe" value="{{account.name}}"/>
<input type="text" name="iguser" id="iguser" placeholder="eg. jandoe" value="{{account.username}}"/>
<input type="submit" name="updateAccountButton" value="Update">
</form>
{% endfor %}
您正在通过 id
列查询 table scrapingaccount
,并将可能不相关的帐户返回到您的机器人列表。您要按 owner_id
列查询 scrapingaccount
。
这一行:
accounts = scrapingAccount.query.get_or_404(id)
应该是:
accounts = db.session.query(scrapingAccount).filter(scrapingAccount.owner_id == id).all()
编辑:我假设您函数中的 id
是 BotList table.
的 id
我同意前面一位演讲者 (@PGHE) 的观点。
这不是 session。
原因是您对“scrapingAccounts”的查询。
查询基于在 URL 中传递的 id。但是,这属于BotList。
<form action="/update/{{bot.id}}" method="POST">
但是,您在这里申请一个帐户。
accounts = scrapingAccount.query.get_or_404(id)
最明智的做法是不要将所有内容都写在同一个路由中,而是将任务分配给多个。这不仅改进了您的应用程序的结构,而且使它更加清晰。
CRUD
范式可以作为更好结构的指南。使用 blueprints 可以使
这更容易。
以下方法是一种 hacky 的快速修复方法。
# Parent
class botList(db.Model):
__tablename__ = 'botlist'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(200), nullable=False)
channel = db.Column(db.String(200), nullable=False)
bots = db.Column(db.Integer, nullable=False)
status = db.Column(db.String(200), nullable=False)
igUsername = db.Column(db.String(200), nullable=True)
igPassword = db.Column(db.String(200), nullable=True)
ytUsername = db.Column(db.String(200), nullable=True)
ytPassword = db.Column(db.String(200), nullable=True)
scrapingAccounts = db.relationship("scrapingAccount",
backref="owner",
lazy='dynamic',
# Make sure that the referenced child objects are deleted when the
# parent object is deleted.
cascade="all, delete-orphan"
)
def __repr__(self):
return '<Username %r>' % self.id
# Child
class scrapingAccount(db.Model):
__tablename__ = 'scrapingaccount'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(200), nullable=False)
username = db.Column(db.String(200), nullable=False)
# To avoid unassigned accounts, you can forbid empty entries for the foreign key.
# An error will be thrown if you try to set it to null / none.
owner_id = db.Column(db.Integer, db.ForeignKey("botlist.id"), nullable=False)
@app.route('/update/<int:id>', methods=['GET', 'POST'])
def updateBot(id):
bot_to_update = botList.query.get_or_404(id)
if request.method == "POST":
# If it is a POST request.
if request.form.get("updateBotButton"):
bot_to_update.username = request.form['username']
bot_to_update.channel = request.form['channel']
bot_to_update.bots = request.form['bots']
bot_to_update.status = request.form['status']
bot_to_update.igUsername = request.form['igUsername']
bot_to_update.igPassword = request.form['igPassword']
bot_to_update.ytUsername = request.form['ytUsername']
bot_to_update.ytPassword = request.form['ytPassword']
try:
db.session.commit()
return redirect('#')
except:
return "There was a problem updating that bot."
elif request.form.get("addAccountButton"):
name = request.form['addigname']
username = request.form['addiguser']
# You want to add an account to the bot here, so you have to set the
# reference, otherwise it will be saved, but it does not belong anywhere.
new_account = scrapingAccount(
name=name,
username=username,
owner_id=bot_to_update.id
)
db.session.add(new_account)
# Instead of assigning the associated bot id to the new account and
# adding it to the session, you can also add it to the scrapingAccounts
# list. Then you don't need to add it to the session as the parent is
# already part of it. The owner_id is assigned automatically.
#
# bot_to_update.scrapingAccounts.add(new_account)
try:
db.session.commit()
return redirect('#')
except:
return "There was a problem adding an account."
elif request.form.get("updateAccountButton"):
# Request the account using its id and owner_id.
account = scrapingAccount.query.filter_by(
owner_id=bot_to_update.id,
id=request.form['accountid']
).first_or_404()
account.name = request.form['igname']
account.username = request.form['iguser']
try:
db.session.commit()
return redirect('#')
except:
return "There was a problem updating an account."
else:
return render_template("update.html", bot=bot_to_update)
{% for account in bot.scrapingAccounts %}
<form action="/update/{{bot.id}}" method="POST">
<input type="hidden" name="accountid" value="{{ account.id }}"/>
<input type="text" name="igname" id="igname" placeholder="eg. Jane Doe" value="{{account.name}}"/>
<input type="text" name="iguser" id="iguser" placeholder="eg. jandoe" value="{{account.username}}"/>
<input type="submit" name="updateAccountButton" value="Update accounts">
</form>
{% endfor %}
另一种选择是在路由中使用可选参数 / URL.
我的 SQLAlchemy 表单将 db.session.commit()
提交给 .first()
parent?
我有一个“one-to-many”的情况,我可以将新的 child 添加到特定的 parent object,但是由于某些原因,当我提交更改时child,它会自动提交到 .first()
parent。不确定我是如何陷入这种情况的,我认为这只是我代码中某处的错误输入。我想提交对
如何将 db.session.commit()
提交到正确的 parent?
app.py
# Parent
class botList(db.Model):
__tablename__ = 'botlist'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(200), nullable=False)
channel = db.Column(db.String(200), nullable=False)
bots = db.Column(db.Integer, nullable=False)
status = db.Column(db.String(200), nullable=False)
igUsername = db.Column(db.String(200), nullable=True)
igPassword = db.Column(db.String(200), nullable=True)
ytUsername = db.Column(db.String(200), nullable=True)
ytPassword = db.Column(db.String(200), nullable=True)
scrapingAccounts = db.relationship("scrapingAccount", backref="owner", lazy='dynamic')
def __repr__(self):
return '<Username %r>' % self.id
# Child
class scrapingAccount(db.Model):
__tablename__ = 'scrapingaccount'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(200), nullable=False)
username = db.Column(db.String(200), nullable=False)
owner_id = db.Column(db.Integer, db.ForeignKey("botlist.id"))
@app.route('/update/<int:id>', methods=['GET', 'POST'])
def updateBot(id):
bot_to_update = botList.query.get_or_404(id)
if request.method == "POST":
if request.form.get("updateBotButton"):
bot_to_update.username = request.form['username']
bot_to_update.channel = request.form['channel']
bot_to_update.bots = request.form['bots']
bot_to_update.status = request.form['status']
bot_to_update.igUsername = request.form['igUsername']
bot_to_update.igPassword = request.form['igPassword']
bot_to_update.ytUsername = request.form['ytUsername']
bot_to_update.ytPassword = request.form['ytPassword']
try:
db.session.commit()
return redirect('#')
except:
return "There was a problem updating that bot."
elif request.form.get("addAccountButton"):
if request.method == "POST":
name = request.form['addigname']
username = request.form['addiguser']
new_account = scrapingAccount(name=name, username=username)
try:
db.session.add(new_account)
db.session.commit()
return redirect('#')
except:
return "There was a problem adding an account."
else:
return redirect('#')
elif request.form.get("updateAccountButton"):
if request.method == "POST":
account = scrapingAccount.query.filter_by(owner_id=bot_to_update.id, id=request.form['accountid']).first_or_404()
account.name = request.form['igname']
account.username = request.form['iguser']
try:
db.session.commit()
return redirect('#')
except:
return "There was a problem updating an account."
else:
return redirect('#')
else:
return render_template("update.html", bot=bot_to_update)
update.html
{% for account in bot.scrapingAccounts %}
<form action="/update/{{bot.id}}" method="POST">
<input type="hidden" name="accountid" value="{{ account.id }}"/>
<input type="text" name="igname" id="igname" placeholder="eg. Jane Doe" value="{{account.name}}"/>
<input type="text" name="iguser" id="iguser" placeholder="eg. jandoe" value="{{account.username}}"/>
<input type="submit" name="updateAccountButton" value="Update">
</form>
{% endfor %}
您正在通过 id
列查询 table scrapingaccount
,并将可能不相关的帐户返回到您的机器人列表。您要按 owner_id
列查询 scrapingaccount
。
这一行:
accounts = scrapingAccount.query.get_or_404(id)
应该是:
accounts = db.session.query(scrapingAccount).filter(scrapingAccount.owner_id == id).all()
编辑:我假设您函数中的 id
是 BotList table.
id
我同意前面一位演讲者 (@PGHE) 的观点。
这不是 session。
原因是您对“scrapingAccounts”的查询。
查询基于在 URL 中传递的 id。但是,这属于BotList。
<form action="/update/{{bot.id}}" method="POST">
但是,您在这里申请一个帐户。
accounts = scrapingAccount.query.get_or_404(id)
最明智的做法是不要将所有内容都写在同一个路由中,而是将任务分配给多个。这不仅改进了您的应用程序的结构,而且使它更加清晰。
CRUD
范式可以作为更好结构的指南。使用 blueprints 可以使
这更容易。
以下方法是一种 hacky 的快速修复方法。
# Parent
class botList(db.Model):
__tablename__ = 'botlist'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(200), nullable=False)
channel = db.Column(db.String(200), nullable=False)
bots = db.Column(db.Integer, nullable=False)
status = db.Column(db.String(200), nullable=False)
igUsername = db.Column(db.String(200), nullable=True)
igPassword = db.Column(db.String(200), nullable=True)
ytUsername = db.Column(db.String(200), nullable=True)
ytPassword = db.Column(db.String(200), nullable=True)
scrapingAccounts = db.relationship("scrapingAccount",
backref="owner",
lazy='dynamic',
# Make sure that the referenced child objects are deleted when the
# parent object is deleted.
cascade="all, delete-orphan"
)
def __repr__(self):
return '<Username %r>' % self.id
# Child
class scrapingAccount(db.Model):
__tablename__ = 'scrapingaccount'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(200), nullable=False)
username = db.Column(db.String(200), nullable=False)
# To avoid unassigned accounts, you can forbid empty entries for the foreign key.
# An error will be thrown if you try to set it to null / none.
owner_id = db.Column(db.Integer, db.ForeignKey("botlist.id"), nullable=False)
@app.route('/update/<int:id>', methods=['GET', 'POST'])
def updateBot(id):
bot_to_update = botList.query.get_or_404(id)
if request.method == "POST":
# If it is a POST request.
if request.form.get("updateBotButton"):
bot_to_update.username = request.form['username']
bot_to_update.channel = request.form['channel']
bot_to_update.bots = request.form['bots']
bot_to_update.status = request.form['status']
bot_to_update.igUsername = request.form['igUsername']
bot_to_update.igPassword = request.form['igPassword']
bot_to_update.ytUsername = request.form['ytUsername']
bot_to_update.ytPassword = request.form['ytPassword']
try:
db.session.commit()
return redirect('#')
except:
return "There was a problem updating that bot."
elif request.form.get("addAccountButton"):
name = request.form['addigname']
username = request.form['addiguser']
# You want to add an account to the bot here, so you have to set the
# reference, otherwise it will be saved, but it does not belong anywhere.
new_account = scrapingAccount(
name=name,
username=username,
owner_id=bot_to_update.id
)
db.session.add(new_account)
# Instead of assigning the associated bot id to the new account and
# adding it to the session, you can also add it to the scrapingAccounts
# list. Then you don't need to add it to the session as the parent is
# already part of it. The owner_id is assigned automatically.
#
# bot_to_update.scrapingAccounts.add(new_account)
try:
db.session.commit()
return redirect('#')
except:
return "There was a problem adding an account."
elif request.form.get("updateAccountButton"):
# Request the account using its id and owner_id.
account = scrapingAccount.query.filter_by(
owner_id=bot_to_update.id,
id=request.form['accountid']
).first_or_404()
account.name = request.form['igname']
account.username = request.form['iguser']
try:
db.session.commit()
return redirect('#')
except:
return "There was a problem updating an account."
else:
return render_template("update.html", bot=bot_to_update)
{% for account in bot.scrapingAccounts %}
<form action="/update/{{bot.id}}" method="POST">
<input type="hidden" name="accountid" value="{{ account.id }}"/>
<input type="text" name="igname" id="igname" placeholder="eg. Jane Doe" value="{{account.name}}"/>
<input type="text" name="iguser" id="iguser" placeholder="eg. jandoe" value="{{account.username}}"/>
<input type="submit" name="updateAccountButton" value="Update accounts">
</form>
{% endfor %}
另一种选择是在路由中使用可选参数 / URL.