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.
Related
I am trying to get the id of a model object of django from the template using ajax. But I cannot get the exact value of data in the views.py.I am getting a None value for id in views. Where I have done wrong? Here is my code
Views.py:
def is_ajax(request):
return request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
def home(request):
return render(request, 'home/home.html')
def index(request):
# If you have passed any data through the 'data' property
#you can get it here, according to the method used.
##if is_ajax(request=request):
id = request.GET.get('data')
msg = testing.objects.exclude(id = id).first()
# Note that we are passing the 'ajax_comments.html' template.
#return render(request, 'practice.html', {'text': msg, 'id':id})
##else:
##msg = testing.objects.last()
return render(request, 'practice.html', {'text': msg, 'id': id} )
#comments = Comment.objects.all.first()
#return render(request, 'practice.html', {'comments': comments})
Template:
{% extends 'base.html'%}
{%block head %} {%endblock%}
{%block navbar %} {% endblock %}
{%block navsmall %} {% endblock %}
{% block header%}
{% endblock %}
{% block firstgrid%}
{%endblock%}
{%block secondgrid%}
<div id = 'myTest'>
{{text.text}} <br> {{id}}
<button class = "asd" value="{{text.td}}">Button</button>
</div>
<script>
$(".asd").click(function(e){
e.preventDefault();
var id = $(this).val();
$.ajax({
method: 'GET', // defaults to GET. Set POST if you like, but be aware of the CSRF token submission too!
url: "{% url 'practice' %}", // submit to the same url
data: {'data': id}, // pass here any data to the server. You can omit it.
success: function(data) {
// This function will run when server returns data
$('#my-Test').replaceWith(data);
alert('ok');
console.log(id);
},
error: (error) => {
console.log('error');
}
});
});
</script>
{%endblock%}
{%block footer%}
{%endblock%}
In views I am getting None value in id
I am trying to resolve the problem for few days. I want to post the comment without refreshing page. The problem is the div in which comments are is not reloading or disappearing after hitting post button. It depends on how i "catch" it: when I use the id like:
$('#comments').append('<p>' + data.comment.content + '</p><small>' + data.comment.author + '/' + data.comment.date_of_create + '</small>');
it is not reloading and if i use class it disappears. When i refresh the page, comment is being add properly. This is my code below.
views.py
class PostDetailView(FormMixin, DetailView):
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
form_class = CommentForm
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['form'] = CommentForm(initial={'post': self.object})
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
if request.method == 'POST':
new_comment = Comment.objects.create(
author = self.request.user,
post = self.object,
content = form.cleaned_data['content']
)
return JsonResponse({'comment': model_to_dict(new_comment)}, status=200)
else:
return self.form_invalid(form)
def form_valid(self, form):
form.instance.author = self.request.user
form.instance.post = self.get_object()
form.save()
return super(PostDetailView, self).form_valid(form)
def get_success_url(self):
return reverse('post_detail', kwargs={'slug': self.object.slug})
comments.js
$(document).ready(function(){
$("#commentBtn").click(function(){
var serializedData = $("#commentForm").serialize();
$.ajax({
url: $("commentForm").data('url'),
data: serializedData,
type: 'post',
dataType: 'json',
success: function(data){
console.log(data);
$('#comments').append('<p>' + data.comment.content + '</p><small>' + data.comment.author + '/' + data.comment.date_of_create + '</small>');
$('textarea').val('');
}
})
});
});
post_detail.html
<h3>Dodaj komentarz:</h3>
<form action="" method="POST" id="commentForm" data-url="{% url 'post_detail' slug=post.slug %}">
{% csrf_token %}
{{ form.as_p }}
<button type="button" id="commentBtn">WyĆlij</button>
</form>
<div id="comments">
<h3>Komentarze:</h3>
{% for comment in post.comment_set.all %}
<p>{{ comment.content }}</p>
<small>{{ comment.author }} / {{ comment.date_of_create }}</small>
{% endfor %}
</div>
In my base.html file i placed on the bottom:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="{% static 'js/comment.js' %}"></script>
settings.py
STATIC_URL = '/static/'
STATICFILES_DIR = [
os.path.join(BASE_DIR, 'static')
]
The most annoying thing is I followed some resolutions/tutorials, i do the same and it still not working. Can it be problem with bad configure? Thanks for any help in advance!
console gives object:
https://i.stack.imgur.com/vWp9N.png
Instead of this
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
do this
<script src="https://code.jquery.com/jquery-3.5.1.min.js" ></script>.
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!
I am pretty new to Django and I try to create my first application. However I faced a problem now.
I have a form (to add Alert - my mdoel) with Django validation and a div with list of all alerts. If I fill all fields correctly the alert is added and my div with alerts is refreshed using ajax. The problem is with form validation.
Without ajax I was sending bound form with errorLists for every field. I used Django form API. It was working ok but I can't refresh the errorlist if adding a form couldn't succeed.
I am getting either no render of form or render without context - all fields are blank. Of course they shouldn't be blank if I made a mistake in one field and It was working OK without ajax.
Bunch of code, First this is the view method I call:
def add_alert(request):
report = __get_or_create_empty_Report(request)
if request.method == 'POST':
form = AlertForm(request.POST)
if form.is_valid():
alert_project = ""
alert_name = ""
alert_ticket = ""
alert_date = ""
alert_type = ""
alert_comment = ""
if form.cleaned_data.has_key('alert_project'):
alert_project = form.cleaned_data['alert_project']
if form.cleaned_data.has_key('alert_name'):
alert_name = form.cleaned_data['alert_name']
if form.cleaned_data.has_key('alert_ticket'):
alert_ticket = form.cleaned_data['alert_ticket']
if form.cleaned_data.has_key('alert_date'):
alert_date = form.cleaned_data['alert_date']
if form.cleaned_data.has_key('alert_type'):
alert_type = form.cleaned_data['alert_type']
if form.cleaned_data.has_key('alert_comment'):
alert_comment = form.cleaned_data['alert_comment']
alert = Alert()
alert.alt_prj_id = get_object_or_404(Project, prj_id=alert_project)
alert.alt_name = alert_name
alert.alt_ticket = alert_ticket
alert.alt_date = alert_date
alert.alt_type = alert_type
comment = Comment()
comment.com_value = alert_comment
comment.save()
alert.alt_com_id = comment
alert.alt_rep_id = report
alert.save()
alerts = Alert.objects.filter(alt_rep_id=report.rep_id)
return render(request, 'main/theform.html', {'form': form, 'alerts': alerts, 'error_message': "Alert has been added"})
else:
form = AlertForm()
alerts = Alert.objects.filter(alt_rep_id=report.rep_id)
return render(request, 'main/theform.html', {'form': form, 'alerts': alerts})
alerts = Alert.objects.filter(alt_rep_id=report.rep_id)
return render(request, 'main/theform.html', {'form': form, 'alerts': alerts, 'error_message': "Alert has NOT been added"})
Here's the form template:
{% extends 'main/base.html' %}
{% block content %}
<div id="alerts"> </div>
{% for alert in alerts %}
<p> {{ alert.alt_name }} </p>
{% endfor %}
<form class="col-md-12" action="{% url 'main:add_alert' %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
{{ error_message }}
{% endblock %}
and simple script:
$(document).ready(function() {
//AJAX util
$('.add-alert-form').submit(function (e) {
e.preventDefault();
$.ajax({
type: "POST",
url: $(this).attr("action"),
data: $(this).serialize(),
success: function (data) {
$("#alerts").load(" #alerts");
}
});
});
}
I think I am doing it completly wrong but I have no idea what's the right way.
Take a look at https://docs.djangoproject.com/en/1.7/topics/forms/modelforms/
so your views would look like that:
if form.is_valid():
obj = form.save()
data = serializers.serialize('json', [obj,])
else:
data = json.dumps(dict([(k, [unicode(e) for e in v]) for k,v in form.errors.items()]))
return HttpResponse(data, mimetype='application/json')