Change the data in div periodically on django template - javascript

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);
});

Related

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).

Rendering JS.ERB results in raw code

When an AJAX request executes, show.js.erb renders the partial _article.haml.
What I want to be able to do in show.js.erb is to write:
<%= j render 'article' %>
Since it has a .js extension I am required to wrap this in JavaScript (the example above does not render the partial), so:
'<%= j render 'article' %>' OR ('<%= j render 'article' %>');
This would render the partial but with
raw code--including HTML and JS escaping.
('things will go back to \"normal.\"<\/p>\n\n');
What's the right way to do this?
welcome#index:
.ajax_load.article-content{ data: { 'remote-url' => article_path(#article) } }
articles.js:
$(document).ready(function() {
$('.ajax_load').each(function(index, element) {
var url = $(element).data('remote-url')
if (url) {
$.get(url, function(responseText) {
$(element).html(responseText);
})
} else {
console.log("missing url for ajax!")
}
})
})
This answer belongs to #MrYoshiji.
Ajax:
$(document).ready(function() {
$('.ajax_load').each(function(index, element) {
var url = $(element).data('remote-url')
if (url) {
$.get(url, function(responseText) {
$(element).html(responseText);
}, 'html' )
} else {
console.log("missing url for ajax!")
}
})
})
articles_conroller renders _article partial directly:
def show
#respond to JS
respond_to do |format|
format.js { render :partial => "article" }
end
end
welcome#index:
.ajax_load.article-content{ data: { 'remote-url' => article_path(#article) } }
You have to decide where you want the partial to render. In most cases, you'd grab the container on the page with jQuery and insert the partial there like:
$('#article-container').html('<%= j render 'article' %>');

Passing JSON data from views to html via ajax in Django

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}}

Django ModelForm and AJAX usage

i am working on for a basic feedback system. I just want to submit feedback with ajax, for example if fields are empty ajax will return some error msg and update the div at the same page with no redirection. if everything is ok it will update the feedbacks div with the new one...
Here is my code
models.py
from django.db import models
# Create your models here.
class FeedBack(models.Model):
title = models.CharField(max_length=255)
description = models.TextField()
def __unicode__(self):
return self.title
class Meta:
ordering = ["-title"]
views.py
import json
from django.shortcuts import *
from django.template import RequestContext
from submission.forms import *
def feedback(request):
if request.method == "POST":
form = FeedBackForm(request.POST)
message = 'Oops!'
if(form.is_valid()):
message = request.POST['title']
return HttpResponse(json.dumps({'message': message}))
return render_to_response('index.html',
{'form':FeedBackForm()}, RequestContext(request))
urls.py
from django.conf.urls import patterns, include, url
from submission.views import *
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'^feedback/', feedback, name='feed_back'),
)
index.html
<html>
<head>
<script type="text/javascript" src="../static/helper.js"></script>
</head>
<body>
<h1>Leave a Suggestion Here</h1>
<div class="message"></div>
<div>
<form id="feed_form" action="" method="POST">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit Feedback" />
</form>
</div>
</body>
</html>
helper.js
$(document).ready(function() {
$('#feed_form').submit(function() { // catch the form's submit event
$.ajax({ // create an AJAX call...
data: $(this).serialize(), // get the form data
type: $(this).attr('method'), // GET or POST
url: '', // the file to call
success: function(data) { // on success..
$('.message').html(data); // update the DIV
},
error: function(e, x, r) { // on error..
$('.message').html(e); // update the DIV
}
});
return false;
});
});

Backbone JS populating model/collection correctly

I have a collection which contains one model. In my template I should be able to do:
<% _.each(collection, function(model) { %>
<p>logged in <%= model.username %>!</p>
<% }); %>
But I've found I need to do:
<% _.each(models, function(model) { %>
<p>logged in <%= model.attributes.username %>!</p>
<% }); %>
I'm not sure exactly what the problem is but the model isn't being populated properly. Anyone know why this is happening and how I can set the values within the model so I can loop over a collection of models and access the values simply with model.username ?
Thank you in advance.
Here's my model and collection:
var AccountModel = Backbone.Model.extend({
defaults:
{
username: "bob"
}
});
var AccountCollection = Backbone.Collection.extend(
{
model: AccountModel,
url: "/php/account-details.php",
parse: function(data, xhr)
{
return data
},
initialize: function()
{
}
});
Here's my fetch function:
fetchAccountCollection: function(){
var $this = this;
$this.homepageAccountCollection = new AccountCollection();
$this.homepageAccountCollection.fetch(
{
dataType: "json",
cache: false,
success: function(collection)
{
Backbone.trigger('accountcollection:loaded', collection);
},
error: function()
{
console.log("fetchAccountCollection: error");
}
});
},
When the success function is called the trigger invokes the render function within the controller:
renderAccount: function(collection)
{
var $this = this;
$this.loginPageView = new LoginView(
{
el: '#login-form',
template: 'loggedin-template',
collection: collection
});
$this.loginPageView.render();
},
When $this.loginPageView.render(); is called the following code is executed:
render: function()
{
var collection = this.options.collection;
var tpl = _.template($(this.options.template).html(), collection);
this.$el.html(tpl);
return this;
},
The value of the username is being returned from a PHP script like so:
$array=array('username' => $user['username']);
echo json_encode($array);
Underscore's each method (and many others) are mixed directly into the collection. Use collection.each(function () { ... }) instead of _.each(collection ...).
The problem is that your passing a Backbone.Collection to your template, so when you end up calling the following in your template:
_.each(models,function(model) { ... });
You are iterating through collection.models array which is an array of Backbone.Model's. Since a Backbone.Model stores model values within the attributes property, you have to access via model.attributes.username, you could also use model.get('username'), which would work as well.
As you stated, you would like to access via model.username, and you can do that by calling collection.toJSON() before passing it to the template, you have to put it in an object such as, {collection:collection.toJSON()};. Check out the documentation for toJSON. The method exists for both Backbone.Collection and a Backbone.Model.
The code would be the following:
render: function() {
var collection = this.options.collection;
var tpl = _.template($(this.options.template).html(), {collection:collection.toJSON());
this.$el.html(tpl);
return this;
}
Then you could use your template like:
<% _.each(collection, function(model) { %>
<p>logged in <%= model.username %>!</p>
<% }); %>
Here is a minimal JSBIN DEMO.
As a side note: It looks like your using a version of backbone < 1.1.0. Just be aware this.options goes away in the newer versions. When you upgrade be sure to have a look at the Change Log.

Categories