Flask-SQLAlchemy查询连接关系表

时间:2015-01-12 10:37:20

标签: join flask sqlalchemy flask-sqlalchemy

我正在使用Flask& amp; SQLAlchemy的。我基本上有3个表:用户,友谊和bestFriends:

用户可以有很多朋友但只有一个最好的朋友。所以我希望我的模型是关系型的。 '一对多'用于'用户'和'友谊'之间的关系& '用户'和'bestFriends'之间的'一对一'。

这是我的模特:

from app import db
from sqlalchemy.orm import relationship, backref
from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.ext.declarative import declarative_base

class users(db.Model):

    __tablename__ = "Users"

    id = db.Column(db.Integer, primary_key=True)
    userName = db.Column(db.String, nullable=False)
    userEmail = db.Column(db.String, nullable=False)
    userPhone = db.Column(db.String, nullable=False)
    userPass = db.Column(db.String, nullable=False)

    friendsR = db.relationship('friendships', backref='friendships.friend_id', primaryjoin='users.id==friendships.user_id', lazy='joined')

    def __init__(self, userName, userEmail, userPhone, userPass):
        self.userName = userName
        self.userEmail = userEmail
        self.userPhone = userPhone
        self.userPass = userPass

    def __repr__(self):
        return '{}-{}-{}-{}'.format(self.id, self.userName, self.userEmail, self.userPhone)

class friendships(db.Model):

    __tablename__ = "Friendships"

    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, ForeignKey('Users.id'), nullable=False)
    friend_id = db.Column(db.Integer, ForeignKey('Users.id'), nullable=False)

    userR = relationship('users', foreign_keys='friendships.user_id')
    friendR = relationship('users', foreign_keys='friendships.friend_id')

    def __init__(self, user_id, friend_id):
        self.user_id = user_id
        self.friend_id = friend_id


    def __repr__(self):
        return '{}-{}-{}-{}'.format(self.user_id, self.friend_id)


class bestFriends(db.Model):

    __tablename__ = "BestFriends"

    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, ForeignKey('Users.id'), nullable=False)
    best_friend_id = db.Column(db.Integer, ForeignKey('Users.id'), nullable=False)

    user = relationship('users', foreign_keys='bestFriends.user_id')
    best_friend = relationship('users', foreign_keys='bestFriends.best_friend_id')


    def __init__(self, user_id, best_friend_id):

        self.user_id = user_id
        self.best_friend_id = best_friend_id


    def __repr__(self):
        return '{}-{}-{}-{}'.format(self.user_id, self.best_friend_id)

我需要能够查询登录用户的朋友列表以及该用户的最好朋友(如果存在)。我还需要找到结果:

这是我的app.py函数,用于显示用户的朋友:

@app.route('/friendList<int:page>', methods=['GET', 'POST'])
@app.route('/friends')
def friendList(page=1):

if not session.get('logged_in'):
        return render_template('login.html')
    else:
        userID = session['user_id']

        userList = users.query.join(friendships).add_columns(users.id, users.userName, users.userEmail, friendships.user_id, friendships.friend_id).filter(users.id == friendships.friend_id).filter(friendships.user_id == userID).paginate(page, 1, False)

        return render_template(
            'friends.html', userList=userList)

这将是代码的Jinja方面:

{% extends "layout.html" %}
{% block body %}

<div id="pagination">
{% if userList.has_prev %}
        <a href="{{ url_for('friendList', page=userList.prev_num) }}">Back</a>
{% endif %} 

{% if userList.has_next %}
        <a href="{{ url_for('friendList', page=userList.next_num) }}">Next</a>
{% endif %}
</div>

<div style="clear:both;"></div>

<div id="innerContent">
{% if userList %}
    {% for friends in userList %}
                    <div class="contentUsers">
                        {{ friends.userName }}
                    </div>

                    <br><br><br><br>

    {% endfor %}{% else %}<div>No friends</div>
{% endif %} 

</div>
  {% endblock %}

如果我这样查询:

userList = db.session.query(users,friendships).filter(users.id == friendships.friend_id).filter(friendships.user_id == userID).paginate(page, 1, False)

我收到此错误:

InvalidRequestError: Could not find a FROM clause to join from. Tried joining to <class 'models.friendships'>, but got: Can't determine join between 'Users' and 'Friendships'; tables have more than one foreign key constraint relationship between them. Please specify the 'onclause' of this join explicitly.

如果我这样查询:

userList = users.query.join(friendships, users.id==friendships.user_id).add_columns(users.id, users.userName, users.userEmail, friendships.user_id, friendships.friend_id).filter(users.id == friendships.friend_id).filter(friendships.user_id == userID).paginate(page, 1, False)

我收到以下错误:

TypeError: 'Pagination' object is not iterable

我仍然认为以后的查询是正确的方法,但我认为我的关系/表之间的外键有问题!!!

如果在Jinja方面我将.items添加到循环中:

