我正在为自己的娱乐和学习体验编写一个基于网络的自动点唱机。
我终于完成了我的模型,除了一个重要组成部分:成员类型标签。我知道我必须联系三个模型,并且它涉及使用association_proxy
和集合类。
这里是相关的模型(我有一个抽象的模型来处理声明id和name字段,但这会引起我稍后会看到的问题):
class Member(db.Model):
__tablename__ = 'members'
id = db.Column('id', db.Integer, primary_key=True)
name = db.Column('name', db.String(128))
##tagged_artists is a backref from MemberTaggedArtist
tags = association_proxy('tagged_artists', 'artist',
creator=lambda k,v: MemberTaggedArtist(tag=k, artist=v)
)
class Artist(db.Model):
__tablename__ = 'artists'
id = db.Column('id', db.Integer, primary_key=True)
name = db.Column('name', db.String(128))
class Tag(db.Model):
__tablename__ = 'tags'
id = db.Column('id', db.Integer, primary_key=True)
name = db.Column('name', db.String(128))
class MemberTaggedArtist(db.Model):
__tablename__ = 'membertaggedartists'
id = db.Column('id', db.Integer, primary_key=True)
member_id = db.Column('member_id', db.Integer, db.ForeignKey('members.id'))
artist_id = db.Column('artist_id', db.Integer, db.ForeignKey('artists.id'))
tag_id = db.Column('tag_id', db.Integer, db.ForeignKey('tags.id'))
member = db.relationship(Member, backref=db.backref(
'tagged_artists',
collection_class=attribute_mapped_collection('tag')
)
artist = db.relationship(Artist, backref='applied_tags')
tag = db.relationship(Tag, backref='applied_artists')
>>> member = Member(name='justanr')
>>> artist = Artist(name='Gorguts')
>>> tag = Tag('Death Metal')
>>> member.tags['Death Metal'].append('Gorguts')
>>> member.tags
... {'Death Metal':['Gorguts']}
当前发生了什么(注意,我构建了一个mixin来处理repr调用):
>>> member.tags
... {Tag (ID:1 Name:Death Metal): MemberTaggedArtist (ID: 1 Member:justanr Artist:Gorguts Tag:Death Metal)}
我还没有和association_proxy
一起工作足够长的时间来理解我做错了什么,甚至文档中的简短教程也给了我一些问题(我不知道为什么我并不认为这是因为我使用的是Flask-SQLAlchemy。
简而言之,我试图建立一个关联代理来创建一个列表的字典,我完全迷失了。我不确定我应该代理什么值,如果使用一个中间表使这个复杂化,以及如何构建二级(可能是三级)中间表
答案 0 :(得分:0)
我终于想出了一个解决方案。我忘了我对SQL的了解......哈哈,我猜?但我最近看了几次关于SQLA的讨论让我意识到我有一个' O'问题 - 我专注于ORM的Object部分而不是思考" SQLA只是让编写查询更容易,它不会为我做。"它也没有帮助FSQLA的查询对象让我自满。不要误解我,这对于基本的查询和连接来说非常棒,但是对于这个复杂的东西......不是真的。
我也没有意识到我的问题实际上是几个问题。一部分是"如何使用SQLA的工具编写此查询?"另一个是,"帮助我解决正确的association_proxy
以获得理想的结果。"
我还不确定它的关联代理部分,或者我是否想再这样做了。但我终于解开了第一个问题的解决方案:
# Query database for tag names and count for a particular artist
artist = models.Artist.find_by_some_means(magic, vars)
q = db.session.query(
models.Tag.id.label('id'),
models.Tag.name.label('tag'),
db.func.count(models.MemberTaggedArtist.member_id).label('count')
)
q = q.join(models.MemberTaggedArtist, models.Tag.id == models.MemberTaggedArtist.tag_id)
q = q.filter(models.MemberTaggedArtist.artist_id == artist.id)
q = q.group_by(models.MemberTaggedArtist.tag_id)
# order by tag count first, then tag name if two tags happen to have the same count
# there's probably a much better way to get there without recalculating the result
q = q.order_by(db.desc(db.func.count(models.MemberTaggedArtist.member_id)))
q = q.order_by(models.Tag.name)
我最初的计划查询是:
SELECT tags.name AS tag, tags.id AS id, count(mta.member_id) AS count
FROM tags, membertaggedartists AS mta
WHERE tags.id = mta.tag_id AND mta.artist_id = :artist_id
GROUP BY id
ORDER BY count DESC, name ASC
SQLA将我的请求解释为:
SELECT tags.id AS id, tags.name AS tag, count(membertaggedartists.member_id) AS count
FROM tags
JOIN membertaggedartists ON tags.id = membertaggedartists.tag_id
WHERE membertaggedartists.artist_id = :artist_id_1
GROUP BY membertaggedartists.tag_id
ORDER BY count(membertaggedartists.member_id) DESC, tags.name
这非常相似(并使用与我不同的显式连接,这使我无需使用我原来的WHERE子句。)