Skip to content

Instantly share code, notes, and snippets.

@pilosus
Created November 4, 2016 12:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pilosus/e369d11a08079c557a1eb9dbe657a0f2 to your computer and use it in GitHub Desktop.
Save pilosus/e369d11a08079c557a1eb9dbe657a0f2 to your computer and use it in GitHub Desktop.
Comment SQLAlchemy model
class Comment(db.Model):
"""Comment is a message under a post.
Comment that has a parent treated as a reply. Comment with replies
(children) represents n-ary tree.
"""
__tablename__ = 'comments'
id = db.Column(db.Integer, primary_key=True)
parent_id = db.Column(db.Integer, db.ForeignKey('comments.id'))
body = db.Column(db.Text)
body_html = db.Column(db.Text)
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
disabled = db.Column(db.Boolean)
screened = db.Column(db.Boolean)
read = db.Column(db.Boolean)
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
recipient_id = db.Column(db.Integer, db.ForeignKey('users.id'))
post_id = db.Column(db.Integer, db.ForeignKey('posts.id'))
replies = db.relationship('Comment',
backref=db.backref('parent', remote_side=[id]),
cascade='all, delete-orphan')
@staticmethod
def dfs(comment, fun):
"""Traversal of the comment n-ary tree using Depth-First Search algorithm.
Function passed as a parameter used to process a node while
traversing the tree: print it, remove, etc.
>>> Comment.dfs(Comment.query.first(), print)
>>> descendants = []
>>> Comment.dfs(Comment.query.first(), lambda x: descendants.append(x))
"""
# comment has no replies
if not comment.replies:
return
else:
for r in comment.replies:
# do something with a comment here
fun(r)
# recurr
Comment.dfs(r, fun)
@staticmethod
def bfs(comment, fun):
"""Traversal of the comment n-ary tree using Breadth-First Search.
>>> Comment.bfs(Comment.query.first(), print)
"""
cur_level = [comment]
while cur_level:
next_level = []
for c in cur_level:
# do not touch original comment to comply with dfs version
if not c == comment:
# do something with a comment
fun(c)
if c.replies:
next_level.extend(c.replies)
# level change
cur_level = next_level
@staticmethod
def on_changed_body(target, value, oldvalue, initiator):
allowed_tags = current_app.config['PILI_ALLOWED_COMMENT_TAGS']
target.body_html = bleach.linkify(bleach.clean(
markdown(value, output_format='html'),
tags=allowed_tags, strip=True))
def to_json(self):
json_comment = {
'url': url_for('api.get_comment', id=self.id, _external=True),
'post': url_for('api.get_post', id=self.post_id, _external=True),
'body': self.body,
'body_html': self.body_html,
'timestamp': self.timestamp,
'author': url_for('api.get_user', id=self.author_id,
_external=True),
}
return json_comment
@staticmethod
def from_json(json_comment):
body = json_comment.get('body')
if body is None or body == '':
raise ValidationError('comment does not have a body')
return Comment(body=body)
def __repr__(self):
return '<Comment %r>' % self.id
db.event.listen(Comment.body, 'set', Comment.on_changed_body)
@greyli
Copy link

greyli commented Jul 16, 2017

What the remote_side=[id] mean? I read the docs, but still can't figure it out. The docs said it makes the relationship to become a many-to-one pattern. However, I think the relationship between comment and replies was one(comment)-to-many(replies).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment