NoReverseMatch error using django with ajax - python

I am trying to add ajax on the delete button of my blog, I want to update the page without reloading it when I delete an article, but I am getting this error when I use ajax and I don't understand what's going wrong.
Reverse for 'article_effacer' with arguments '('',)' and keyword arguments '{}' not found. 1 pattern(s) tried: ['article/delete/(?P<slug>[-\\w\\d]+)/$']
I am turning myself to you because I'm having a hard time resolving this issue, so here is the code.
views.py
def article_effacer(request, slug):
if request.user.is_authenticated():
user = request.user
article = Article.objects.filter(slug=slug, user=user).delete()
context = {'article':article}
return HttpResponse(json.dumps(context), content_type='application/json')
else:
return HttpResponseForbidden()
urls.py
url(r'^delete/(?P<slug>[-\w\d]+)/$', views.article_effacer, name="article_effacer"),
template.html
<input class="delete" id="{{ a.slug }}" data-url='{% url "article.views.article_effacer" a.slug %}' value="Effacer">
Ajax
$('.delete').click(function(){
var slug = $(this).attr('id');
var url = $(this).data('url');
$.ajax({
type: "POST",
url: url,
data: {'slug': $(this).attr('id'), 'csrfmiddlewaretoken': '{{ csrf_token }}'},
dataType: "json",
success: function(response) {
console.log('deleted')
$(".article" + slug).remove();
},
error: function(rs, e) {
console.log(slug)
}
})
})
The problems comes probably from the url, but I don't understand how to correct it, any suggestions?
_ Update _
Full template
{% block content %}
{% for a in article %}
<div class="article{{a.slug}}">
[... unrelated html ...]
{% if a.user == user %} |
<input class="delete" id="{{ a.slug }}" data-url='{% url "article.views.article_effacer" a.slug %}' value="Effacer">
{% endif %}
</div>
{% endfor %}
{% endblock %}
{% block javascript %}
<script>
$('.delete').click(function(){
var slug = $(this).attr('id');
var url = $(this).data('url');
$.ajax({
type: "POST",
url: url,
data: {'slug': $(this).attr('id'), 'csrfmiddlewaretoken': '{{ csrf_token }}'},
dataType: "json",
success: function(response) {
console.log('deleted')
$(".article" + slug).remove();
},
error: function(rs, e) {
console.log(slug)
}
})
})
</script>
{% endblock %}

You can't use template tags like that, with values coming from JS; it should be clear that template tags are resolved on the server side, whereas the JS is executed on the client according to what the user clicks.
However you are halfway there by putting the value in the input. Actually, inputs don't have a href attribute, but you could use a data- prefix; the point is that you can then grab that in your JS to use as the URL. Something like:
<input class="delete" id="{{ a.slug }}" data-url='{% url "article.views.article_effacer" a.slug %}' value="Effacer">
var url = $(this).data('url');
$.ajax({
type: "POST",
url: url,
...
});

Related

Ajax form not sending data in django

I am trying to send data to Django without page refresh. So I am using ajax.
Created a Django model and form
class MyModel(models.Model):
text=models.CharField(max_length=100)
class MyForm(forms.ModelForm):
class Meta:
model=MyModel
fields = "__all__"
Then send the form to the HTML page via views.py
def home(request):
print(request.POST.get('text',False))
form = MyForm(request.POST)
if request.method=='POST':
print(request.POST.get('text',False))
if form.is_valid():
data=form.save()
return render(request,'home.html',{'form':form})
Create a form in HTML template
<form action = "" id="post-form" method = "post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="submit" id="submit-button">
</form>
This is the javascript file
$(document).on('submit','#post-form',
function(x){
x.preventDefault();
console.log("button clicked")
$.ajax({
type:'POST',
url:'/',
data:{
text:$("id_text").val(),
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val()
},
success:function(){
alert('Saved');
}
})
}
)
I think it might be the issue with URL, if i set url like this way
url:'{% url "home" %}',
Then in console, i got this error
XHR POST http://127.0.0.1:8000/%7B%%20url%20%22home%22%20%%7D
I am unable to find, where is the issue.
You should use # for selecting id, so use text:$("#id_text").val().
For using dynamic url, use like this:
url:"{% url 'home' %}",
For more information, about using dynamic urls refer this question.
Edit:
One way to use dynamic url is to pass it with onsubmit event, so try this:
Template file or home.html
<form id="post-form" method="POST" onsubmit="checking('{% url 'home' %}')">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="submit" id="submit-button">
</form>
Js file:
function checking(dynamicUrl) {
$(document).on("submit", "#post-form", function (x) {
x.preventDefault();
console.log("button clicked");
$.ajax({
type: "POST",
url: dynamicUrl,
data: {
text: $("#id_text").val(),
csrfmiddlewaretoken: $("input[name=csrfmiddlewaretoken]").val(),
},
success: function () {
alert("Saved");
},
});
});
}

