Flask 和 SQLalchemy NoForeignKeysError:无法确定关系 User.posts 上的父/子表之间的连接条件 [关闭]

Posted

技术标签:

【中文标题】Flask 和 SQLalchemy NoForeignKeysError:无法确定关系 User.posts 上的父/子表之间的连接条件 [关闭]【英文标题】:Flask and SQLalchemy NoForeignKeysError: Could not determine join condition between parent/child tables on relationship User.posts [closed] 【发布时间】:2016-03-18 00:58:44 【问题描述】:

我从 Mega Flask 教程开始构建一个简单的博客风格网站。我已经将它与一些 python 学习相关联,以尝试强化我所学的知识。为了了解更多信息,我决定将不推荐使用 OAuth 登录的教程换成传统的烧瓶登录。我在尝试登录时遇到了问题。

NoForeignKeysError:无法确定关系 User.posts 上的父/子表之间的连接条件 - 没有链接这些表的外键。确保引用列与 ForeignKey 或 ForeignKeyConstraint 相关联,或指定“primaryjoin”表达式。

由于我对 Python 的了解有限,我在这里遇到了很大的障碍。我似乎无法弄清楚我做错了什么。

这是我的 models.py 文件。

followers = db.Table('followers',
            db.Column('follower_id', db.Integer, db.ForeignKey('user.id')),
            db.Column('followed_id', db.Integer, db.ForeignKey('user.id'))
            )

class User(db.Model):
    __tablename__ = "users"
    id = db.Column(db.Integer, primary_key=True)
    nickname = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password = db.Column(db.String(100))
    posts = db.relationship('Post', backref='author', lazy='dynamic')
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime)
    followed = db.relationship('User',
                                secondary=followers,
                                primaryjoin=(followers.c.follower_id == id),
                                secondaryjoin=(followers.c.followed_id == id),
                                backref=db.backref('followers', lazy='dynamic'),
                                lazy='dynamic')

    #reviews = db.relationship('Review', backref='author', lazy='dynamic') This is the review connectino for the user. 

    @staticmethod
    def make_unique_nickname(nickname):
        if User.query.filter_by(nickname=nickname).first() is None:
            return nickname
        version = 2
        while True:
            new_nickname = nickname + str(version)
            if User.query.filter_by(nickname=new_nickname).first() is None:
                break
            version += 1
        return new_nickname

    def is_authenticated(self):
        return True

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def get_id(self):
        try:
            return unicode(self.id) #python 2
        except:
            return str(self.id) #python 3

    def follow(self, user):
        if not self.is_following(user):
            self.followed.append(user)
            return self

    def unfollow(self, user):
        if self.is_following(user):
            self.followed.remove(user)
            return self

    def is_following(self, user):
        return self.followed.filter(
            followers.c.followed_id == user.id).count() > 0

    def followed_posts(self):
        return Post.query.join(
            followers, (followers.c.followed_id == Post.user_id)).filter(
                followers.c.follower_id == self.id).order_by(
                    Post.timestamp.desc())

    def __repr__(self):
            return '<User >'.format(self.nickname)

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    body = db.Column(db.String(140))
    timestamp = db.Column(db.DateTime)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

    def __repr__(self):
        return '<Post >'.format(self.body)

这是我的 views.py 文件,应该包含相关代码:

#This loads the user from the database
@lm.user_loader
def load_user(id):
    return User.query.get(int(id))


@app.before_request
def before_request():
    g.user = current_user
    if g.user.is_authenticated:
        g.user.last_seen = datetime.utcnow()
        db.session.add(g.user)
        db.session.commit()


@app.errorhandler(404)
def not_found_error(error):
    return render_template('404.html'), 404


@app.errorhandler(500)
def internal_error(error):
    db.session.rollback()
    return render_template('500.html'), 500


@app.route('/')
@app.route('/index')
@login_required
def index():
    user = g.user
    posts = [ #fake array of posts
        
        'author': 'nickname': 'Zach',
        'body': 'Reviewed this product!'
        ,
        
        'author': 'nickname': 'Mark',
        'body': 'I like buttcheese!'
        
    ]
    return render_template('index.html',
                            title='Home',
                            user=user,
                            posts=posts)



@app.route('/login', methods=['GET', 'POST'])
def login():
    if g.user is not None and g.user.is_authenticated:
        return redirect(url_for('index'))
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.get(form.nickname.data)
        session['remember_me'] = form.remember_me.data
        if user:
            if check_password_hash(user.password, form.password.data):
                user.authenticated = True
                db.session.add(user)
                db.session.commit()
                login_user(user, remember=True)
                flash("you've been logged in!, 'success'")
                return redirect(url_for('index'))
            else:
                flash('your email or password doesnt match!', 'error')
    return render_template('login.html', 
                            title='Sign In', 
                            form=form)


@app.route('/logout')
def logout():
    logout_user()
    return redirect(url_for('index'))

我真的在为此苦苦挣扎,因为我还不够了解数据库关系。这是一次很棒的学习经历,但如果有人能提供帮助,那就太好了。

【问题讨论】:

【参考方案1】:

连同上面的帖子,我相信我已经解决了这个问题。我注意到我的表名实际上是“用户”而不是“用户”。结果,我需要将所有外键从 user.id 更改为 users.id,因为它正在寻找一个名为“user”的表,但该表并不存在。

【讨论】:

【参考方案2】:

我认为这是一个 sqlalchemy 错误。当您在此处定义 User 和 Posts 之间的关系时:

posts = db.relationship('Post', backref='author', lazy='dynamic')

sqlalchemy 需要在用户和帖子之间的数据库上找到一个外键。如果你不想更新你的数据库架构,你可以定义一个连接,就像你在 follow 上所做的那样,sqlalchemy 可以做一个连接来猜测与用户相关的帖子是什么。

你的情况可能是:

posts = db.relationship('Post', backref='author', lazy='dynamic',
                        primaryjoin="User.id == Post.user_id")

这是有关它的文档:

http://docs.sqlalchemy.org/en/latest/orm/join_conditions.html#specifying-alternate-join-conditions

更新:

我看到实际上 Post.user_id 定义了一个外键。也许sql server没有创建外键?无论如何,我上面所说的可能是一种解决方法。

【讨论】:

尝试了这个潜在的解决方案。我现在遇到一个稍微不同的错误。我仍然不知道如何解决。感谢您的尝试! “与列 'post.user_id' 关联的外键找不到表 'user' 用于生成目标列 'id' 的外键” 那是因为实际的表叫users

以上是关于Flask 和 SQLalchemy NoForeignKeysError:无法确定关系 User.posts 上的父/子表之间的连接条件 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

Flask 学习-19.配置管理flask_sqlalchemy 和 flask_migrate

Python flask-sqlalchemy初级解析

flask-sqlalchemy

11.7 Flask flask-sqlalchemy组件

FLASK-SQLALCHEMY如何使用or和and条件进行组合查询

Flask-SQLAlchemy安装及设置