I'm new to Django. I'm trying to build a discussion app in Django that, like Reddit and Hacker News, is fully threaded and sorts each comment by up/down vote.
I've been using the django-voting app, and would like to continue using it if at all possible.
A simplified version of my models.py is:
Class Comment(models.Model):
text = models.TextField()
user = models.ForeignKey(User)
parent = models.ForeignKey('self', related_name='children', null=True, blank=True)
Because I am using the django-voting app, I can get the "score" (upvotes minus downvotes) for any particular comment like so:
comment = Comment.objects.get(pk=1) # Grab the comment with primary key = 1
score = Vote.objects.get_score(comment)["score"]
What I can't figure out is how to: (a) prepare the data in a view in a format that could be turned into a threaded discussion in a template, and (b) how to do so in a way that is sorted by upvotes.
Any help on those two issues would be greatly appreciated.
I am open to using another method, like mptt, to build the comment tree -- but I still am not clear how I would be able to sort by upvote within the django-mptt app.
Thanks!
EDIT: I have come up with my own, extremely hack-ish solution below. I'm not going to mark this question as answered, because I don't think this is the sort of solution one would use in production (and I hope to not use this in production myself). But, just in case anyone was looking for a solution, I hope this is helpful:
In my views.py, I created a function that outputs a sorted list by vote given a queryset of objects:
def list_sorted_by_score(query_set):
sorted_score_list = []
# First, I create a list of all the objects with their associated scores
for object in query_set:
# django-voting's get_score outputs a dictionary that looks like:
# {"score": score, "num_votes": num_votes}
score_dict = Vote.objects.get_score(object)
sorted_score_list.append({"object": object, "score": score_dict["score"], "num_votes": score_dict["num_votes"]})
# Then, I sort that list of dictionaries by the "score"
sorted_score_list.sort(key=lambda x:x["score"], reverse=True) # This sorts by score, highest to lowest (reverse=False is lowest to highest)
return sorted_score_list
I use that function to sort all the top-level comments by score. So my context variable only includes Comments, sorted by score, for which there are no parents -- i.e.:
top_level_comments = list_sorted_by_score(Comment.objects.filter(parent=None))
In my models.py, I defined a method that gets the list_sorted_by_score of the children of a given comment:
def score_list_children(self):
children = self.children.all()
sorted_score_list = []
for object in children:
score_dict = Vote.objects.get_score(object)
sorted_score_list.append({"object": object, "score": score_dict["score"], "num_votes": score_dict["num_votes"]})
sorted_score_list.sort(key=lambda x:x["score"], reverse=True)
return sorted_score_list
Finally, I made a template for a single comment, "comment.html." A simplified version of that looks like:
<div class="comment">
{{ comment.object.text }}
{# note that I must use the comment**.object**, since
that's how I constructed my list_sorted_by_score list of dictionaries #}
{% for child in comment.object.score_list_children %}
{% with filename="comment.html" %} {# hack to get around Django throwing a maximum recusion error #}
{% include filename with comment=child %}
{% endwith %}
{% endfor %}
</div>
As may be obviously evident, this is quite hack-ish. I am still very interested in hearing real solutions people have tried in the real world.