How to use Ajax in Django for like button

I want to Ajax feature for the like button in my blog. I have implemented like feature first, it was working as expected(reloads page after clicking like button). Then, i want to implement Ajax so the page should not reload after clicking like button. But, it is not working as expected.
views.py:
def like_post(request):
user = request.user
if request.method == 'POST':
post_id = request.POST.get('id')
post_obj = get_object_or_404(Post, id=post_id)
if user in post_obj.liked.all():
post_obj.liked.remove(user)
else:
post_obj.liked.add(user)
like, created = Like.objects.get_or_create(user=user, post_id=post_id)
if not created:
if like.value == 'Like':
like.value = 'Unlike'
else:
like.value = 'Like'
like.save()
if request.is_ajax():
post = Post.objects.all()
context={
'post': post
}
html = render_to_string('blog/like_section.html', context, request=request)
return JsonResponse({'form': html})
Jquery in base.html:
<script src="https://code.jquery.com/jquery-3.1.1.min.js">
<script type="text/javascript">
$(document).ready(function(event){
$(document).on('click', '#like', function(event){
event.preventDefault();
console.log("hello")
var pk = $(this).attr('value');
$.ajax({
type : 'POST',
url : {% url 'like-post' %},
data : {'id' : pk, 'csrfmiddlewaretoken': '{{ csrf_token }}'},
dataType : 'json',
success : function(response){
$('#like-section').html(response['form'])
console.log($('#like-section').html(response['form']));
},
error : function(rs, e){
console.log(rs.responseText);
},
});
});
});
</script>
like_section.html:
<form action="{% url 'like-post' %}" method="POST">
{% csrf_token %}
<input type="hidden" name="post_id" value="{{ post.id }}">
{% if user not in post.liked.all %}
<button id="like" class="btn btn-outline-info mb-4" type="submit">Like</button>
{% else %}
<button id="like" class="btn btn-info mb-4" type="submit">Unlike</button>
{% endif %}
</form>
urls.py:
path('likes/', views.like_post, name='like-post'),
It returns 404 with No Post matches the given query. My observation is, it looks it is not detecting Jquery part because if I replace 'id' with 'post_id' in request.POST.get('id') & return redirect under request.method == 'POST', it works like my previous version(Reloads page if I hit like button). So the control never goes into request.is_ajax() loop, How do I make Django to use Ajax?

How do I submit a form without page reload using Ajax

