如何检查某些 post 是否被列表中的当前用户投票

How to check if some post was voted for by the current user in a list

我是django的新手,所以请耐心等待:)

像以前的许多人一样,我正在尝试构建一个 reddit 克隆。我已经完成了所有工作,但缺少一部分。在不产生太多数据库请求的情况下,我想指出当前用户是否已对特定问题投票。

这是我的模型的样子:

from django.db import models
from django.conf import settings
from mptt.models import MPTTModel, TreeForeignKey

max_post_length = 2000


class Thread(models.Model):
    title = models.CharField(max_length=200)
    text = models.TextField(max_length=max_post_length)
    created = models.DateField()
    user = models.ForeignKey(settings.AUTH_USER_MODEL)
    userUpVotes = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='threadUpVotes')
    userDownVotes = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='threadDownVotes')

    def __str__(self):
        return self.title


class Comment(MPTTModel):
    title = models.CharField(max_length=200)
    text = models.TextField(max_length=max_post_length)
    created = models.DateField()
    user = models.ForeignKey(settings.AUTH_USER_MODEL)
    thread = models.ForeignKey(Thread)
    parent = TreeForeignKey('self', related_name='children', blank=True, null=True)
    vote_count = models.IntegerField(default=0)
    userUpVotes = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='commentUpVotes')
    userDownVotes = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='commentDownVotes')

    class MPTTMeta:
        order_insertion_by = ['created']

    def save(self, *args, **kwargs):
        self.vote_count = self.userUpVotes.count() - self.userDownVotes.count()
        super(Comment, self).save(*args, **kwargs)

    def __str__(self):
        return self.title

这是我的观点:

from django.shortcuts import get_object_or_404, render
from community.models import Thread, Comment
from django.http import HttpResponse


def detail(request, thread_id):
    thread = get_object_or_404(Thread, pk=thread_id)
    comments = thread.comment_set.all()

    return render(request, 'threads/detail.html', {
        'thread': thread,
        'comments': comments
    })

这是我的模板:

{% extends "base.html" %}
{% load mptt_tags %}
{% block content %}
    <h1>{{ thread.title }}</h1>
    <p>{{ thread.text }}</p>
    <ul class="comments">
        {% recursetree comments %}
            <li>
                <div class="comment-block clearfix">
                    <vote-up-down up="{{ node.up_vote_by_user }}"
                                  down="{{ node.user_down_vote }}"
                                  url="/community/comment/{{ node.id }}/vote/"
                                  count="{{ node.vote_count }}"></vote-up-down>
                    <div class="comment">
                        <h4>{{ node.title }}</h4>

                        <div class="text">{{ node.text }}</div>
                    </div>
                </div>
                {% if not node.is_leaf_node %}
                    <ul class="children">
                        {{ children }}
                    </ul>
                {% endif %}
            </li>
        {% endrecursetree %}
    </ul>
{% endblock content %}

填充 node.up_vote_by_user 和 node.down_vote_by_user 的最佳方法是什么? 我尝试使用用户中间件和模型方法。我也尝试过预迭代评论,但这不能与用于列表的递归一起工作。

首先,看起来 CommentThread 的超集,换句话说,它们都使用相同的字段,例如 titletextusercreated 等,所以如果我处在你的位置,我会创建一个名为 Nodeabstract base class 并将这些字段放入基础模型中:

class Node(models.Model):
    title = models.CharField(max_length=200)
    text = models.TextField(max_length=max_post_length)
    created = models.DateField()
    user = models.ForeignKey(settings.AUTH_USER_MODEL)
    up_votes = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='%(class)s_upvotes')
    down_votes = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='%(class)s_downvotes')

    class Meta:
        abstract = True

    def __str__(self):
        return self.title

class Thread(Node):
    pass

class Comment(Node, MPTTModel):
    thread = models.ForeignKey(Thread)
    parent = TreeForeignKey('self', related_name='children', blank=True, null=True)
    vote_count = models.IntegerField(default=0)

    class MPTTMeta(Node.Meta):
        order_insertion_by = ['created']

关于如何查找用户是否投票给特定问题的实际问题,假设您不想用现有模型替换自己的 User 模型,我肯定会迭代用户:(我不知道为什么它对你不起作用)

class Node(models.Model):
    ...

   def upvoted_by(self, user):
       return self.up_votes.filter(user=user).exists()

   def downvoted_by(self, user):
       return self.down_votes.filter(user=user).exists()

通过这种方式,您甚至不仅可以搜索评论,还可以搜索由特定用户投票的话题。但是,为了在模板中使用这些方法,您必须编写自己的 template filter 并将 request.user 作为参数。

@register.filter
def up_voted_by(obj, user):
    return obj.upvoted_by(user)

我认为您不会对上述解决方案的性能感到满意,因为它会为每个节点实例访问数据库,因此如果线程有太多评论,您最终可能会加载网页花费太长时间.因此,更好的选择是迭代用户的赞成票并过滤与线程匹配的投票。

(还有另一种解决方案,我认为它在 here 中获取评论对象的所有后代更快且性能更好,但我发现它很难理解。)

def get_all_descendants(nodes, include_self=True):
     result = set()
     for node in nodes:
        descendants = node.get_descendants(include_self=include_self)
        result.update(set(descendats.values_list('pk', flat=True)))
     return result

def detail(request, thread_id):
    thread = get_object_or_404(Thread, pk=thread_id)

    comments = thread.comment_set.all()
    descendants = get_all_descendants(comments, include_self=True)

    upvoted_comments = request.user.comment_upvotes.filter(
        id__in=descendants).values_list('pk', flat=True)

    downvoted_comments = request.user.comment_downvotes.filter(
        id__in=descendants).exclude(
        id__in=upvoted_comments).values_list('pk', flat=True)

    return render(request, 'threads/detail.html', {
        'thread': thread,
        'comments': comments,
        'upvoted_comments': set(upvoted_comments),
        'downvoted_comments': set(downvoted_comments)
    })

然后,在您的模板中,您可以轻松检查 0(1) 中的评论是赞成票还是反对票:

{% recursetree comments %}
 <li>
   <div class="comment-block clearfix">
     <vote-up-down up="{%if node.pk in upvoted_comments %}{% endif %}"
                 down="{%if node.pk in downvoted_comments %}{% endif %}"

  ...