python - 在 Django 中,除了第一个对象之外,无法影响 ListView 中的其他对象
问题描述
我正在尝试使用 ajax 在我的 Django 站点中创建一个支持系统,因此当用户单击支持按钮时它不会刷新。在我的站点的主页中,所有帖子的列表视图中,我都能够让 ajax 和 upvote 请求工作,但仅限于顶部帖子。无论我是否点击另一个帖子的投票按钮,它只会在顶部帖子上注册效果。我哪里做错了?下面是我的代码。
模型.py
User = settings.AUTH_USER_MODEL #this is the user model
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE) #many to one relationship where many posts can be tied to one user
content = models.TextField(blank=True, null=True)
date_posted = models.DateTimeField(default=timezone.now)
image = models.ImageField(upload_to='trade_images', blank=True, null=True)
upvotes = models.ManyToManyField(User, blank=True, related_name='upvotes')
total_upvotes = models.IntegerField(default='0')
def get_absolute_url(self):
return reverse('main:post-detail', kwargs={'pk': self.pk}) #returns the url for individual posts
def __str__(self):
return self.content
class Upvote(models.Model):
user = models.ForeignKey(User, related_name='upvoted_user', on_delete=models.CASCADE)
post = models.ForeignKey(Post, related_name='upvoted_post', on_delete=models.CASCADE)
def __str__(self):
return str(self.user) + ':' + str(self.post)
视图.py
# list of all posts
class post_list_view(ListView):
model = Post
template_name = 'main/home.html'
context_object_name = 'posts' # this is called from the html as 'for post in posts'
ordering = ['-date_posted'] # minus to reverse the date posted, so newer posts show up on top
paginate_by = 5 #sets pagination per page
return context
def upvote(request):
if request.POST.get('action') == 'post':
result = ''
id = int(request.POST.get('postid'))
post = get_object_or_404(Post, id=id) #grabbing the selected post using postid
new_relation = Upvote(user=request.user, post=post) #storing the upvote relation between user and post using Upvote model arguments - 'user' and 'post'
if post.upvotes.filter(id=request.user.id).exists(): #checking if user already has upvote relations with post by filtering user and post
post.upvotes.remove(request.user) #remove upvote relation from post
post.total_upvotes -= 1 #minus 1 total_upvotes from post
result = post.total_upvotes #storing the new total_upvotes into result
Upvote.objects.filter(user=request.user, post=post).delete() #filtering user and post and deleting the upvote table
post.save()
else:
post.upvotes.add(request.user)
post.total_upvotes += 1
result = post.total_upvotes
print('create upvote')
new_relation.save()
post.save()
return JsonResponse({'result': result }) # return the new total_vote count back to html as json
模板.html
<div id="upvote-section" class="card-footer">
{% if user.is_authenticated %}
<form action="{% url 'main:upvote-post' %}" method=POST>
{% csrf_token %}
<button type="submit" class="btn btn-success btn-sm" id="upvote-btn" name="post_id" value="{{post.id}}">Upvote</button>
<button type="submit" class="btn btn-outline-success btn-sm" id="upvote-btn" name="post_id" value="{{post.id}}">Upvote</button>
<p id="total_upvotes">
{{ post.total_upvotes }}
</p>
<a href="{% url 'main:post-detail' post.id %}" class="card-link"></i> Comment</a>
{% if post.user == user %}
<a href="{% url 'main:post-update' post.id %}" class="card-link">Edit</a>
<a href="{% url 'main:post-delete' post.id %}" class="card-link" style="color:red">Delete</a>
{% endif %}
</form>
{% else %}
Upvotes ({{ post.total_upvotes }}) Please login to upvote or comment.
{% endif %}
</div>
脚本/ajax
<script>
$(document).on('click', '#upvote-btn', function (e) {
e.preventDefault();
$.ajax({
type: 'POST',
url: '{% url "main:upvote-post" %}',
data: {
postid: $('#upvote-btn').val(),
csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),
action: 'post'
},
success: function (json) {
document.getElementById("total_upvotes").innerHTML = json['result']
},
error: function (xhr, errmsg, err) {
console.log(err)
}
});
})
</script>
解决方案
问题在于您的 AJAX 脚本。特别是在您的数据对象中,
data: {
postid: $('#upvote-btn').val(),
csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),
action: 'post'
}
由于您选择postid
using $('#upvote-btn').val()
,因此您始终会发回第一个 upvote 按钮的值。
通过使用它的 id 选择 DOM 中的任何内容,将返回与指定 id 匹配的第一个元素。这就是为什么建议您不要对 DOM 中的多个元素使用相同的 id。
既然我们知道问题出在哪里,这里有一种可能的方法来解决它:
您可以upvote-btn
在模板中将 id 作为类而不是 id,这样每个upvote 按钮都有一个名为upvote-btn
. 所以你的脚本变成了这样:
$(document).on('click', '.upvote-btn', function (e) {
e.preventDefault();
$.ajax({
type: 'POST',
url: '{% url "main:upvote-post" %}',
data: {
postid: e.target.value,
csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),
action: 'post'
},
success: function (json) {
document.getElementById("total_upvotes").innerHTML = json['result']
},
error: function (xhr, errmsg, err) {
console.log(err)
}
});
})
请注意,更改在脚本的第一行,我们将选择器从更改为#upvote-btn
,.upvote-btn
因为我们之前讨论过将 upvote-btn 转换为类而不是 id。
我们还将postid
您的 AJAX 调用$('#upvote-btn').val()
中的e.target.value
推荐阅读
- r - 产品购买——如果消费者购买产品 x,他们购买产品 y 的可能性有多大
- vba - 在表单中插入新记录后保持记录最新
- java - 如何在排序的双向链表中插入字符串数据?
- python - 如何从 n 维集合轻松创建 n-1 维集合?
- multithreading - DirectX11 资源释放多线程
- python - 在 for 循环的每次迭代中保存变量并稍后加载它们
- sql-server - 根据字段分组的 MAX 数量和 SUM 数量选择 ROW
- node.js - 查找以特定字母开头的单词
- powershell - 从快捷方式启动脚本时,Powershell 找不到 PNPUTIL
- symfony - 如何切换 Symfony 的 php-translation/symfony-bundle EditInPlace