Возможность ставить лайк/голосовать за сообщения из списка сообщений
У меня проблема. Я ставил лайки постам на видео с YouTube. Поставить лайк можно только из вида post_detail и это работает правильно! Как сделать так, чтобы можно было ставить лайки постам в post_list (в моем случае это 'feed')? Пожалуйста, помогите!
МОЙ КОД:
utils.py
def is_ajax(request):
return request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
views.py
from .utils import is_ajax
def feed(request):
comments = Comment.objects.all()
posts = Post.objects.all().order_by('-created_at')
return render(request, 'main/feed.html', {'posts': posts,
'comments': comments})
def post_detail(request, slug):
post = get_object_or_404(Post, slug=slug)
comments = Comment.objects.filter(post=post, reply=None).order_by('-created_at')
is_voted = False
if post.votes.filter(id=request.user.id).exists():
is_voted = True
if request.method == 'POST':
comment_form = CommentForm(request.POST or None)
if comment_form.is_valid():
content = request.POST.get('content')
reply_id = request.POST.get('comment_id')
comment_qs = None
if reply_id:
comment_qs = Comment.objects.get(id=reply_id)
comment = Comment.objects.create(
post = post,
author = request.user,
content = content,
reply = comment_qs
)
comment.save()
return redirect(post.get_absolute_url())
else:
for error in list(comment_form.errors.items()):
messages.error(request, error)
else:
comment_form = CommentForm()
return render(request, 'main/post_detail.html', {'post':post,
'comment_form':comment_form,
'comments':comments,
'is_voted':is_voted})
@login_required
def post_vote(request, slug):
post = get_object_or_404(Post, id=request.POST.get('id'), slug=slug)
is_voted = False
if post.votes.filter(id=request.user.id).exists():
post.votes.remove(request.user)
is_voted = False
else:
post.votes.add(request.user)
is_voted = True
context = {
'post': post,
'is_voted': is_voted,
}
if is_ajax(request):
html = render_to_string('main/votes.html', context, request)
return JsonResponse({'form':html})
votes.html
<form action="{% url 'main:post-vote' slug=post.slug %}" method="post">
{% csrf_token %}
{% if is_voted %}
<button type="submit" id="vote" name="post_id" value="{{ post.id }}" btn="btn btn-outline">Unvote</button>
{% else %}
<button type="submit" id="vote" name="post_id" value="{{ post.id }}" btn="btn btn-outline">Vote</button>
{% endif %}
</form>
{{post.votes_count}}
post_detail.html
<div id="vote-section">
{% include 'main/votes.html' %}
</div>
<script>
$(document).ready(function(event){
$(document).on('click','#vote', function(event){
event.preventDefault();
var pk = $(this).attr('value');
$.ajax({
type: 'POST',
url: "{% url 'main:post-vote' slug=post.slug %}",
data: {'id':pk, 'csrfmiddlewaretoken': '{{ csrf_token }}'},
dataType: 'json',
success: function(resoponse){
$('#vote-section').html(resoponse['form'])
console.log($('#vote-section').html(resoponse['form']));
},
error: function(rs, e){
console.log(rs.responseText);
}
})
});
});
</script>
feed.html
...
{% for post in posts %}
<div id="vote-section">
{% include 'main/votes.html' %}
</div>
...
{% endfor %}
<script>
$(document).ready(function(event){
$(document).on('click','#vote', function(event){
event.preventDefault();
var pk = $(this).attr('value');
$.ajax({
type: 'POST',
url: "{% url 'main:post-vote' slug=post.slug %}",
data: {'id':pk, 'csrfmiddlewaretoken': '{{ csrf_token }}'},
dataType: 'json',
success: function(resoponse){
$('#vote-section').html(resoponse['form'])
console.log($('#vote-section').html(resoponse['form']));
},
error: function(rs, e){
console.log(rs.responseText);
}
})
});
});
</script>
models.py
class Post(models.Model):
...
votes = models.ManyToManyField(get_user_model(), related_name='votes', blank=True)
@property
def votes_count(self):
return self.votes.count()
urls.py
urlpatterns = [
path('', views.feed, name='feed'),
path('post_create/', views.post_create, name='post-create'),
path('post/<slug>/', views.post_detail, name='post-detail'),
path('post/<slug>/delete', views.post_delete, name='post-delete'),
path('post/<slug>/update', views.post_update, name='post-update'),
path('post/<slug>/vote', views.post_vote, name='post-vote'),
path('comment/<slug>/delete', views.comment_delete, name='comment-delete'),
path('comment/<slug>/update', views.comment_update, name='comment-update'),
path('comment_reply/<slug>/delete', views.reply_comment_delete, name='comment-reply-delete')
]
Я понимаю, что мне нужно сделать то же самое, что и в посте подробнее. Я пытался это сделать, но получил ошибки
def post_detail(request, slug):
post = get_object_or_404(Post, slug=slug)
comments = Comment.objects.filter(post=post, reply=None).order_by('-created_at')
is_voted = False
if post.votes.filter(id=request.user.id).exists():
is_voted = True
...
Попытка сделать:
def feed(request, slug=None):
comments = Comment.objects.all()
posts = Post.objects.all().order_by('-created_at')
v_post = get_object_or_404(Post, slug=slug)
is_voted = False
if v_post.votes.filter(id=request.user.id).exists():
is_voted = True
return render(request, 'main/feed.html', {'posts': posts,
'comments': comments,
'is_voted': is_voted})
гот
Page not found (404)
Также пробовал
def feed(request, slug):
comments = Comment.objects.all()
count_filter = Q(votes=request.user)
vote_case = Count('votes', filter=count_filter, output_field=BooleanField())
posts = Post.objects \
.annotate(is_voted=vote_case) \
.all().order_by('-created_at')
return render(request, 'main/feed.html', {'posts': posts,
'comments': comments})
гот
TypeError at /
feed() missing 1 required positional argument: 'slug'
OK - здесь есть что распаковать:
Во-первых, вообще говоря, похоже, что вы хотите, чтобы Ajax выполнял основную часть работы. Причина (или, по крайней мере, начальная причина), почему это не работает, кроется в том, как вы воспроизводите формы
Для хорошо построенного HTML только один элемент на странице должен иметь определенный ID
Ваша форма генерируется следующим образом
{% for post in posts %}
<div id="vote-section"> #This will repeat for every post
{% include 'main/votes.html' %}
</div>
Это означает, что каждая форма будет иметь один и тот же id
, раздел голосования, так что
success: function(resoponse){
$('#vote-section').html(resoponse['form'])
не будет знать, куда писать.
Аналогично, в формах списка каждая кнопка имеет id="vote"
На странице подробностей это не проблема, потому что там только одна форма и одна кнопка голосования/отмены голосования. На странице списка с несколькими формами это так.
Так что же с этим делать?
Прежде всего, добавьте класс к вашей кнопке.
votes.html
<button type="submit" id="vote" class="vote-button" name="post_id" value="{{ post.id }}" btn="btn btn-outline">Unvote</button>
Далее настройте html вашего фида так, чтобы он правильно использовал идентификаторы. Мы будем использовать {{[forloop.counter][1]}}
для генерации уникальных идентификаторов.
Мы также перенесем ajax в цикл, так как slug для каждого поста разный в разделе генерации url:
. Мы добавим ID нового div в селектор и функцию success, чтобы сделать его уникальным.
feed.html
{% for post in posts %}
<div id="vote-section{{forloop.counter}}">
{% include 'main/votes.html' %}
</div>
...
<script>
$(document).ready(function(event){
$(document).on('click','#vote-section{{forloop.counter}} .vote-button', function(event){
event.preventDefault();
var pk = $(this).attr('value');
$.ajax({
type: 'POST',
//this needs to be in the for post in posts loop to get post.slug
url: "{% url 'main:post-vote' slug=post.slug %}",
data: {'id':pk, 'csrfmiddlewaretoken': '{{ csrf_token }}'},
dataType: 'json',
success: function(resoponse){
$('#vote-section{{forloop.counter').html(resoponse['form'])
console.log($('#vote-section{{forloop.counter}}').html(resoponse['form']));
},
error: function(rs, e){
console.log(rs.responseText);
}
})
});
});
</script>
{% endfor %}
Сейчас, вероятно, есть способы сделать это более эффективным, но, надеюсь, это поможет вам преодолеть горб и добиться того, чтобы это работало.