{% if userList.items %}
{% for friends in userList.items %}

                <div class="contentUsers">
                    {{ friends.userName }}
                </div>

                <br><br><br><br>

{% endfor %}{% else %}<div>No friends</div>

{%endif%}

它根本不循环,只显示“没有朋友”的else语句

2 个答案:

答案 0 :(得分:32)

错误消息告诉您SQLAlchemy无法确定如何连接两个表usersfriendships,因为有多个外键链接它们。您需要明确定义连接条件。

尝试:

userList = users.query.join(friendships, users.id==friendships.user_id).add_columns(users.userId, users.name, users.email, friends.userId, friendId).filter(users.id == friendships.friend_id).filter(friendships.user_id == userID).paginate(page, 1, False)

答案 1 :(得分:1)

好的看起来就像睡觉后看到Matthewh的建议我几乎找到了最终的解决方案:

我的模特:

from app import db
from sqlalchemy.orm import relationship, backref
from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.ext.declarative import declarative_base

class users(db.Model):
    __tablename__ = "Users"

    id = db.Column(db.Integer, primary_key=True)
    userName = db.Column(db.String, nullable=False)
    userEmail = db.Column(db.String, nullable=False)
    userPhone = db.Column(db.String, nullable=False)
    userPass = db.Column(db.String, nullable=False)

    def __init__(self, userName, userEmail, userPhone, userPass):
        self.userName = userName
        self.userEmail = userEmail
        self.userPhone = userPhone
        self.userPass = userPass

    def __repr__(self):
        return '{}-{}-{}-{}'.format(self.id, self.userName, self.userEmail, self.userPhone)


 class friendships(db.Model):
    __tablename__ = "Friendships"

    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('Users.id'), nullable=False)
    friend_id = db.Column(db.Integer, db.ForeignKey('Users.id'), nullable=False)
    userR = db.relationship('users', foreign_keys='friendships.user_id')
    friendR = db.relationship('users', foreign_keys='friendships.friend_id')

    def __init__(self, user_id, friend_id):
        self.user_id = user_id
        self.friend_id = friend_id

    def __repr__(self):
        return '{}-{}-{}-{}'.format(self.user_id, self.friend_id)


class bestFriends(db.Model):
    __tablename__ = "BestFriends"

    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('Users.id'), nullable=False)
    best_friend_id = db.Column(db.Integer, db.ForeignKey('Users.id'), nullable=False)
    user = db.relationship('users', foreign_keys='bestFriends.user_id')
    best_friend = db.relationship('users', foreign_keys='bestFriends.best_friend_id')

    def __init__(self, user_id, best_friend_id):
        self.user_id = user_id
        self.best_friend_id = best_friend_id

    def __repr__(self):
        return '{}-{}-{}-{}'.format(self.user_id, self.best_friend_id)

我的app.py功能(显示用户登录的朋友):

@app.route('/friendList<int:page>', methods=['GET', 'POST'])
@app.route('/friends')
  def friendList(page=1):
  if not session.get('logged_in'):
     return render_template('login.html')
  else:
     userID = session['user_id']
     userList = users.query.join(friendships, users.id==friendships.user_id).add_columns(users.id, users.userName, users.userEmail, friendships.id, friendships.user_id, friendships.friend_id).filter(friendships.friend_id == userID).paginate(page, 1, False)
     return render_template('friends.html', userList=userList)

'friends.html'的Jinja方面:

{% extends "layout.html" %}

{% block body %}
    <div id="pagination">
        {% if userList.has_prev %}
            <a href="{{ url_for('friendList', page=userList.prev_num)}}">Back</a>
        {% endif %}

        {% if userList.has_next %}
            <a href="{{ url_for('friendList', page=userList.next_num)}}">Next</a>
        {% endif %}
    </div>

    <div style="clear:both;"></div>

    <div id="innerContent">
    {% if userList.items %}
        {% for friends in userList.items %}
            <div class="contentUsers">
                {{ friends.userName }}
            </div>
            <br><br><br><br>
        {% endfor %}
        {% else %}
            <div>No friends</div>
        {% endif %}
    </div>
{% endblock %}

这给了我一个对象(userList.items中的朋友),如下所示:

(2-Carlos-carlos@carlos.com-900102030, 2, u'Carlos', u'carlos@carlos.com', 2, 2, 1)

我在期待这个:     | users.id | users.userName | users.userEmail | users.userPhone | friendships.id | friendships.user_id(朋友)| friendships.friend_id(登录用户)|

所以我有以下疑问/问题:

我并不完全理解查询产生的对象结构:

- 重复的用户ID,姓名和电子邮件 - 第二个名字和电子邮件的“你”是什么?

我不完全理解关系模型结构:

  • 为什么数据库模型的'users'类中不需要以下内容:

    #friendsR = db.relationship('friendships', backref='friendships.friend_id', primaryjoin='users.id==friendships.user_id', lazy='joined')
    

我的数据库模型是否正确定义了关于标准化关系模型的答案,或者我应该改进一些方法吗?