Flask-SQLAlchemy Query Join relational tables
The error message is telling you that SQLAlchemy can't determine how to join the two tables users
and friendships
, because there is more than one foreign key linking them. You need to explicitly define the join condition.
Try:
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)
Ok looks like after getting some sleep and viewing Matthewh's suggestion I almost found the final solution:
My model:
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)
My app.py function(shows friends of user logged in):
@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)
And the Jinja side of 'friends.html':
{% 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 %}
This gives me an object(friends in userList.items) like this:
([email protected], 2, u'Carlos', u'[email protected]', 2, 2, 1)
I was expecting this: |users.id|users.userName|users.userEmail|users.userPhone|friendships.id|friendships.user_id(the friends)| friendships.friend_id(the logged in user)|
So I have the following doubts/questions:
I am not completely understanding the structure of the object resulting for the query:
-repeated user id, name and email -what is the 'u' infront of second name and email
I am not completely understanding the relations model structure:
why is the following NOT required in the 'users' class of the database model:
#friendsR = db.relationship('friendships', backref='friendships.friend_id', primaryjoin='users.id==friendships.user_id', lazy='joined')
IS my database model correctly defined regarding standarized relationship model as posted in this answer or should I improove some how??