Passing JSON data from views to html via ajax in Django - javascript

I cannot render html template with JSON data passed from views via ajax. I get correct JSON format from views and I can see the correct response in console.log(response), but when I run from the browser this url http://127.0.0.1:8000/our-stores/ I get this result:
[{'fields': {'address': 'Kilpolantie 16',
'city': 'Helsinki',
'country': 'Finland',
'name': 'K-market'},
'model': 'shoppire.supermarket',
'pk': 1},
{'fields': {'address': 'Kontulankari 16',
'city': 'Helsinki',
'country': 'Finland',
'name': 'S-market'},
'model': 'shoppire.supermarket',
'pk': 2}]
But instead of this output I should get rendered ourstores.html file. Please, find the code below:
models.py
class Supermarket(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
country = models.CharField(max_length=50)
def __unicode__(self):
return self.name
urls.py
urlpatterns = [
url(r'^our-stores/$','shoppire.views.ourstores',name='ourstores'),
url(r'^admin/', include(admin.site.urls)),
]
views.py
def ourstores(request):
stores_list = Supermarket.objects.all()
response_data = serializers.serialize('json',stores_list)
return HttpResponse(response_data,content_type="application/json")
ourstores.html
{% extends 'base.html' %}
{% block content %}
<div class='col-sm-12' style='text-align:center'>
<h2>Check out our stores:</h2>
<div id="show_stores" onload="ShowStores()"></div>
<div id="results"></div>
</div>
{% endblock %}
ShowStores.js
$(document).ready(function(){
ShowStores();
});
function ShowStores() {
console.log("show stores is working");
$.ajax({
url : "our-stores",
type : "GET",
dataType : "json",
success: function(response){
$.each(response,function(index){
$('#results').append(response[index].fields.name);
console.log(response[index].fields.name);
});
console.log(response);
console.log("success");
},
error : function(xhr,errmsg,err) {
$('#show_stores').html("<div class='alert-box alert radius' data-alert>Oops! We have encountered an error: "+errmsg+
" <a href='#' class='close'>×</a></div>"); // add the error to the dom
console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
}
});
};
Thanks a lot!

You do not render the ourstores.html template anywhere in your ourstores view. In order for a template to be displayed, it has to be rendered by the view. In your case, if the request is AJAX, you'd want a JSON to be rendered and if the request is not AJAX, the actual template to be rendered.
Your view could look something like this:
def ourstores(request):
if request.is_ajax():
stores_list = Supermarket.objects.all()
response_data = serializers.serialize('json',stores_list)
return HttpResponse(response_data,content_type="application/json")
return render(request, 'ourstores.html')

If you ask for JSON response, JSON response is what you will get.
If you want to render a template, use the following:
def ourstores(request):
stores_list = Supermarket.objects.all()
response_data = serializers.serialize('json',stores_list)
return render_to_response('our_stores.html',
response_data,
context_instance=RequestContext(request))
Then, inside your template use the passed data as {{response_data}}

Related

How to allow JSON access to the text within a textarea in HTML>

I am trying to create a button that allows users to save edits to a post, which they write in a textarea, through JSON. I am trying to save the data through a PUT request, but I get the following error:
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
javascript function:
function save_edit(id){
console.log("save button is clicked");
const edit_area = document.querySelector(`#edit_area_${id}`);
//save the post
fetch(`/edit/${id}`,{
method: 'PUT',
post: JSON.stringify({
post: edit_area.value
})
})
fetch(`/edit/${id}`)
.then(response => response.json())
.then(post => {
const post_itself =
document.querySelector(`#post_itself_${id}`);
post_itself.value = `${post.post}`;
post_itself.style.display = 'block';
})
}
django.views:
def edit(request, post_id):
try:
post = Post.objects.get(pk=post_id)
except Post.DoesNotExist:
return JsonResponse({"error": "Post not found."}, status=404)
if request.method == "POST":
edited_post = request.POST.get('post')
try:
post.post = edited_post
post.save()
except:
return JsonResponse({"error": "Editing the post did not work."}, status=404)
elif request.method == "GET":
return JsonResponse(post.serialize())
elif request.method == "PUT":
data = json.loads(request.body)
edited_post = data["edit_area"]
post.post = data["edited_post"]
post.save()
else:
return JsonResponse({"error": "Need a GET request."}, status=404)
html
{% for post in page_obj.object_list %}
<div class = "individual_posts">
<h5 id="p_user" class = "post_user">{{ post.user }}</h5>
<h6 id = "post_itself_{{ post.id }}" class="post_itself">{{ post.post }}</h6>
{% if post.user == request.user %}
<button id="{{ post.id }}" class="edit_button" value="{{ post.id }}">Edit</button>
{% endif %}
<textarea class="textarea" id="edit_area_{{ post.id }}" cols="220" rows="5"></textarea>
<button class="edit_save" id="save_{{ post.id }}">Save</button>
</div>
{% endfor %}
models.py serialization
def serialize(self):
return{
"id": self.pk,
"post": str(self.post),
"user": self.user.pk,
}
The GET request works correctly, but I am receiving the previously stated error from the PUT request. I think that it is because of the way I am getting the value through edited_post = data["edit_area"]. How do I correctly get access to the text within the textarea to pass to JSON?
In your save_edit PUT function you are using
post: JSON.stringify({
post: edit_area.value
})
But in your view you are looking for
data = json.loads(request.body)
edited_post = data["edit_area"]
post.post = data["edited_post"]
The JSON you are sending looks like
{"post": "Here's my edits"}
So you probably want something like:
data = json.loads(request.body)
post.post = data["post"]
Also - fetch uses "body" not "post" so you might want to amend your put function to
body: JSON.stringify({
post: edit_area.value
})

JSON POST and GET 404 (Not Found)

I am trying to create an API in Django but am receiving the following errors message in the JavaScript console.
GET http://127.0.0.1:8000/edit/undefined 404 (Not Found)
POST http://127.0.0.1:8000/edit/undefined 404 (Not Found)
Does anyone know how to fix this problem?
API url: path("edit/<int:post_id>", views.edit, name="edit")
views.py
def edit(request, post_id):
try:
post = Post.objects.get(user=request.user, pk=post_id)
except Post.DoesNotExist:
return JsonResponse({"error": "Post does not exist."}, status=404)
if request.method == "GET":
return JsonResponse(post.serialize())
else:
return JsonResponse({"error": "Need a GET request."}, status=404)
JavaScript
document.addEventListener('DOMContentLoaded', function(){
const editButtons = document.querySelectorAll('.edit_button');
for (const button of editButtons) {
button.addEventListener('click', () => edit_email());
}
});
function edit_email(id){
console.log("edit button is clicked")
document.querySelector('#post_itself').style.display = 'none';
document.querySelector('#date_and_time').style.display = 'none';
document.querySelector('#likes').style.display = 'none';
const textarea = document.createElement('textarea');
//get post
fetch(`/edit/${id}`)
.then(response => response.json())
.then(post => {
textarea.innerHTML = `${post.post}`
document.querySelector('#p_user').append(textarea);
})
//save the post
fetch(`/edit/${id}`,{
method: 'POST',
post: JSON.stringify({
post: textarea.value
})
})
}
HTML
{% for post in page_obj.object_list %}
<div class = "individual_posts">
<h5 id="p_user" class = "post_user">{{ post.user }}</h5>
<h6 id = "post_itself">{{ post.post }}</h6>
<h6 id="date_and_time" class = "post_elements">{{ post.date_and_time }}</h6>
<h6 id="likes" class = "post_elements">{{ post.likes }}👍</h6>
{% if post.user == request.user %}
<button id="editButton" class="edit_button">Edit</button>
{% endif %}
</div>
{% endfor %}
I think something might be wrong in the way I am passing in the id to the API, but I am not sure. Could the for loop in the HTML be causing the problem?
models.py
class User(AbstractUser):
followers = models.ManyToManyField("self", related_name="users_followers", symmetrical=False)
following = models.ManyToManyField("self", related_name ="who_user_is_following", symmetrical=False)
def serialize(self):
return{
"followers": self.followers,
"following": self.following
}
class Post(models.Model):
post = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE, default="")
likes = models.IntegerField(default=0)
date_and_time = models.DateTimeField(auto_now_add=True)
def serialize(self):
return{
"id": self.id,
"post": self.post,
"user": self.user,
"likes": self.likes,
"date_and_time": self.date_and_time
}
you call edit_email without id here:
button.addEventListener('click', () => edit_email());
of cause, after call you get /edit/undefined on this line:
fetch(`/edit/${id}`)
you don't send anything like id, I can imagine it should be something like this:
button.addEventListener('click', (event) => edit_email(event.target.value));
You will also need to pass the value property to the button as post.id assuming that the post object will have an id key in your for loop.
If you are getting a reference error you need to check if page_obj.object_list has an id key for all the posts.

How to access Queryset returned to template by AJAX response - Django

I want to return a queryset to a template using ajax.
this is my ajax function in a seperate js file:
$(document).ready(function(){
$("#data_to_plot_button").click(function(){
var serialized_data = $("#data_to_plot_form").serialize();
$.ajax({
url: $("data_to_plot_form").data('url'),
data: serialized_data,
type: 'post',
success: function(response){
$("#graph").append('<p>data returned successfuly</p>'); //this line is printed correctly.
$.each(response, function(i, val) {
$('graph').empty().append(
$('<li>').addClass('list-group-item list-group-item-success').text(val)
)
}); // this block adds nothing to the #graph div
}
})
});
});
and my views.py:
def my_products(request):
queryset_list_intro_products = Intro_products.objects.all().order_by('title')
products = 0
if request.method == 'POST':
products_checkbox = request.POST.get('products')
if products_checkbox:
products = serializers.serialize('json', list(queryset_list_intro_products))
context = {
'products': products,
}
return JsonResponse(context, status=200)
return render(request, 'users/basket/my_products.html')
based on an answer to this question, I try to access the returned products which is in response. but the js code adds nothing to the #graph div.
in XHR section of network tab of inspects in chrome, the ajax call's status is 200 and in the preview section I can see the products as following:
products: "[{"model": "products.intro_products", "pk": 5, "fields": {"products_intro": false, "ip_sensor_intro": false, "control_valve_intro": false, "water_quality_sensor_intro": false, "accessories_intro": true, "cover_intro": "photos/products/intro_cover/solutions.png", "title": "Accessories", "subtitle": "", "description": "description", "detailed_description": "", "video_link": "", "is_published": true, "image_left": "", "title_left": "", "description_left": "", "image_right": "", "title_right": "", "description_right": ""}},
How to access the fields of an ajax response knowing its a queryset?
You need to do something like this;
from django.template.loader import render_to_string
if request.is_ajax():
html = render_to_string(
template_name="your seperate template where you want to display the queryset",
context=dict_of_items_to_be_passed_to_above_temp.
)
data_dict = {"html_from_view": html}
return JsonResponse(data=data_dict, safe=False)

