4

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.

Jake
  • 809
  • 1
  • 8
  • 18

0 Answers0