Flask&SQLAlchemyを使用してアプリを構築しています。基本的に、ユーザー、友情、およびbestFriendsの3つのテーブルがあります。
ユーザーは多くの友人を持つことができますが、親友は1人だけです。だから私は自分のモデルをリレーショナルにしたい。 「ユーザー」と「友情」の関係は「1対多」、「ユーザー」と「ベストフレンド」は「1対1」。
これは私のモデルです:
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ステートメントを表示します
エラーメッセージは、SQLAlchemyが2つのテーブルusers
とfriendships
を結合する方法を決定できないことを示しています。これらは複数の外部キーがリンクしているためです。結合条件を明示的に定義する必要があります。
試してください:
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)
寝てマシューの提案を見て、最終的な解決策をほとんど見つけたようです。
私のモデル:
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の友達)が得られます。
([email protected], 2, u'Carlos', u'[email protected]', 2, 2, 1)
私はこれを期待していました:| users.id | users.userName | users.userEmail | users.userPhone | friendships.id | friendships.user_id(the friends)| friendships.friend_id(ログインしているユーザー)|
だから私は次の疑問/疑問を持っています:
クエリの結果オブジェクトの構造を完全に理解していません。
-ユーザーID、名前、メールの繰り返し-2番目の名前とメールの前にある「u」
関係モデルの構造を完全に理解していない:
データベースモデルの 'users'クラスでは、次が必要ないのはなぜですか。
#friendsR = db.relationship('friendships', backref='friendships.friend_id', primaryjoin='users.id==friendships.user_id', lazy='joined')
この回答に掲載されている標準化された関係モデルに関してデータベースモデルが正しく定義されていますか、それとも何らかの改善が必要ですか?