Skip to content

Instantly share code, notes, and snippets.

@miguelgrinberg
Created October 7, 2017 21:56
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save miguelgrinberg/39503aabef376dbe215788770b5ae839 to your computer and use it in GitHub Desktop.
Save miguelgrinberg/39503aabef376dbe215788770b5ae839 to your computer and use it in GitHub Desktop.
#!/bin/env python
# -------------------------------------------------------------------------------
# This is a basic implementation of comments with a flat structure,
# as described in my article:
# https://blog.miguelgrinberg.com/post/implementing-user-comments-with-sqlalchemy
# -------------------------------------------------------------------------------
from datetime import datetime
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class Comment(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.String(140))
author = db.Column(db.String(32))
timestamp = db.Column(db.DateTime(), default=datetime.utcnow, index=True)
# create the database and insert a few comments
db.create_all()
c1 = Comment(text='hello1', author='bob')
c2 = Comment(text='hello2', author='alice')
c3 = Comment(text='hello3', author='bob')
c4 = Comment(text='hello4', author='alice')
db.session.add_all([c1, c2, c3, c4])
db.session.commit()
# display the comments
for comment in Comment.query.order_by(Comment.timestamp.asc()):
print('{}: {}'.format(comment.author, comment.text))
#!/bin/env python
# -------------------------------------------------------------------------------
# This is an implementation of comments with replies using an adjacency list,
# as described in my article:
# https://blog.miguelgrinberg.com/post/implementing-user-comments-with-sqlalchemy
# -------------------------------------------------------------------------------
from datetime import datetime
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class Comment(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.String(140))
author = db.Column(db.String(32))
timestamp = db.Column(db.DateTime(), default=datetime.utcnow, index=True)
parent_id = db.Column(db.Integer, db.ForeignKey('comment.id'))
replies = db.relationship(
'Comment', backref=db.backref('parent', remote_side=[id]),
lazy='dynamic')
def add_reply(self, text):
return Comment(text=text, parent=self)
db.create_all()
c1 = Comment(text='hello1', author='alice')
c2 = Comment(text='hello2', author='bob')
c11 = Comment(text='reply11', author='bob', parent=c1)
c12 = Comment(text='reply12', author='susan', parent=c1)
c111 = Comment(text='reply111', author='susan', parent=c11)
c21 = Comment(text='reply21', author='alice', parent=c2)
db.session.add_all([c1, c2, c11, c12, c111, c21])
db.session.commit()
def display_comment(comment, level=0):
print('{}{}: {}'.format(' ' * level, comment.author, comment.text))
for reply in comment.replies:
display_comment(reply, level + 1)
for comment in Comment.query.filter_by(parent=None):
display_comment(comment)
#!/bin/env python
# -------------------------------------------------------------------------------
# This is an implementation of comments with replies using a comment path,
# as described in my article:
# https://blog.miguelgrinberg.com/post/implementing-user-comments-with-sqlalchemy
# -------------------------------------------------------------------------------
from datetime import datetime
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class Comment(db.Model):
_N = 6
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.String(140))
author = db.Column(db.String(32))
timestamp = db.Column(db.DateTime(), default=datetime.utcnow, index=True)
path = db.Column(db.Text, index=True)
parent_id = db.Column(db.Integer, db.ForeignKey('comment.id'))
replies = db.relationship(
'Comment', backref=db.backref('parent', remote_side=[id]),
lazy='dynamic')
def save(self):
db.session.add(self)
db.session.commit()
prefix = self.parent.path + '.' if self.parent else ''
self.path = prefix + '{:0{}d}'.format(self.id, self._N)
db.session.commit()
def level(self):
return len(self.path) // self._N - 1
db.create_all()
c1 = Comment(text='hello1', author='alice')
c2 = Comment(text='hello2', author='bob')
c11 = Comment(text='reply11', author='bob', parent=c1)
c12 = Comment(text='reply12', author='susan', parent=c1)
c111 = Comment(text='reply111', author='susan', parent=c11)
c21 = Comment(text='reply21', author='alice', parent=c2)
for comment in [c1, c2, c11, c12, c111, c21]:
comment.save()
for comment in Comment.query.order_by(Comment.path):
print('{}{}: {}'.format(' ' * comment.level(), comment.author, comment.text))
@ondiekelijah
Copy link