How to attach a callback on a recursively generated <select> dropdown?

I'm trying to implement chained dependent dropdown combobox selection, so you start with one combobox for main category and once you select main category, another <select> appears to select a subcategory, and so on until the innermost (most specific) category is selected. The code I have currently only works for one subcategory (direct children), how can I make it work for other levels too? So, I need to attach an onChange callback to a newly created <select> somehow.
This is jQuery code in my Django template:
{% extends 'pages/base.html' %}
{% block content %}
<h1>Create a product</h1>
<form method='POST' id='productForm' data-products-url="{% url 'products:ajax_load_categories' %}">
{{ form.as_p }}
</form>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$("select").change(function () {
var url = $("#productForm").attr("data-products-url");
var categoryId = $(this).val();
$.ajax({
url: url,
data: {
'category': categoryId
},
success: function (data) {
$("#productForm").append(data);
}
});
});
</script>
{% endblock %}
Here is my view:
def load_categories(request):
category_id = request.GET.get('category')
subcategories = Category.objects.get(id=category_id).get_children()
return render(request, 'products/category_dropdown_list_options.html', {'subcategories': subcategories})
products/category_dropdown_list_options.html
<select id="select_{{ subcategories.first.get_level }}">
<option value="">---------</option>
{% for subcategory in subcategories %}
<option value="{{ subcategory.pk }}">{{ subcategory.name }}</option>
{% endfor %}
</select>
Here is my urls.py:
app_name = 'products'
urlpatterns = [
path('create/', product_create_view, name='product-create'),
path('ajax/load-categories/', load_categories, name='ajax_load_categories')
]
Here is my Category model as per request:
from mptt.models import MPTTModel, TreeForeignKey
class Category(MPTTModel):
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')
name = models.CharField(max_length=255)
slug = models.SlugField()
class Meta:
unique_together = (('parent', 'slug',))
verbose_name_plural = 'categories'
class MPTTMeta:
order_insertion_by = ['name']
def __str__(self):
return self.name
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super().save(*args, **kwargs)
def get_slug_list(self):
ancestors = self.get_ancestors(include_self=True)
slugs = [ancestor.slug for ancestor in ancestors]
new_slugs = []
for idx, ancestor in enumerate(slugs, 1):
new_slugs.append('/'.join(slugs[:idx]))
return new_slugs
def get_recursive_product_count(self):
return Product.objects.filter(category__in=self.get_descendants(include_self=True)).count()
You'll need to turn your jQuery ajax script into a function, then call it recursively, like this:
<script>
var $r_ = function() {
var url = $("#productForm").attr("data-products-url");
var categoryId = $(this).val();
$.ajax({
url: url,
data: {
'category': categoryId
},
success: function (data) {
if (data != 'leaf_node') {
$("#productForm").append(data);
}
$('select').change($r_);
}
});
} //end of $r_
$('select').change($r_);
</script>
Update
If you take a look at the get_children method of the MPTT model, you'll see that it checks whether or not the instance has any children, and returns None if it doesn't.
Add a None check in your view, then add a different response when you've reached a leaf node:
from django.http import HttpResponse
def load_categories(request):
category_id = request.GET.get('category')
subcategories = Category.objects.get(id=category_id).get_children()
if subcategories:
return render(request, 'products/category_dropdown_list_options.html', {'subcategories': subcategories})
return HttpResponse('leaf_node')
Then add a check for leaf nodes in your ajax call (see above).

