跟踪会员标记的艺术家

时间:2014-04-05 00:19:02

标签: python sqlalchemy flask

我正在为自己的娱乐和学习体验编写一个基于网络的自动点唱机。

我终于完成了我的模型,除了一个重要组成部分:成员类型标签。我知道我必须联系三个模型,并且它涉及使用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。

简而言之,我试图建立一个关联代理来创建一个列表的字典,我完全迷失了。我不确定我应该代理什么值,如果使用一个中间表使这个复杂化,以及如何构建二级(可能是三级)中间表

1 个答案:

答案 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子句。)