This code is incomplete. You have url_for('reply'), yet you don't have a reply route (or forgot to include it in your listing).

That's a great tutorial; I've been following it and I've managed to update my workings, but I'm having trouble printing the comments and replies properly. Could you possibly show me how to use jinja loops and Bootstrap5 to accomplish this?

Here's what I've tried.

        {% for comment in comments %}
        <div class="row">
            <!-- Begin comment indentation -->
            <div class="col-lg-{{1*comment.level()}}">
                <!-- End comment indentation -->
                <div>
                    <p>{{comment.author}}</p>
                    <p>{{comment.text}}</p>
                    <a class="btn btn-primary btn-sm" data-bs-toggle="collapse" href="#comment-{{comment.id}}" role="button" aria-expanded="false" aria-controls="collapseExample">
                        Reply
                    </a>
                </div>
                <div class="collapse" id="comment-{{comment.id}}">
                    <form action="{{url_for('reply_comment',post_id=post.id,comment_id=comment.id) }}" method="POST">
                        {{ form2.csrf_token }}
                        <div class="form-group mt-2">
                            {{form2.author(class="form-control", placeholder="Author")}}
                        </div>
                        <div class="form-group mt-2">
                            {{form2.reply(class="form-control" ,placeholder="Reply")}}
                        </div>
                        <button type="submit" class="btn btn-primary mt-2 btn-sm">Post Reply</button>
                    </form>
                </div>
            </div>
        </div>
        {% endfor %}

@miguelgrinberg
Copy link
Author

The code to print the comments in order is in the article, you just need to adapt the loop to Jinja.

@ondiekelijah
Copy link

Thanks, here's how I did it

        {% for comment in comments %}
        <div class="row">
            <!-- Begin comment indentation -->
            {% if comment.level() == 0 %}
            <div class="col-12 border-start border-primary">
                {% elif comment.level() == 1 %}
                <div class="col-11 offset-1 border-start border-secondary">
                    {% elif comment.level() == 2 %}
                    <div class="col-10 offset-2 border-start border-success">
                        {% elif comment.level() == 3 %}
                        <div class="col-9 offset-3 border-start border-info">
                            {% elif comment.level() == 4 %}
                            <div class="col-8 offset-4 border-start border-primary">
                                {% else %}
                                <div class="col-7 offset-5 border-start border-primary">
                                    {% endif %}
                                    <!-- End comment indentation -->
                                    <div>
                                        <p class="m-0 text-muted"><span class="text-primary">
                                                {{comment.author}}</span> <br>
                                            <span class="text-small">{{comment.timestamp}}</span>
                                        </p>
                                        <p class="m-0 text-muted">{{comment.text}}</p>
                                        <a class="" data-bs-toggle="collapse" href="#comment-{{comment.id}}" role="button" aria-expanded="false" aria-controls="collapseExample">
                                            <i class="bi bi-reply"></i>
                                        </a>
                                    </div>
                                    <div class="collapse w-50" id="comment-{{comment.id}}">
                                        <form action="{{url_for('reply_comment',post_id=post.id,comment_id=comment.id) }}" method="POST">
                                            {{ form2.csrf_token }}
                                            <div class="form-group mt-2">
                                                {{form2.author(class="form-control", placeholder="Name")}}
                                            </div>
                                            <div class="form-group mt-2">
                                                {{form2.reply(class="form-control" ,placeholder="Response")}}
                                            </div>
                                            <button type="submit" class="btn btn-primary mt-2 btn-sm">Post Reply</button>
                                        </form>
                                    </div>
                                </div>
                            </div>
                            {% endfor %}

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