Change the data in div periodically on django template

I want to change the comments (data) appearing on my page periodically. I have referred to a question here, yet the data is not changing, it does on page refresh though.
Here's my code in views.py
def index(request):
count = Article.objects.all().count()
comments = Article.objects.values_list('content')[randint(0, count - 1)][0]
context = {
'current_date': datetime.now(),
'title': 'Home',
'comments': comments,
}
return render(request, 'index.html', context)
And urls.py
urlpatterns = [
url(r'^$', index, name='index'),
url(r'^about/$',about),
# url(r'^comments/$',ArticleCreateView.as_view(), name='comments'),
url(r'^admin/', admin.site.urls),
]
And index.html
<div>
<div id="comments">
{{ comments }}
</div>
</div>
<script>
var ALARM_URL = "{% url 'index' %}";
function refresh() {
$.ajax({
url: ALARM_URL,
success: function(data) {
$('#comments').html(data);
}
});
};
$(document).ready(function ($) {
refresh();
var int = setInterval("refresh()", 3000);
});
</script>
Your view index() returns an HTML page, so the argument data contains a whole HTML page.
You probably want to add a second view that returns JSON data only, maybe like this:
views.py
def index_data(request):
# here you return whatever data you need updated in your template
return JsonResponse({
'something_1': 0,
})
urls.py
url(r'^data$', index_data, name='index-data'),
And then in your AJAX call you do something like this:
url: "{% url 'index-data' %}",
success: function(data) {
$('#comments').html(data['something_1']);
}
Let us know if that helps.
EDIT
You probably also need to adjust your timer; pass it the function name, but without calling it and also not as a string (see the docs of setInterval):
$(document).ready(function ($) {
refresh();
setInterval(refresh, 3000);
});

Categories