I'm learning Ajax on how I can submit a comment form without page reload. I'm using Django, I have list of posts in homepage each with comment form. When I submit a comment it is not saving in database and also it doesn't display in browser. When I check on Chrome console, I got an error 2 elements with non-unique id.
from django.http import JsonResponse
from django.template.loader import render_to_string
def home(request):
all_images = Post.objects.filter(poster_profile=request.user)
if request.method == 'POST':
post_id = request.POST.get("post_comment")
post_obj = Post.objects.get(pk=post_id)
form = CommentForm(request.POST)
if form.is_valid():
comment=form.save(commit=False)
comment.user=request.user
comment.commented_image=post_obj
comment.save()
else:
form=CommentForm()
context = {'all_images':all_images, 'form':form}
if request.is_ajax():
html=render_to_string('ajax_feeds_comment.html', context, request=request)
return render(request, 'home.html', context)
#home.html
{% for post in all_images %}
<img src="{{ post.image.url }}">
{% for comment in post.comments.all %}
<p>{{ comment.comment_post }}</p>
{% endfor %}
<div class="reload-form">
{% include "ajax_feeds_comments.html" %}
</div>
{% endfor %}
#ajax_feeds_comments.html
<form method="POST" class="feeds-comment" action=".">
{% csrf_token %}
<input type="hidden" value="{{post.id}}" name="post_comment">
<textarea name="comment_post" class="form-control" id="id_comment_post{{post.id}}"></textarea>
<button type="submit" class="btn btn-primary">submit</button>
</form>
<script>
$(document).on('submit', '.feeds-comment', function(event){
event.preventDefault();
console.log($(this).serialize());
$.ajax({
type: 'POST',
url: $(this).attr('action'),
data: $(this).serialize(),
dataType: 'json',
success: function(response){
$('.reload-form').html(response['form']);
},
error: function(rs, e) {
console.log(rs,responseText);
},
});
});
</script>
There are multiple things not correct here:
In your "urls.py" you should not send every request to your views. This is where you get the "favico.ico" error 500 get from. Having no favico is ok, but getting a Error 500 is not.
Check the html code for duplicate id's! These have to be unique.
If you use django variables for html, do it like this: Instead of "{{post.id}}" use: "{{ post.id }}" with spaces around the var.
In the
document.on("submit", "feeds-comment", function(){...})
you're not using the id of that element but it's class name.
Check where the submit is going to. Check Django if the request is being handled. (You see that in the console where Django is running). Also maybe Post a screenshot here!

Updating an HTML fragment with ajax response Django using CBV(ListView)

So I have a homepage that consists of a base listview which includes all of the objects from my db(as shown in the cbv .all() query). What I wanted to do is include a search filter, hence I thought it would be a good idea to isolate it into a separate "list.html" fragment in order to make it reusable later on. Currently, I have an ajax call that sends information to the cbv and the return is a render to list.html fragment. However, when I visit the homepage, the page doesn't get rendered to begin with.
Help or advice would be very much appreciated, thank you again.
urls.py
urlpatterns = [
url(r'^$', exp_view.DrugListView.as_view() , name = 'drug_list')]
here is my CBV template:
views.py
class DrugListView(ListView):
context_object_name = 'drugs'
model = Drug
template_name = 'expirations/drug_list.html'
def get(self, request):
if self.request.is_ajax():
text_to_search = self.request.GET.get('searchText')
print(text_to_search)
return render(request, "expirations/list.html", {'drug':Drug.objects.filter(name__contains = text_to_search).order_by('name')})
else:
return render(request, "expirations/list.html", {'drug':Drug.objects.all().order_by('name')})
here is
drug_list.html
{% extends 'expirations/index.html' %}
{% block content %}
{% include 'expirations/list.html' %}
{% endblock %}
{% block javascript %}
<script>
$(document).ready(function(){
var input = $("#searchText")
input.keyup(function() {
$.ajax({
type: "GET",
url: "{% url 'drug_list' %}",
data: {'searchText' : input.val()},
success: function(data){
$("#list_view").load("expirations/list.html")
}
})
});
})
</script>
{% endblock %}
here is list.html:
{% for drug in drugs %}
<div class = '.col-sm-12'>
<ul class = 'list-group'>
<li class = "list-group-item" >
<span class = "badge">First Expiration: {{ drug.early_exp|date:"l M d Y" }}</span>
{{ drug.name }}
{% regroup drug.expiration_dates.all by facility as facility_list %}
{% for x in facility_list %}
<span class="label label-info">{{ x.grouper }}</span>
{% endfor %}
</li>
</ul>
</div>
{% endfor %}
You've misunderstood what you need to do in your success callback.
That function has no access to Django templates. But it doesn't need to, because Django has already sent the fragment in the response, which is available in the data parameter. You just need to insert that into the existing page.
For that you'll need a container; so you should modify your drug_list.html so that there is a div around the include:
{% block content %}
<div id="list_contents">
{% include 'expirations/list.html' %}
</div>
{% endblock %}
and now your success function can be:
success: function(data) {
$("#list_contents").html(data)
}
I do not think that it will work this way. For Ajax calls and CBV look at example in the docs.
Then you would need to return the new, filtered data (in html format) to the ajax call and replace the html in the sucess function via JS.
Daniel Roseman definitely helped solve the puzzle, here is the updated code:
views.py: updated if/else statement to include a default list view of the general template. also added the context name 'drugs' instead of 'drug'
class DrugListView(ListView):
context_object_name = 'drugs'
model = Drug
template_name = 'expirations/drug_list.html'
def get(self, request):
if self.request.is_ajax():
text_to_search = self.request.GET.get('searchText')
print(text_to_search)
return render(request, "expirations/list.html", {'drugs':Drug.objects.filter(name__contains = text_to_search).order_by('name')})
else:
return render(request, "expirations/drug_list.html", {'drugs':Drug.objects.all().order_by('name')})
drug_list.html: changed success callback function load html as suggested by daniel.
{% extends 'expirations/index.html' %}
{% block content %}
<div id = 'list_view'>
{% include 'expirations/list.html' %}
</div>
{% endblock %}
{% block javascript %}
<script>
$(document).ready(function(){
var input = $("#searchText")
input.keyup(function() {
$.ajax({
type: "GET",
url: "{% url 'drug_list' %}",
data: {'searchText' : input.val()},
success: function(data){
$("#list_view").html(data)
}
})
});
})
</script>
{% endblock %}

