如何检查某些 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 的最佳方法是什么?
我尝试使用用户中间件和模型方法。我也尝试过预迭代评论,但这不能与用于列表的递归一起工作。
首先,看起来 Comment
是 Thread
的超集,换句话说,它们都使用相同的字段,例如 title
、text
、user
、created
等,所以如果我处在你的位置,我会创建一个名为 Node
的 abstract 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 %}"
...
我是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 的最佳方法是什么? 我尝试使用用户中间件和模型方法。我也尝试过预迭代评论,但这不能与用于列表的递归一起工作。
首先,看起来 Comment
是 Thread
的超集,换句话说,它们都使用相同的字段,例如 title
、text
、user
、created
等,所以如果我处在你的位置,我会创建一个名为 Node
的 abstract 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 %}"
...