Пожалуйста, смотрите обновление внизу
У меня есть три класса.Давайте назовем их Post
, PostVersion
и Tag
.(Это для внутренней системы контроля версий в веб-приложении, возможно, похожей на 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, где я запрашиваю все PostVersion
с тегом, используя фильтр or_
:
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
, но это должно быть вторичное соединение для или, и у меня естьПонятия не имею, как вообще начать писать это.
Оглядываясь назад на мой код, я начинаю задумываться, почему некоторые из этих отношений требуют определения параметра foreign_keys
, а другие - нет.Я думаю, что это связано с тем, где они определены (сразу после столбца идентификатора FK или нет) и замечая, что есть список для foreign_keys
, я думаю это , как я мог бы его определить,Но я не уверен, как добиться этого.
Мне также интересно теперь, могу ли я обойтись без pointer_id
на PostVersion
с хорошо настроенными отношениями.Это, однако, не имеет отношения к вопросу (хотя циклическая ссылка вызывает головные боли).
Для справки, я использую Flask-SQLAlchemy, Flask-migrate и MariaDB.Я очень следую Мегуторию Мигеля Гринберга "Настой" .
Любая помощь или совет будут находкой.
ОБНОВЛЕНИЕ
Я разработал следующий запрос 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>";