I have a problem. I made likes to the posts on the video from YouTube. It is possible to like a post only from post_detail view and it's works correctly! How to make it possible to like posts in the post_list (in my case it's 'feed')? Please help!
MY CODE:
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')
]
I understand that I need to do the same as in post detail. I tried to do this, but i got errors
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
...
Tried to do:
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})
got
Page not found (404)
Also tried
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})
got
TypeError at /
feed() missing 1 required positional argument: 'slug'
UPD: For the attemp above, I also did something:
set slug to None
def feed(request, slug=None):
And and moved the script to the for-loop (for post in posts)
This gave its results. Now everything works almost as it should, only there is a bug. When you click on the vote button, he votes for for the post below, and vice versa
OK - there's a fair bit to unpack here:
First, generally speaking, it looks you you want Ajax to handle the bulk of the work. The reason (or at least the initial reason) why that isn't working above is in how you replicate forms
For well constructed HTML, only one element per page should have a particular ID
Your form is generated like this
{% for post in posts %}
<div id="vote-section"> #This will repeat for every post
{% include 'main/votes.html' %}
</div>
What this means is that every form will have the same id, vote-section, so
success: function(resoponse){
$('#vote-section').html(resoponse['form'])
won't know where to write.
Similarly, in the forms on list, every button has id="vote"
This isn't an issue on the details page, because there's only one form and one vote/unvote button. On the list page, with multiple forms, it is.
So what to do about it?
First off, add a class to your button.
votes.html
<button type="submit" id="vote" class="vote-button" name="post_id" value="{{ post.id }}" btn="btn btn-outline">Unvote</button>
Next tweak your feed html so it uses IDs correctly. We'll use {{[forloop.counter][1]}} to generate unique IDs.
We'll also move the ajax into the loop, as the slug for each post is different in the url: generation section. We add the new div ID into the selector and the success function to make it unique.
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 %}
Now there are probably ways to make this more efficient, but hopefully it should get you over the hump to where it works.
views.py
dicfacultets = DicFacultets.objects.all()
disfacultetsedit = DicFacultets.objects.get(id=id)
form = FacultetsForm(request.POST, instance=disfacultetsedit)
if request.method == 'GET':
return render(request, 'tao/dicfacultetsedit.html', {'dicfacultets': dicfacultets,
'form': FacultetsForm(),
})
else:
try:
if form.is_valid():
disfacultetsedit = form.save(commit=False)
title = request.POST.get('title')
disfacultetsedit.title = title
disfacultetsedit.save()
return redirect('dicfacultets')
except TypeError:
return render(request, 'tao/dicfacultets.html', {'dicfacultets': dicfacultets,
'error': 'error',
'form': form,
})
return render(request, 'tao/dicfacultets.html', {'dicfacultets': dicfacultets, })
html
{% for fac in dicfacultets %}
...
# *call modal window for editing*
<a href="facultetsedit/{{ fac.id }}" class="on-default edit-row" data-toggle="modal"
data-target="#facultetsedit{{fac.id}}"><i class="fa fa-pencil"></i></a>
# next call form method="POST" action="{% url 'facultetsedit' id=fac.id %}"> {{ form }} form in modal window
when a modal window is called, the field is empty, but editing and saving work
I have my views file:
from django.shortcuts import render
from .models import Note
# Create your views here.
def index(request):
return render(request, "index.html", {})
def write(request):
notes = Note.objects
return render(request, "write.html", {})
def create(request):
if request.POST:
body = request.POST['note']
title = request.POST['title']
print(f'title = { title }\nbody = { body }'
and my html code:
<h1>CREATE A NOTE</h1>
<form action="{% url 'notes:create' %}" method="post">
{% csrf_token %}
<label for="title">Title</label>
<input type="text" name="title"><br>
<label for="note">Note</label>
<input type="text" name="note"><br>
<input type="submit" value="Submit">
</form>
Whenever I submit this form and try to access either the title or note values I get a MultiValueDictKeyError
A MultiValueDictKeyError comes when you try to access something from request.POST which doesn't exist. It's basically the same the python KeyError. Use request.POST.get('key', 'default_if_key_doesn't exist')
def create(request):
if request.POST:
body = request.POST.get('note', '')
title = request.POST('title', '')
print(f'title = { title }\nbody = { body }'
I am trying to add something to a session by performing a post request via a form, when I try to view what's in the session by going to a page and a empty list is returned (I see this []), I have looked around toher questions and tried adding this request.session.modified = True but it hasn't worked as I thought that the session may not have been saving. Can anyone suggest any possible solutions?
Views
def cart(request):
cartSession = request.session['cart']
context={
'cartSession': cartSession
}
return render(request, "OsbourneSpiceWebsite/cart.html", context=context)
def menu(request):
request.session['cart'] = []
context = {
"items": Menu.objects.all(),
}
return render(request, "OsbourneSpiceWebsite/menu.html", context)
def addToCart(request):
if request.method == 'POST':
request.session.modified = True
itemID = request.POST['productId']
getItemFromPost = Menu.objects.get(itemid=itemID)
if getItemFromPost is None:
return HttpResponse("Error there is no data")
cartSession = request.session['cart']
for item in cartSession:
if item['Itemname'] == getItemFromPost.itemname:
item['Qty'] += 1
break
else:
cartSession.append({
'Itemname': getItemFromPost.itemname,
'Itemprice': int(getItemFromPost.itemprice),
'Qty': 1
})
request.session['cart'] = cartSession
context = {
"cartSession": cartSession
}
request.session.modified = True
return render(request, "OsbourneSpiceWebsite/cart.html", context=context)
Form
<form action="{% url 'addToCart' %}" method="POST">
{% csrf_token %}
<input name="productId" type="hidden" value="{{ item.itemid }}">
<div class="addToCartButtonContainer">
<button aria-label="Add" class="addButton " type="submit">+</button>
</div>
</form>
Cart page (page that should display items in session)
{% block body %}
<h1>CART</h1>
<h1>{{cartSession}}</h1>
{% endblock %}
I want to hide form if user already pushed on the button. I tried to use a bit of JS code, it works well, but after refreshing page, button on form stay valid. It not a huge surprise for me)) I want hide form using Django templates, but it is not so easy as I thought.
my html :
<div class="who_come">
<p>Who come : </p>
{% for u in who_come %}
{%if u.visiter.username != request.user.username %}
<form class="form-inline" role="form" method="post" id="joinToEventForm">
{% csrf_token %}
<p align="left"><b ><button type="submit">I want join</button></b></p></b></p>
</form>
{% endif %}
{% endfor %}
<div id="who_come_links">
{% for u in who_come reversed %}
<p>{{u.visiter.username}}</p>
{% endfor %}
</div>
<script>
$('#joinToEventForm').submit(function() {
$(this).find("button[type='submit']").prop('disabled',true);
});
</script>
<script>
$('#joinToEventForm').on('submit', function(event){
event.preventDefault();
console.log("form submitted!") // sanity check
$.ajax({
type:'POST',
data:{
action: 'joinEvent',
csrfmiddlewaretoken:'{{ csrf_token }}'
},
success : function (data) {
//console.log(data);
var usernames = JSON.parse(data);
var html_str = '';
for (var i=0; i < usernames.length; i++) {
html_str += '<p>' + usernames[i] + '</p>';
}
$('#who_come_links').html(html_str);
}
});
});
</script>
</div>
my model :
class WhoComeOnEvent(models.Model):
visiter = models.ForeignKey(User, related_name='WhoComeOnEvent')
which_event = models.ForeignKey(Topic, related_name='WhoComeOnEvent')
in this place :
<p>Who come : </p>
{% for u in who_come %}
{%if u.visiter.username != request.user.username %}
<form class="form-inline" role="form" method="post" id="joinToEventForm">
{% csrf_token %}
<p align="left"><b ><button type="submit">I want join</button></b></p></b></p>
</form>
{% endif %}
{% endfor %}
i tried to check who already joined to event and if user that do request same that user in database table, then hide form. But this code doesn't work. It hide form no matter if user exist in db table or not. Second part is that has no connection to current event. Suppose django template that hide form in my case will work. It will check only row in table but it will ignore current it event or it different event (not sure, but i'm afraid of it).
my view :
def p(request, pk):
user = request.user
topic = get_object_or_404(Topic, pk=pk)
post = get_object_or_404(Post, pk=pk)
comment = Comments.objects.filter(pk=pk)
who_come = WhoComeOnEvent.objects.filter(which_event=topic.id)
if request.is_ajax() and request.POST.get('action') == 'joinEvent':
who_come_obj = WhoComeOnEvent.objects.create(
visiter=user,
which_event=post.topic
)
visitors_usernames = []
for w in who_come:
visitors_usernames.append(w.visiter.username)
return HttpResponse(json.dumps(visitors_usernames))
if request.method == 'POST':
form = CustomCommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.creator = user
comment = Comments.objects.create(
body=form.cleaned_data.get('body'),
creator=user,
post=post
)
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
# return render(request, 'post.html', {'post': post, 'topic': topic, 'comment': comment,
# 'form': form, 'who_come': who_come})
else:
form = CustomCommentForm()
return render(request, 'post.html', {'post': post, 'topic': topic, 'comment': comment,
'form': form, 'who_come': who_come})
Solution:
I found more elegant solution, just adding .distinct('visitor') to my who_come queryset.
If someone will read it question and found my solution helpful, notice, that objects still creating to the databese and it doesn't unique!