如何使用 SQLAlchemy 使用多个值初始化关联对象的字段?
How to initialize an association object's field with multiple values using SQLAlchemy?
上下文
我有三个 table、Users
、Subreddits
和 Keywords
。思路是users
可以监控多个subreddits
,subreddits
可以监控多个keywords
.
由于一个用户可以监控多个subreddit,一个subreddit可以有多个用户监控,我想在Users
和Subreddits
之间建立多对多关系。
同理,由于一个subreddit可以监控多个关键词,一个关键词可以被多个subreddit监控,我希望Subreddits
和[=18之间是多对多的关系=].
我使用的是 association object
而不是 2 association tables
,因为我有 3 个 table 想要相互关联,而关联 table 仅适用于 2 tables.
之间的关系
实施
以下是我如何实现每个table和关联对象:
用户
class User(db.Model, JsonSerializer):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(128), index=True, unique=True)
password_hash = db.Column(db.String(128))
phone_num = db.Column(db.String(64), index=True, unique=True)
received_posts = db.Column(db.String(128), index=True, unique=True)
monitor = db.relationship('Monitor', back_populates='user')
//...
子版块
class Subreddit(db.Model, JsonSerializer):
__tablename__ = 'subreddits'
id = db.Column(db.Integer, primary_key=True)
subreddit_name = db.Column(db.String(128), index=True)
// ...
关键字
class Keyword(db.Model, JsonSerializer):
__tablename__ = 'keywords'
id = db.Column(db.Integer, primary_key=True)
keyword = db.Column(db.String(128), index=True)
monitor = db.relationship('Monitor', back_populates='keyword')
// ...
关联对象:
class Monitor(db.Model):
__table_name__ = 'monitors'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=True)
subreddit_id = db.Column(db.Integer, db.ForeignKey("subreddits.id"), nullable=True)
keyword_id = db.Column(db.Integer, db.ForeignKey("keywords.id"), nullable=True)
user = db.relationship('User', back_populates='monitor')
subreddit = db.relationship('Subreddit')
keyword = db.relationship('Keyword', back_populates='monitor')
我想做什么
理想情况下,每次 user
跟踪一个 subreddit
和相应的多个 keywords
,所有这些信息都存储到一个 monitor
对象中。
例如,
user1
监控 subreddit1
,它跟踪 keyword1
和 keyword2
。
- 所有这些关系都应存储在
monitor1
中。
这应该导致:
- monitor1.user =
user1
- monitor1.subreddit =
subreddit1
- monitor1.keyword =
keyword1, keyword2
我尝试过的
方法一
正在用 keywords
列表初始化一个 monitor
实例。
# Parse the incoming data.
incoming = request.get_json()
subreddit_name = incoming["subredditName"]
subreddit_keywords = incoming["subredditKeywords"]
logged_in_user_id = incoming["id"]
# Query the User.
user = User.query.get(logged_in_user_id)
# Query the Subreddit.
subreddit = Subreddit.query.filter_by(subreddit_name=subreddit_name).first()
# Create a list of Keywords.
monitor_keywords = []
for kw in subreddit_keywords:
# check if Keyword objects are in the database already
if Keyword.query.filter_by(keyword=kw).first() is not None:
keyword = Keyword.query.filter_by(keyword=kw).first()
else: # create new Keyword objects
keyword = Keyword(kw)
monitor_keywords.append(keyword)
# Create the monitoring1 association object.
monitor1 = Monitor(user=user, subreddit=subreddit, keyword=monitor_keywords) <----- Problem
db.session.add(monitor1)
db.session.commit()
我无法用 keyword=monitor_keywords
初始化 monitor1
。
我收到以下错误:
AttributeError: 'list' object has no attribute '_sa_instance_state'
。
Stack Overflow 上的其他提交与我的特定错误案例无关。
方法#2 到#5
我试过先初始化一个 monitor
对象,然后向其附加多个 keywords
但这也没有用。
# Query the User
user = User.query.get(logged_in_user_id)
# Create the association object
monitor = Monitor(user=user, subreddit=None, keyword=None)
db.session.add(monitor)
// ...
for kw in subreddit_keywords:
# check if Keyword objects are in the database already
if Keyword.query.filter_by(keyword=kw).first() is not None:
keyword = Keyword.query.filter_by(keyword=kw).first()
else: # create new Keyword objects
keyword = Keyword(kw)
# Initialize monitor.keyword so we can append to it.
if (monitor.keyword is None):
monitor.keyword = keyword
else: #All failed attempts to append to monitor.keyword.
# monitor.keyword.append(keyword) <- Method #2
# keyword.append(monitor) <- Method #3
# monitor.append(keyword) <- Method #4
# user.monitor.append(keyword) <- Method #5
问题
当我初始化一个关联对象时,我需要用多个keywords
来初始化它。
无论是用keywords
初始化关联对象,还是稍后用keywords
更新关联对象,都不起作用。理想情况下,我想坚持使用方法 #1 的方法,但我愿意接受其他建议。
我对你的要求感到困惑,但我认为在这种情况下,你的意思似乎是让用户在特定的 subreddit 中收听特定的关键字?这意味着关联对象将跟踪每一个。为了简化复数形式,也许是 Monitor。对于列外键,用单数名称命名它们是有意义的。与复数或单数的关系与复数的 backref 形成对比。
class Monitor(db.Model):
__table_name__ = 'monitors'
id = db.Column(db.Integer, primary_key=True)
# Singular
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=True)
# Singular
subreddit_id = db.Column(db.Integer, db.ForeignKey("subreddits.id"), nullable=True)
# Singular
keyword_id = db.Column(db.Integer, db.ForeignKey("keywords.id"), nullable=True)
# Backref is all monitors for user across ALL subreddits and keywords.
user = db.relationship('User', backref='monitors')
# Backref is all monitors for subreddit across ALL users and keywords.
subreddit = db.relationship('Subreddit', backref='monitors')
# Backref is all monitors for keyword across ALL users and subreddits.
keyword = db.relationship('Keyword', backref='monitors')
# Parse the incoming data.
incoming = request.get_json()
subreddit_name = incoming["subredditName"]
subreddit_keywords = incoming["subredditKeywords"]
logged_in_user_id = incoming["id"]
# Query the User.
user = User.query.get(logged_in_user_id)
# Query the Subreddit.
subreddit = Subreddit.query.filter_by(subreddit_name=subreddit_name).first()
# Create a list of Keywords.
for kw in subreddit_keywords:
# Create Keyword in database (this is another problem if you want the names to be unique)
keyword = Keyword(keyword=kw)
# The subreddit and keyword this user is watching.
db.session.add(Monitor(user=user, subreddit=subreddit, keyword=keyword))
db.session.commit()
# All subreddit/keyword pairs being watched by this user.
monitors = db.query(Monitor).filter(Monitor.user == user).all()
# All keywords being watched for this subreddit for this user
monitored_kws = db.query(Keyword).join(Keyword.monitors).filter(Monitor.user == user, Monitor.subreddit == subreddit).all()
上下文
我有三个 table、Users
、Subreddits
和 Keywords
。思路是users
可以监控多个subreddits
,subreddits
可以监控多个keywords
.
由于一个用户可以监控多个subreddit,一个subreddit可以有多个用户监控,我想在Users
和Subreddits
之间建立多对多关系。
同理,由于一个subreddit可以监控多个关键词,一个关键词可以被多个subreddit监控,我希望Subreddits
和[=18之间是多对多的关系=].
我使用的是 association object
而不是 2 association tables
,因为我有 3 个 table 想要相互关联,而关联 table 仅适用于 2 tables.
实施
以下是我如何实现每个table和关联对象:
用户
class User(db.Model, JsonSerializer):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(128), index=True, unique=True)
password_hash = db.Column(db.String(128))
phone_num = db.Column(db.String(64), index=True, unique=True)
received_posts = db.Column(db.String(128), index=True, unique=True)
monitor = db.relationship('Monitor', back_populates='user')
//...
子版块
class Subreddit(db.Model, JsonSerializer):
__tablename__ = 'subreddits'
id = db.Column(db.Integer, primary_key=True)
subreddit_name = db.Column(db.String(128), index=True)
// ...
关键字
class Keyword(db.Model, JsonSerializer):
__tablename__ = 'keywords'
id = db.Column(db.Integer, primary_key=True)
keyword = db.Column(db.String(128), index=True)
monitor = db.relationship('Monitor', back_populates='keyword')
// ...
关联对象:
class Monitor(db.Model):
__table_name__ = 'monitors'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=True)
subreddit_id = db.Column(db.Integer, db.ForeignKey("subreddits.id"), nullable=True)
keyword_id = db.Column(db.Integer, db.ForeignKey("keywords.id"), nullable=True)
user = db.relationship('User', back_populates='monitor')
subreddit = db.relationship('Subreddit')
keyword = db.relationship('Keyword', back_populates='monitor')
我想做什么
理想情况下,每次 user
跟踪一个 subreddit
和相应的多个 keywords
,所有这些信息都存储到一个 monitor
对象中。
例如,
user1
监控subreddit1
,它跟踪keyword1
和keyword2
。- 所有这些关系都应存储在
monitor1
中。
这应该导致:
- monitor1.user =
user1
- monitor1.subreddit =
subreddit1
- monitor1.keyword =
keyword1, keyword2
我尝试过的
方法一
正在用 keywords
列表初始化一个 monitor
实例。
# Parse the incoming data.
incoming = request.get_json()
subreddit_name = incoming["subredditName"]
subreddit_keywords = incoming["subredditKeywords"]
logged_in_user_id = incoming["id"]
# Query the User.
user = User.query.get(logged_in_user_id)
# Query the Subreddit.
subreddit = Subreddit.query.filter_by(subreddit_name=subreddit_name).first()
# Create a list of Keywords.
monitor_keywords = []
for kw in subreddit_keywords:
# check if Keyword objects are in the database already
if Keyword.query.filter_by(keyword=kw).first() is not None:
keyword = Keyword.query.filter_by(keyword=kw).first()
else: # create new Keyword objects
keyword = Keyword(kw)
monitor_keywords.append(keyword)
# Create the monitoring1 association object.
monitor1 = Monitor(user=user, subreddit=subreddit, keyword=monitor_keywords) <----- Problem
db.session.add(monitor1)
db.session.commit()
我无法用 keyword=monitor_keywords
初始化 monitor1
。
我收到以下错误:
AttributeError: 'list' object has no attribute '_sa_instance_state'
。
Stack Overflow 上的其他提交与我的特定错误案例无关。
方法#2 到#5
我试过先初始化一个 monitor
对象,然后向其附加多个 keywords
但这也没有用。
# Query the User
user = User.query.get(logged_in_user_id)
# Create the association object
monitor = Monitor(user=user, subreddit=None, keyword=None)
db.session.add(monitor)
// ...
for kw in subreddit_keywords:
# check if Keyword objects are in the database already
if Keyword.query.filter_by(keyword=kw).first() is not None:
keyword = Keyword.query.filter_by(keyword=kw).first()
else: # create new Keyword objects
keyword = Keyword(kw)
# Initialize monitor.keyword so we can append to it.
if (monitor.keyword is None):
monitor.keyword = keyword
else: #All failed attempts to append to monitor.keyword.
# monitor.keyword.append(keyword) <- Method #2
# keyword.append(monitor) <- Method #3
# monitor.append(keyword) <- Method #4
# user.monitor.append(keyword) <- Method #5
问题
当我初始化一个关联对象时,我需要用多个keywords
来初始化它。
无论是用keywords
初始化关联对象,还是稍后用keywords
更新关联对象,都不起作用。理想情况下,我想坚持使用方法 #1 的方法,但我愿意接受其他建议。
我对你的要求感到困惑,但我认为在这种情况下,你的意思似乎是让用户在特定的 subreddit 中收听特定的关键字?这意味着关联对象将跟踪每一个。为了简化复数形式,也许是 Monitor。对于列外键,用单数名称命名它们是有意义的。与复数或单数的关系与复数的 backref 形成对比。
class Monitor(db.Model):
__table_name__ = 'monitors'
id = db.Column(db.Integer, primary_key=True)
# Singular
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=True)
# Singular
subreddit_id = db.Column(db.Integer, db.ForeignKey("subreddits.id"), nullable=True)
# Singular
keyword_id = db.Column(db.Integer, db.ForeignKey("keywords.id"), nullable=True)
# Backref is all monitors for user across ALL subreddits and keywords.
user = db.relationship('User', backref='monitors')
# Backref is all monitors for subreddit across ALL users and keywords.
subreddit = db.relationship('Subreddit', backref='monitors')
# Backref is all monitors for keyword across ALL users and subreddits.
keyword = db.relationship('Keyword', backref='monitors')
# Parse the incoming data.
incoming = request.get_json()
subreddit_name = incoming["subredditName"]
subreddit_keywords = incoming["subredditKeywords"]
logged_in_user_id = incoming["id"]
# Query the User.
user = User.query.get(logged_in_user_id)
# Query the Subreddit.
subreddit = Subreddit.query.filter_by(subreddit_name=subreddit_name).first()
# Create a list of Keywords.
for kw in subreddit_keywords:
# Create Keyword in database (this is another problem if you want the names to be unique)
keyword = Keyword(keyword=kw)
# The subreddit and keyword this user is watching.
db.session.add(Monitor(user=user, subreddit=subreddit, keyword=keyword))
db.session.commit()
# All subreddit/keyword pairs being watched by this user.
monitors = db.query(Monitor).filter(Monitor.user == user).all()
# All keywords being watched for this subreddit for this user
monitored_kws = db.query(Keyword).join(Keyword.monitors).filter(Monitor.user == user, Monitor.subreddit == subreddit).all()