Jquery script in Django project isn't detecting form submission - javascript

I'm creating a website using Django, and using Ajax to prevent site reload after submitting a form. Right now, I have orders being displayed on the site with an x button beside each order. Clicking the x cancels the order on the database (a post request that changes a value rather than simply deleting it) and also reloads the div in which the orders are housed. I have other forms on this website that are working correctly (they do have fields, though and use crispyforms). The problem I'm facing is that the script isn't detecting that the form is submitted.
Here are the pertinent parts of my project:
views.py
class CancelForm(ModelForm):
class Meta:
model = Order
fields = ['Filled']
...
def cancelorder(request, pk):
form = CancelForm(request.POST)
if request.is_ajax and request.method == "POST":
order = Order.objects.get(pk=pk)
order.Filled = "C"
instance = order.save(update_fields=["Filled"])
return JsonResponse({"canceled": pk}, status=200)
return JsonResponse({"error": ""}, status=400)
urls.py
urlpatterns = [
path('', views.orderpage, name="order-index"),
path('cancel_order/<int:pk>/', views.cancelorder, name="cancel_order"),
path('post/ajax/order/', views.postorder, name = "post_order"),
path('yourorders/', views.yourorders, name="your_orders"),
path('allorders/', views.allorders, name="all_orders"),
]
orderpage.html (this is my main page, with the div that is to be reloaded on yourorders.html)
<div class="container-fluid ActiveOrderInfoDiv" id="YourOrdersDiv">
{% include 'order/yourorders.html' %}
</div>
yourorders.html
{% for order in all_orders %}
<div class="row">
...
<div class="col">{{ order.OrderID }}</div>
<form action="{% url 'order:cancel_order' pk=order.OrderID %}" id="cancel_button" method="POST">
{% csrf_token %}
<button type="submit" class="close" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</form>
</div>
{% endfor %}
Javascript (here, check 2 is never logged)
$(document).ready(function () {
$("#cancel_button").submit(function (e) {
console.log("check 2");
e.preventDefault();
console.log("check 3");
$.ajax({
url: "{% url 'order:your_orders' %}",
success: function (response) {
$("#YourOrdersDiv").load("/order/yourorders/");
},
error: function (response) {
}
});
});
});
What I've tried
Moved the script to yourorders.html (I thought maybe the JS wasn't seeing the cancel_button ID)
Used console.log to see where the flow stopped (it doesn't seem to pick up that the cancel button was submitted)
Added the CancelForm modelform (previously I was updating the DB without a modelform)
Generally poking around with Ajax syntax and order
Looking at other questions here on StackOverflow - I seem to be following them, and my syntax may just be right, it's just not picking up that the cancel_button form is being submitted

You should delegate the event handler to the document level so that when the form is reloaded the event is still handled. When you "reload" the form you are inserting a new element into the DOM that does not have the event handler attached to it
$(document).on("submit", "#cancel_button", function(e) {
...

Related

How can I show a div or html element after submitting a form and retrieve some information from a view in Django

Currently when the user introduces a string in an input field and clicks the submit button, this invokes a view that returns through return render(request, 'index.html', context) a context that
it basically contains data that is displayed in a table.
I would like said table to be visible only after submitting the form and not before and that when it is visible it shows the information obtained from the view.
The problem is that if through inline styling I make this table not visible, for example in the following way:
<div class="row" id="searchVariantTable" style="display: none;">
<!-- SOME TABLE HERE-->
</div>
And then I use the onsubmit event for form or onclick for button, it doesn't work. (It works partially, I can see the tbody but thead is missing, so basically I can't display the retrieved data from the database).
Similarly, if I try something like this:
$('document').ready(function() {
$('#searchVariantTable').hide();
$('form').submit(function(e) {
$('#searchVariantTable').show();
e.preventDefault();
});
});
It doesn't work either.
I think the last option, if I'm not mistaken, is AJAX, but I'm not quite sure how to do something like that with Django (it's my first time using Django)
What am I doing wrong? Is there an option that I am missing?
You can try by using if else in django template:
index.html
<form method="post">
<input type="submit">
</form>
{% if allowed == "yes" %}
<!-- your table code -->
{% endif %}
and in views.py
if request.method == "POST":
return render(request, 'index.html',{'allowed':'yes'})
else:
return render(request,'index.html',{'allowed':'no'})

Django-tinyMCE Submit button not working

I've implemented TinyMCE with the django-tinymce package. However, my submit button which worked fine without TinyMCE now has become rather useless since I can't submit the form, once everything is filled out.
I can use Ctrl + S inside of TinyMCE (I discovered that by accident) and everything will get submitted correctly. Also, I can use the save-button of the TinyMCE "save" plugin to submit.. Do I have to configure the submit button to make it work with TinyMCE?
Template:
{% extends 'medisearch/header.html' %}
{% load crispy_forms_tags %}
{% block header %}
{{ form.media }}
{% endblock %}
{% block content %}
▷⋅⋅⋅⋅⋅⋅⋅<form action="{{ url }}" method="post">
▷⋅⋅⋅⋅⋅⋅⋅ <div class="form-group">
▷⋅⋅⋅⋅⋅⋅⋅ {% csrf_token %}
▷⋅⋅⋅⋅⋅⋅⋅ {{ form|crispy }}
▷⋅⋅⋅⋅⋅⋅⋅ </div>
▷⋅⋅⋅⋅⋅⋅⋅ <input type="submit" class="btn btn-primary" value="Speichern" />
▷⋅⋅⋅⋅⋅⋅⋅</form>
{% endblock %}
views.py
class EntryDetail(DetailView):
model = Mediwiki
slug_field = 'non_proprietary_name'
template_name = 'mediwiki/entry.html'
class MediwikiForm(FormView):
template_name = 'mediwiki/create.html'
form_class = MediwikiForm⋅
success_url = "/" #TODO user get's redirected to page he's created⋅
def form_valid(self, form):
form.save()
return super(MediwikiForm, self).form_valid(form)
class EntryDisplay(View):
def get(self, request, *args, **kwargs):
try:
view = EntryDetail.as_view()
return view(request, *args, **kwargs)
except Http404: # If there's no entry in db:
if check_user_editor(request.user) == True:
view = MediwikiForm.as_view()
return view(request, *args, **kwargs)
else:
pass
def post(self, request, *args, **kwargs):
view = MediwikiForm.as_view()
return view(request, *args, **kwargs)⋅
forms.py
class MediwikiForm(ModelForm):
wiki_page = forms.CharField(widget=TinyMCE(attrs={'cols': 80, 'rows': 30}))
class Meta:
model = Mediwiki⋅
fields = '__all__'
TinyMCE is in urls.py and under INSTALLED_APPS..
I know it's probably too late for you, but it seems that i had the same issue, just now and my solution might help someone in the future.
You are using crispy, which includes the javascript files for the form on it's own.
Therefore the django_tinymce/init_tinymce.js will be referenced twice.
This will break the submittion of your content, since the form is initialized twice.
In order to fix this you may just remove the call of {{ form.media }}.
I had a similar issue and learned that it has to do with the way that TinyMCE deals with text areas. The following init script worked for me:
<script>
tinymce.init({
selector:'.editor',
setup: function (editor) {
editor.on('submit', function (e) {
editor.save();
});
}
});
</script>
#artifex_knowledge answers makes sense and it works.
To build up on it, besides calling editor.save() on submit (or on change), keep in mind that if users don't fill the text area, they won't be able to submit the form, but the this field is required error message won't be displayed.
This is because the text area field (in this case wiki_page) is required by default, so in the html it will be rendered with required. But TinyMCE hides the text area (and replaces it with an iframe :( ), so if you try to submit the form with an empty required, it won't, but the error message will keep hidden.
(A possible solution is to use JS to remove the required attribute and check it in django later).
Just delete required field from textarea element, which is used as editor.
Deleting the 'required' field in the textarea element solved my problem (like Krysits mentioned)
I also had the same issue as yours, and I just removed for instance: "wiki_page" charfield from the subclass of Modelform, and put Tinymce widget in the Meta class.
class MediwikiForm(ModelForm):
class Meta:
model = Mediwiki⋅
fields = '__all__'
widgets = {
'wiki_page': TinyMCE(attrs={'cols': 80, 'rows': 30})
}

Django: make a POST request on form submit without reloading the page or rendering to another?

I have been struggling with this problem for more than two days, I have looked at similar questions here and to many documentations but nothing helped me. Basically, I am building a Django web application (where people can add themselves as friends) I am using Django package django-friendship, but I want the following to happen: when a user is in someone else profile, the first can click an 'Add friend' button and a friend request must be sent. But I want to achieve this without reloading the page or rendering to another one. The method that has to be triggered in order to send the request is in views.py. Probably I need to use ajax but in what way? My final goal is the button to perform as an 'Add friend' button in facebook, for example (without reloading).
friend_profile.html
<form action="{% url 'users:fr_request' pk=friend.id %}" method="POST">
{% csrf_token %}
<input type="submit" value="Add friend"/>
views.py
def friendship_add(request, pk):
if request.method == 'POST':
to_user = User.objects.get(pk=pk)
from_user = request.user
try:
Friend.objects.add_friend(from_user, to_user)
except AlreadyExistsError as e:
print('Exception when adding a friend ' + e)
else:
return HttpResponse('You added a friend')
return render(request, 'users/friend_profile.html')
urls.py
urlpatterns = [
url(r'users/(?P<pk>\d+)/send/$', views.friendship_add, name='fr_request'),
]
At the moment when submitting the form, I render the page to users/(?P\d+)/send/ which calls the view method and executes Friend.objects.add_friend, but I want this to happen without going to /send/ page or reloading the current?
This can be easily achieved using jQuery. You may want to assign an id to your form in case there are multiple forms in your page:
<form action="{% url 'users:fr_request' pk=friend.id %}" method="POST" id="friend_form">
{% csrf_token %}
<input type="submit" value="Add friend"/>
The script could then look like that:
$(function() {
$("#friend_form").submit(function(event) {
// Stop form from submitting normally
event.preventDefault();
var friendForm = $(this);
// Send the data using post
var posting = $.post( friendForm.attr('action'), friendForm.serialize() );
// if success:
posting.done(function(data) {
// success actions, maybe change submit button to 'friend added' or whatever
});
// if failure:
posting.fail(function(data) {
// 4xx or 5xx response, alert user about failure
});
});
});
For more information and examples refer to the jQuery.post() documentation.

ajax like count is showing the same for every post in django

The problem is due to the ajax implementation in my django twitter clone app, the like count for every post is showing the same after clicking the like button.but after the page refresh that is okay. I am near to solve the problem but stuck somehow.
view:
def add_like(request):
if request.method == 'GET':
ans_id = request.GET['id']
user = request.user.profile
liked_tweet = get_object_or_404(Tweet, pk=ans_id)
if ans_id:
# creating instance by sending the Like table fields
instance, created = Like.objects.get_or_create(liker=user, liked_tweet=liked_tweet)
ans = Tweet.objects.get(id=(int(ans_id)))
if ans:
likes = ans.likes + 1
ans.likes = likes
ans.save()
# returns the likes field of a tweet post
return HttpResponse(likes)
the HttpResponse is sending the likes and that creates the problem I guess.
the template:
{% for tw in tweets %}
<div class="blog-post">
<p>
{{ tw.content|safe }}<br><hr>
<small class="small">
লিখসে -
<!-- in the "href" we can pass data like "pk", accessing by the structure the current object is based on-->
{{ tw.tweeter.user.username|capfirst }}
</small>
</p>
{% if user.is_authenticated %}
<button class="btn btn-default likes-button" type="button"
data-ansid="{{ tw.pk }}">Like</button>
<i> Total Likes: </i><em class="like_count">{{ tw.likes }}</em>
{% endif %}
</div>
the ajax script:
$(".likes-button").click(function(e) {
if ($(this).html() == "Like") {
$(this).html('Unlike');
//alert("js working");
// error was there for "data" insted of "attr"
var ansid = $(this).attr("data-ansid");
$.ajax({
url: '{% url "add_like" %}',
type: 'get',
data: {id: ansid}
}).done(function (data) {
alert("success");
$('.like_count').html(data);
//$('#likes').hide();
}).fail(function (err) {
alert(err);
});
}
Thanks in advance.
I think the very first comment by Sayse gives your answer. I am just trying to give a bit more explanation.
So What you have done is after a successful ajax request, you replace existing like count with the data you get from ajax in any the element who have a class named .like_count.
Check In your code $('.like_count').html(data); This select all the elemnt having like_count class and change the html.
Instead, what you should've done is after a successful ajax, change the data only in one place. You need to choose appropriate jquery selector.
Something like .closest() can be used. In that case, use (following code is not tested) $(this).closest('.like_count').html(data); to apeend 'like count' in appropriate element.
Also you can assign dynamic ID to each 'like count' element based on id and then use exect ID Selector.
Hope this helps.
Cheers!
Did you say:
but after the page refresh that is okay
Since your code snippet works, you're simply looking for the likes count incrementation to happen and see the live update in the template.
Well, in theory, here:
The function that increments the like should return with a JSON response of the incremented value from the database.
A client function standing by accepts this JSON response, and updates the template value accordingly.
In Practicals:
See this answer: https://stackoverflow.com/a/31832275/1757321

Initiating AJAX request from multiple buttons on the same page in Django

Ok, so I'm trying to add voting to my website(django based) using Ajax. I have multiple entries in one page, But right now my code only let users vote on the first entry. Please help me with the code so that users can vote on all of them.
First is the html code, basically it's just a vote button for users to vote
{% for answer in answers %}<!-- django template -->
<strong id="vote_count">{{ answer.votes }}</strong> people vote this answer
{% if user.is_authenticated %}
<button id="vote" data-answerid="{{answer.id}}" class="btn btn-primary" type="button">
<span class="glyphicon glyphicon-thumbs-up"></span>
Vote
</button>
{% endif %}
{% endfor %}<!-- end django template -->
Second, below is the django view that process the request
#login_required
def vote_answer(request):
answer_id = None
if request.method == 'GET':
answer_id = request.GET['answer_id']
votes = 0
if answer_id:
answer = Answer.objects.get(id=answer_id)
if answer:
votes = answer.votes + 1
answer.votes = votes
answer.save()
return HttpResponse(votes)
below is the url mapping:
url(r'^like_category/$', views.like_category, name='like_category'),
Finally is the javascript
$('#vote').click(function(){
var answerid;
answerid = $(this).attr("data-answerid");
$.get('/vote_answer/', {answer_id: answerid}, function(data){
$('#vote_count').html(data);
$('#vote').hide();
});
});
Again, my problem is that of all the entries I have in one page, with this code I can only vote the first one. How can modify it so I can vote all of them
You need to use class instead of id on <button>, so that multiple buttons can share the same jQuery event handler.
<button class="vote" data-answerid="...">
Then you can do the following in JavaScript:
$(document).on("click", ".vote", function(){
var answerid;
answerid = $(this).attr("data-answerid");
$.get('/vote_answer/', {answer_id: answerid}, function(data){
$('#vote_count').html(data);
$('#vote').hide();
});
});
This will bind the event handler to click any <button class=vote>.
Also you should do AJAX POST instead of GET by HTTP semantics, because voting is a state changing operation. Otherwise the browser or the web proxies may cache the result (though jQuery have its own cache buster).

Categories