Flask flash and redirect - shows response in devtools but no pageload

New to flask here ... my problem is that I'm trying to check if a response is empty, and if so flash a message. With the below code, I can see that the redirect goes through and the subsequent GET responds with the correct html in dev tools, but the page is not loaded, it stays on the current page (which also happens to be layout.html, not sure if this is an issue, my intent is to simply reload this page to show the flashed message).
Relevant Flask:
#app.route('/')
def hello():
return render_template('layout.html')
#app.route('/query',methods=['POST'])
def query():
start=request.json['start']
end=request.json['end']
name=request.json['name']
sql="select some stuff"
data_list = []
stuff=cur.execute(sql)
for row in stuff:
data_list.append(row[0])
if not data_list:
flash('No balances for selected client/dates')
return redirect(url_for('hello'))
return json.dumps(data_list)
if __name__ == '__main__':
app.secret_key = 'secretkeyhere'
app.run(debug=True,host='127.0.0.1',port=8000)
Relevant portion of the html to avoid a wall of text
<body>
<div class="container">
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="flashed_message" role="alert">
<button type="button" class="close" data-dismiss="alert" aria- label="Close"><span aria-hidden="true">×</span></button>
{{message}}
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
And lastly.... AJAX:
$(function(){
$("[type=submit]").click(
function(){
// event.preventDefault()
$(".container").hide()
var startdate=$('#datetimepicker1').val()
var enddate=$('#datetimepicker2').val()
var name=$('#mhh').val()
$.ajax({
type: "POST",
url: $SCRIPT_ROOT + "/query",
contentType: "application/json; charset=utf-8",
dataType:"json",
success: function(response) {
console.log('worked!')
return {'start':response.start,'end':response.end,'name':response.name}
},
error:function(){
console.log('didn\'t work')
}
})
})
});
From what I can see of your code it looks like you're redirecting the AJAX call. Essentially your AJAX call will load your index, and not the browser itself (which is what you want).
Inside your AJAX, try changing your error function to
error: function() {
document.location.reload()
}
Then, alter your if not data_list block to:
if not data_list:
flash('No balances for selected client/dates')
return Response(status=500)

Categories