如何修复SQLAlchemy形式的"db.session.commit()";错误的父对象? [英] How to fix SQLAlchemy form "db.session.commit()" to the wrong parent object?

查看:78
本文介绍了如何修复SQLAlchemy形式的"db.session.commit()";错误的父对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的SQLAlchemy表单将 db.session.commit()提交给 .first()父级吗?

My SQLAlchemy form commits db.session.commit() to the .first() parent?

我有一个一对多"的提示可以将新的子项添加到特定的父对象的情况,但是由于某种原因,当我对子项进行更改时,它会自动提交给 .first()父项.不确定我是如何陷入这种情况的,我认为这只是我代码中某个地方的错误输入.我想对

I have a "one-to-many" situation where I can add new a child to a specific parent object, but for some reason when I commit changes to the child, it automatically commits to the .first() parent. Not sure how I got into this situation, I assume it's just a mistype somewhere in my code. I'd like to commit changes to

如何将 db.session.commit()提交给正确的父级?

How can I commit db.session.commit() to the correct parent?

# 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 %}

推荐答案

我同意上一位发言人(@PGHE).
这不是会议.您对"scrapingAccounts"的查询是原因.该查询基于URL中传递的ID.但是,这属于BotList.

I agree with the previous speaker (@PGHE).
It's not the session. Your query for the "scrapingAccounts" is the reason.
The query is based on the id passed in the URL. However, this belongs to the BotList.

<form action="/update/{{bot.id}}" method="POST">

但是,您在这里要求一个帐户.

However, you ask for an account here.

accounts = scrapingAccount.query.get_or_404(id)

最明智的做法是,如果您不以相同的方式编写所有内容,而是将任务分配给多个人.这不仅可以改善您的应用程序的结构,还可以使其更加清晰.
CRUD 范式可以作为更好结构的指南.使用蓝图可以制作这甚至更容易.

The most sensible thing would be if you would not write everything in the same route but would distribute tasks to several. This not only improves the structure of your application, but also makes it clearer.
The CRUD paradigm could serve as a guide for a better structure. Using blueprints can make this even easier.

以下方法是一种骇人的,快速的解决方法.

The following approach is a hacky, quick fix.

# 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中使用可选参数.

Another alternative could be to use optional parameters within the route / URL.

这篇关于如何修复SQLAlchemy形式的"db.session.commit()";错误的父对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