In my Django project, I have a page that displays all the instances of a particular model. I want to auto-refresh the page every few seconds, but only update the relevant part of the page.
I'll describe each part. First - I have a Django view. In its simplest form, it looks like this
class MyListView(ListView):
lookup_field = 'uuid'
model = Foo
template_name = 'mylist.html'
def get_queryset(self):
return list(Foo.objects.all()) # finds all my objects from DB
def get_context_data(self, **kwargs):
objects = self.get_queryset()
context = super(MyListView, self).get_context_data(**kwargs)
context['foos'] = objects # this gets passed to html page
return context
So it finds all Foo objects from the database and returns it to the html.
The html page displays a table, where each entry is a row. Furthermore, there is a checkbox at the beginning of each row. Like this
<table id="content">
<thead>
<tr>
<th><input type="checkbox" id="chckHead"/></th>
<th>UUID</th>
<th>Attribute1</th>
<th>Attribute2</th>
</tr>
</thead>
<tbody>
{% for item in foos %}
<tr id="{{ item.uuid }}">
<td>
<input type="checkbox" name="checkboxlist" class="chcktbl" />
</td>
<td><code>{{ item.uuid }}</code></td>
<td>{{ item.attribute1 }}</td>
<td>{{ item.attribute2 }}</td>
</tr>
{% endfor %}
</tbody>
</table>
Now, some of the attributes of the Foo model may get updated in the background, so I want to somehow refresh the page periodically with new data.
I did that in javascript, like this
function setup() {
setTimeout("refreshPage();", 5000);
}
function refreshPage() {
window.location = location.href;
}
$(document).ready(setup);
So every 5 seconds, JS would call refreshPage(), and my page gets refreshed. If the database was changed behind the scenes, my page would reflect it.
The problem with the above is that it refreshes the whole page, which includes the checkbox. So if I had already selected a checkbox, the refresh would reset it to its original state ('not checked').
So, what is the correct way to solve this problem?
Just really high level:
The window.location that you're adding is going to reset the whole page. By this time you've already figured that out. What you're looking for is something could be handled by using a page partial or by using a REST endpoint and pushing it back through the template.
Here are the steps you'll want refreshPage to preform instead.
Save off any values that you think are important
Send an ajax request to get either the partial or json
Render that new information to the page
Put back the values from step one over the values rendered in step 3
More likely you're going to want to wait to do the refreshPage if they're currently editing the area (saves you step 1 and 4). That, may be a choice you've already voted against.
I hope that helps, it's a very non-technical answer but I'm not sure I could give you a very specific answer without knowing A LOT more about your project.
Related
I have a Django localhost web server that I've been experimenting with, and I'm doing the infamous "todo app" as my intro to a few topics.
Conceptually, the user puts their "todo item" into a text input field (which is a Django form that's inside of the Django HTML template). This action takes the text and puts it in an SQLite3 database and at the same time, the action creates a div container that has a paragraph tag and a button inside of that div. The P tag gets filled with the contents of the text given in the input. The button is meant to do two things; firstly, it deletes the parent div, which removes that button and the todo item. So, it's like a "this item is done" button, which deletes that item. That is working properly.
The SECOND function of the button, though, has got me stumped. The idea is to delete that item from the database as well, as that's where I store and retrieve the todo items from when the page is generated. I have attempted to do this by passing the {item} variable (which is a Django Template Language variable made inside of the Django view, which I will show below) into a delete function, which I am expecting should delete the associated database entry, by filtering for any item that contains the text within {item}. Now, I know this isn't a perfect method, but it's my first try. Please don't nitpick it too hard. It's not meant to be a customer-facing application, it's only an experiment to be used by me.
The problem: The button that should be deleting the associated database entry bypassing the {item} variable into a filter delete method for the database is throwing errors and is not working, despite me messing with the syntax endlessly. I'm clearly doing something wrong, but I don't know what, and the chrome dev tools error is REALLY not helping or making sense. I'd greatly appreciate any help.
The code:
relevant django template section for the todo page/app:
<div class="root-div container">
<div class="app-div container">
<div class="form-div container">
<form method="post">
{% csrf_token %}
{{ form }}
<button type="submit" class="btn">Post</button>
</form>
</div>
<div class="tutorial-container container">
<p class="container">Items will show up below.</p><hr></br>
</div>
<div class="todo-items-container container">
{% for item in all_todo_items %}
<div class="spawner container">
<p class="item-container container">>: {{ item }}</p>
<button class="delete-item-button btn" onclick="this.parentNode.remove();Todoitem.objects.filter(item={{ item }}).delete();">Del</button>
</div>
{% endfor %}
</div>
The view associated with this app:
from django.shortcuts import render
from django.http import HttpResponse, HttpResponseNotFound, HttpResponseRedirect #for handling basic responses
from .forms import TodoForm
from .models import Todoitem
def todo_view(request, *args):
all_todo_items = Todoitem.objects.all #grabbing items from the db and putting them in a variable
if request.method == "POST": #block for when info is being pushed thru view
form = TodoForm(request.POST)
if form.is_valid():
form.save() #posts to db hopefully
form = TodoForm()
return render(request, 'todo/base.html', context={
"form": TodoForm,
"all_todo_items": all_todo_items})
The associated model:
# always makemigration and migrate when models are changed
class Todoitem(models.Model):
#the modelform will pull from this list to populate the form fields/types/elements
item = models.CharField(max_length=200, blank=True)
def __str__(self):
return self.item
# when objects are called manually, they return their strings instead because of this
The error:
When I run this application and test it in chrome dev tools, this is what I get.
This makes little to no sense to me, and it doesn't really give me an obvious source of what the issue is, although I obviously suspect it involves the inline JS function I'm attempting to do, AKA
Todoitem.objects.filter(item={{ item }}).delete();
Again, I'd really appreciate any insight on what on earth is going wrong, whether it's a syntax thing, or I'm calling a variable in a method that doesn't work that way, or anything. Although, please be aware that I am a novice to this, and I haven't heavily used Javascript yet, and I am still quite new to the DOM and advanced Python stuff.
Thank you.
edit: I'm running Windows as my main OS, and the Django webserver is running Python 3.8.2, and Django 3.0.4. This is contained within a virtual environment. All other parts of the Django webserver work perfectly. It also boots up normally.
Deleted the previous question to clarify it a bit.
I have A Django powered website and a template with dynamically populated table.
Template:
<table id = 'my_table'>
<thead>
......
</thead>
<tbody>
</tbody>
</table>
Javascript
$(function ()
{
refresh_vendors_list_table();
});
function refresh_vednors_list_table()
{
$.get("/ajax_refresh_vendors_list_table/", function(response)
{
$('#my_table').find('tbody').html(response);
});
}
View:
def ajax_refresh_vendors_list_table(request):
template = 'vendors/vendors_list_paginator.html'
vendors_qs = vendors_qs.filter(...)
context = {'vendors_qs':vendors_qs}
return render_to_response(template,
context,
context_instance=RequestContext(request))
Template for the table (vendors_list_paginator.html)
{% load el_pagination_tags %}
{% paginate vendors_qs %}
{% for vendor in vendors_qs %}
<tr>
......
</tr>
{% endfor %}
{% show_more_table %}
When pressing each row in the table, I am redirected to the corresponding vendor's profile. After editing the profile, I press the back button and coming back again to the table page. At this stage, I start to debug the $.get("/ajax_refresh_vendors_list_table/", data, function(response)
I also put a breakpoint at template = 'vendors/vendors_list_paginator.html' (let's call it breakpoint A)
A very strange behaviour is observed in $.get function: the corresponding Django view is not called (I don't see my app stop at breakpoing A) and yet $('#my_table').find('tbody').html(response); is executed directly as if the $.get function should execute successfully !!!!
In other words, $.get function is executed without any participation of server-side (I emphasize the word any. At first I thought that maybe I messed up the URLConf file and some other view was called. To check this, I cleared up the Django IDE console prior to pressing the browser back button and after pressing it, saw nothing in the Django IDE console meaning that $.get request successfully executed without ANY VIEW CALLED !!!)
Does it have something to do with pagination ? I am completely lost. Help please !
UPDATE
As per Sardorbek's answer, I've included the following decorators before the definition of ajax_refresh_vendors_list_table
#require_http_methods(["GET"])
#never_cache
def ajax_refresh_vendors_list_table(request):
...
Hm... Looks like your browser is caching this request. Trying wrapping this url with never_cache
never_cache(url(...))
I have a table where one of the columns is a Bootstrap Switch element which will be later used to turn on or off the corresponding element of that row.
<tbody>
{{#each sensorslist}}
<tr>
<td>{{name}}</td>
<td>{{mac}}</td>
<td>
<input type="checkbox" class="on-off-checkbox" checked={{state}} />
</td>
</tr>
{{/each}}
</tbody>
However, if I hard code one row, it all works great and the switch always appears. However, when I load from the sensors list in my database as shown, it only shows when I go to that page by a link (I am using Iron Router). If I press F5, it not shows the Bootstrap Switch.
I am not using autopublish. I am subscribing to the sensors collection in the client:
Sensors = new Mongo.Collection("sensors");
Meteor.subscribe('sensors');
And to load the data I use this helper:
Template.sensores.helpers({
'sensorslist': function(){
return Sensors.find();
}
});
And also to render the elements I have this as the rendered function:
Template.sensores.rendered = function (){
$('.on-off-checkbox').bootstrapSwitch({
'offColor': 'danger'
});
};
As I said, if I move around the webapp using only the menu, it works fine, if I refresh the page with F5 or simple go to that page using a direct link it will not work.
I noticed that if I use a setTimeout to execute that code inside the rendered, and add a sufficient amount of time, it will always work, so I guess I am missing something...
Thank you very much in advance!
I had encountered the same issue as yours ever before.
As I know, when Template.sensores is rendered, the data context sensorslist is not loaded completely so we can't query the DOM under sensorslist block.
But when you use setTimeout for a sufficient amount of time, it works as the DOM under sensorslist is rendered completely.
My solution for this is using another template additonally:
template(name='sensores')
...
<tbody>
{{#each sensorslist}}
{{> sensorsList}}
{{/each}}
</tbody>
...
template(name='sensorsList')
<tr>
<td>{{name}}</td>
<td>{{mac}}</td>
<td>
<input type="checkbox" class="on-off-checkbox" checked={{state}} />
</td>
</tr>
The Bootstrap Switch is placed in the rendered function of sensorsList template.
Since this template will be rendered as many times as the data context sensorslist's length, each DOM we want to manipulate should be scoped within its own template instance.
Template.sensorsList.rendered = function () {
this.$('.on-off-checkbox').bootstrapSwitch({
'offColor': 'danger'
});
};
I am developing a web application using Tornado framework. Currently I am working on a front end view.
This page actually displays a list of ids and a small note about that id. (Its a table with two columns)
id -------- Note
100 ----- Note on 100
101 ----- Note on 101
![Table with link on id and note on id][1]
The ids are displayed dynamically. When I click on the id (its a link- anchor tag), it should take me to another page which has all the details about that id.
Approach - I thought of creating a cookie which will store the value of the id clicked, and get the cookie in tornado handler and retrieve all information about that id and send it to the next view.
The code so far is -
HTML
<table class="table table-striped" id="tableid">
<tbody>
<tr>
<th>Config ID</th>
<th>Note</th>
</tr>
<tr>
{% for d in data %}
<td>
<a onclick = "configidCreateCookieFunction()" href="/SelectedConfig" name = "configid" id ="configid">{{ d[0] }}</a></td>
<td> {{ d[1]}}</td>
{% end %}
</tr>
<tbody>
</table>
</div>
So on clicking the link it would store the value of the id, and then the link points to a handler where I can get the cookie and retrieve more information about that id.
JavaScript
<script type="text/javascript">
function configidCreateCookieFunction(){
var cid = document.getElementById("configid").innerHTML;
sessionStorage ['configid'] = cid
}
Tornado code -
class SelectedConfigHandler(BaseHandler):
def get(self):
configid= None
configid = self.get_cookie("configid")
print configid
But this is not working. When I print the id -its getting displayed as None.
So basically I am not able to pull the value of the id which is a link, and the id is basically coming from the database
Please help me out. Is my approach correct?
Thanks in Advance
Where is the database here? since you use id why not accessing a database, say, MongoDB.
You have to know, that the cookie will be sent and received with your http messages, so you have to be aware of that, unless you want to make a local storage, but here, you have to garantee that, your users use an HTML5 browser, and send the data for the first time to be stored at the client end.
So the approache i suggest, is using the database, and use a hidden <input> and the value is the id.
I have a template that displays the list of items. It has one checkbox to each item. I want to be able to remove an item from a checkbox when a checkbox is ticked. So I would need a button that deletes an item once a checkbox is selected. Here is my template.
{% for item in items %}
<tr>
<td><input type="checkbox" name="item" value="{{item.pk}}" checked="checked"></td>
<td>{{item.tiptop_id}}</td><td>{{item.alternative_id}}</td><td>{{item.title}}</td>
<td>{{item.type}}</td><td>{{item.format}}</td>
</tr>
{% endfor %}
I would need probably know what to write in my views as well I guess.
Edit:
Not sure why it is still not deleting. check out my views. My edit order form. It is quiet huge. I thought the delete function would do the trick. Anyway take a look.
def edit_order(request, order_no):
# Alot of code here
if request.method == 'POST':
form = forms.OrderForm(request.POST, instance = order)
if form.is_valid() and save_item is not None:
form.save(True)
request.user.message_set.create(message = "The order has been updated successfully.")
return HttpResponse("<script language=\"javascript\" type=\"text/javascript\">window.opener.location = window.opener.location; window.close();</script>")
if status is not None and contact is not None and save_status is not None and delete_item is not None:
try:
for id in status_items:
item = models.StorageItem.objects.get(pk = id)
delete_item = item
delete_item.delete()
current_status = models.ItemStatusHistory(item = item, contact = contact, status = status,
user = request.user)
current_status.save()
except:
pass
request.user.message_set.create(message = "Status successfully changed for {0} items".format(len(status_items)))
You need to write a view that gets the POST data, finds out which checkboxes have been checked, and then deletes the items from the database matched by id.
You probably also want to wrap the view in a decorator to make sure the user has permission to delete things, or check the logged-in user is the same as the owner of the item to be deleted, if that's how you want to do thing.
Or you could use Django's forms framework to handle some of the heavy work.
Deleting objects from the database is in the db model documentation.
These things aren't totally trivial so don't wait too long here for a full solution - get hacking!
[Edit]:
The real issue is being able to delete items from a database on a form submission, not removing rows from a HTML table. See "A Simple Form-Handling Example" on this pageTutorial for form submissions in Django.
[/Edit]
Here's an example you can copy into a .html file on your computer and open in a web browser. It's using simple JavaScript. For something like this, I prefer to use jQuery, but depending on your use, it may be more overhead than you prefer. However, if you need to do a multitude of client-side programming, I highly recommend using jQuery.
Note: I think it's a little messy using parentNode.parentNode.parentNode, but this example purposely uses a table/checkbox configuration as expressed in the original post.
Preferably, I'd assign Id's to the table's rows that correlate with each checkbox so they're easier to access.
I also included a <input type="button"> because it was asked for in the original post. You may want to consider assigning the onclick= event to each checkbox so the user can remove the items as soon as they're clicked. But that's a preference.
<html>
<head>
<script>
function hideCheckedRows() {
var checkboxes = document.getElementsByName("item");
var checkboxes_to_remove = new Array();
var count = 0;
for (var i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked == true) {
checkboxes_to_remove[count++] = checkboxes[i];
}
}
for (var i = 0; i < checkboxes_to_remove.length; i++) {
cbx = checkboxes_to_remove[i];
// parentNode.parentNode.parentNode is the <tr>
// parentNode.parentNode is the <td> containing the checkbox
cbx.parentNode.parentNode.parentNode.removeChild(
cbx.parentNode.parentNode);
}
}
</script>
</head>
<body>
<table>
<tr name="table_row">
<td><input type="checkbox" name="item" value="Check1"></td>
<td>Id1</td><td>Alt_Id1</td><td>Title1</td>
<td>Type1</td><td>Format1</td>
</tr>
<tr name="table_row">
<td><input type="checkbox" name="item" value="Check2"></td>
<td>Id2</td><td>Alt_Id2</td><td>Title2</td>
<td>Type2</td><td>Format2</td>
</tr>
<tr name="table_row">
<td><input type="checkbox" name="item" value="Check3"></td>
<td>Id3</td><td>Alt_Id3</td><td>Title3</td>
<td>Type3</td><td>Format3</td>
</tr>
</table>
<input type="button" value="Click to remove checkboxes!"
onclick="hideCheckedRows();"/>
</body>
</html>
Edit:
If you want the item deleted from the database, we need more information. We need to know what kind of database being used and what the server-side code that handles the submit button's "POST" looks like. This example will delete the checkbox from the table in the user's web browser, but it will have no effect on whatever on the database.
You're doing it wrong :) Create a view only for deleting. Send in POST or GET id of the element (or in url), remove the element from db and then as response send your list without the deleted element.
Something like this :
def delete_element(request, id):
el = get_object_or_404(Element, id=id)
if el:
el.delete()
html = render_list(request)
if request.is_ajax():
result = simplejson.dumps({
"html": "html",
}, cls=LazyEncoder)
return HttpResponse(result, mimetype='application/javascript')
def render_list(request):
elements = Element.objects.all()
return render_to_string(template_name, RequestContext(request, {
"element" : elements, })
And then in your template you first call url of delete function with javascript and then on success you update your template with data['html'].
You can ask this guy : http://twitter.com/sasklacz as he's writing some tutorials on ajax in django to give you the exact code needed.