【发布时间】:2019-03-09 22:19:18
【问题描述】:
请查看底部更新
我有三门课。我们称它们为Post、PostVersion 和Tag。 (这是针对 Web 应用程序中的内部版本控制系统,可能类似于 StackOverflow,尽管我不确定它们的实施策略)。我有点使用 git 中的术语来理解它。出于此问题的目的,这些是类的高度简化版本:
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
author_id = db.Column(db.Integer, db.ForeignKey("user.id"))
author = db.relationship("User", backref="posts")
head_id = db.Column(db.Integer, db.ForeignKey("post_version.id"))
HEAD = db.relationship("PostVersion", foreign_keys=[head_id])
added = db.Column(db.DateTime, default=datetime.utcnow)
class PostVersion(db.Model):
id = db.Column(db.Integer, primary_key=True)
editor_id = db.Column(db.Integer, db.ForeignKey("user.id"))
editor = db.relationship("User")
previous_id = db.Column(db.Integer, db.ForeignKey("post_version.id"), default=None)
previous = db.relationship("PostVersion")
pointer_id = db.Column(db.Integer, db.ForeignKey("post.id"))
pointer = db.relationship("Post", foreign_keys=[pointer_id])
post = db.Column(db.Text)
modified = db.Column(db.DateTime, default=datetime.utcnow)
tag_1_id = db.Column(db.Integer, db.ForeignKey("tag.id"), default=None)
tag_2_id = db.Column(db.Integer, db.ForeignKey("tag.id"), default=None)
tag_3_id = db.Column(db.Integer, db.ForeignKey("tag.id"), default=None)
tag_4_id = db.Column(db.Integer, db.ForeignKey("tag.id"), default=None)
tag_5_id = db.Column(db.Integer, db.ForeignKey("tag.id"), default=None)
tag_1 = db.relationship("Tag", foreign_keys=[tag_1_id])
tag_2 = db.relationship("Tag", foreign_keys=[tag_2_id])
tag_3 = db.relationship("Tag", foreign_keys=[tag_3_id])
tag_4 = db.relationship("Tag", foreign_keys=[tag_4_id])
tag_5 = db.relationship("Tag", foreign_keys=[tag_5_id])
class Tag(db.Model):
id = db.Column(db.Integer, primary_key=True)
tag = db.Column(db.String(128))
为了创建一个新帖子,我创建了一个帖子和一个初始 PostVersion,Post.head_id 指向它。每次进行编辑时,都会创建一个新的PostVersion 指向以前的PostVersion,并将Post.head_id 重置为指向新的PostVersion。要将发布版本重置为早期版本——好吧,我还没有做到这一点,但是复制之前的版本或者只是将指针重置为之前的版本似乎很简单。
不过,我的问题是:如何在 Post 和 Tag 之间写出这样的关系
-
Post.tags将是当前PostVersion包含的所有标签的列表,并且 -
Tag.posts将是当前具有该特定标签的所有Post的列表?
第一个条件似乎很简单,一个简单的方法
def get_tags(self):
t = []
if self.HEAD.tag_1:
t.append(self.HEAD.tag_1)
if self.HEAD.tag_2:
t.append(self.HEAD.tag_2)
if self.HEAD.tag_3:
t.append(self.HEAD.tag_3)
if self.HEAD.tag_4:
t.append(self.HEAD.tag_4)
if self.HEAD.tag_5:
t.append(self.HEAD.tag_5)
return t
这个技巧现在还不错,但第二个条件现在对我来说几乎是棘手的。我目前在 Tag 中使用了一种令人讨厌的方法,我使用or_ 过滤器查询所有带有标签的PostVersion:
def get_posts(self):
edits = PostVersion.query.filter(or_(
PostVersion.tag_1_id==self.id,
PostVersion.tag_2_id==self.id,
PostVersion.tag_3_id==self.id,
PostVersion.tag_4_id==self.id,
PostVersion.tag_5_id==self.id,
).order_by(PostVersion.modified.desc()).all()
posts = []
for e in edits:
if self in e.pointer.get_tags() and e.pointer not in posts:
posts.append(e.pointer)
return posts
这非常低效,我无法对结果进行分页。
我知道这将是从 Post 到 Tag 或 Tag 到 Post 到 PostVersion 的辅助连接,但它必须是 or 上的辅助连接,我不知道怎么开始写。
回顾我的代码,我开始想知道为什么其中一些关系需要定义foreign_keys 参数,而另一些则不需要。我认为这与它们的定义位置有关(是否紧跟在 FK id 列之后)并注意到有一个 foreign_keys 的列表,我在想 这就是我可以如何定义它。但我不确定如何追求这一点。
我现在也想知道是否可以通过配置良好的关系免除PostVersion 上的pointer_id。然而,这与问题无关(尽管循环引用确实令人头疼)。
作为参考,我正在使用 Flask-SQLAlchemy、Flask-migrate 和 MariaDB。我非常关注Miguel Grinberg's Flask Megatutorial。
任何帮助或建议都是天赐之物。
更新
我设计了以下 有效的 mysql 查询,现在我需要将它翻译成 sqlalchemy:
SELECT
post.id, tag.tag
FROM
post
INNER JOIN
post_version
ON
post.head_id=post_version.id
INNER JOIN
tag
ON
post_version.tag_1_id=tag.id OR
post_version.tag_2_id=tag.id OR
post_version.tag_3_id=tag.id OR
post_version.tag_4_id=tag.id OR
post_version.tag_5_id=tag.id OR
WHERE
tag.tag="<tag name>";
【问题讨论】:
标签: python sqlalchemy