I'm attempting to access the contents of a cell, within my django template using javascript.
The table can contain x rows. When any of the "click to show" links (in the TEST column) are clicked, I would like the row's {{ i.description }} to be passed through to my views (ajax_test function), the "click to show" link to disappear and be replaced with the returned html from the view. I've included my terrible javascript attempt, knowing full well that this doesn't do what I'm asking. If anyone could help, it would be very much welcomed.
Template:
{% extends "myapp/base.html" %}
{% block content %}
<p>You searched for: <strong>{{ query }}</strong></p>
{% if elements %}
<p>Found {{ elements|length }} Element{{ elements|pluralize }}.</p>
<div class="datagrid"><table>
<thead><tr>
<th>Name</th>
<th>Description</th>
<th>Type</th>
<th>Parameter Arguments</th>
<th>TEST</th>
<tbody>
{% for i in elements %}
<tr class="{% if forloop.counter|divisibleby:2 %}alt{% else %}{% endif %}">
<td>{{ i.name }}</td>
<td><div id="desc">{{ i.description }}</div></td>
<td>{{ i.type }}</td>
<td>{% for a in i.parameter.all %}{{ a.argument }}{% endfor %}</td>
<td><div class="clicks">click to show</div></td>
</tr>
{% endfor %}
</tbody>
</table></div>
{% endblock %}
Javascript (this is where I haven't got a clue really, the code below is a failed attempt). The code is included as a static file (ajax_test.js, which is loaded from base.html)
$(function(){
var myCell = document.getElementById("desc");
var cellContents = (myCell.innerHTML);
$('.clicks').click(function() {
$.ajax({
type: "POST",
url: "/myapp/test_ajax/",
data: {
'ajax_data' : cellContents,
'csrfmiddlewaretoken' : $("input[name=csrfmiddlewaretoken]").val()
},
success: ajaxSuccess,
dataType: 'html'
});
});
});
function ajaxSuccess(data, textStatus, jqXHR)
{
$('.here').html(data); //I would like to put this in the "click to show" cell. I was placing it in a random div for testing.
}
test_ajax view:
def ajax_test(request):
if request.POST:
ajax_data = request.POST['ajax_data']
ajax_query = #some generic lookup using ajax_data
return render_to_response('myapp/ajax_test.html', {'ajax_query': ajax_query})
ajax_test template (to be returned to the cell):
{{ ajax_query }}
Try this:
html
<td><div class="clicks"><a class="click-to-show" href="#" data-desc="{{ i.description }}">click to show</a></div></td>
javascript
$(function(){
$('.click-to-show').click(function(e) {
$.ajax({
type: "POST",
url: "/myapp/test_ajax/",
data: {
'ajax_data' : $(e.target).attr("data-desc"),
'csrfmiddlewaretoken' : $("input[name=csrfmiddlewaretoken]").val()
},
success: function ajaxSuccess(data, textStatus, jqXHR) {
$(e.target).parent().html(data);
},
dataType: 'html'
});
});
});
As you can see we keep the DOM relative to link element all the time, since we render rows multiple times, this will make sure we update the correct row.
Related
I want to dynamically filter my querySet using the value of my input field with Ajax.
Right now I’m trying something like this:
Template:
<div class="card mt-3">
<div class="card-body">
<form action="" form="get">
<input data-url="{% url 'klantbeheer' %}" class="zoekklanten" name="q" type="text" placeholder="Zoek op contactnaam...">
</form>
{% if object_list %}
<div class="single-table">
<div class="table-responsive">
<table class="table text-center">
<thead class="text-uppercase bg-dark">
<tr class="text-white">
<th scope="col">Bedrijfsnaam</th>
<th scope="col">Contactnaam</th>
<th scope="col">Locatie</th>
<th scope="col">Actie</th>
</tr>
</thead>
<tbody>
{% for user in object_list %}
<tr>
<td>{{ user.bedrijfsNaam }}</td>
<td>{{ user.contactNaam }}</td>
<td>{{ user.adres }}</td>
<td>
<i class="fas fa-edit"></i>
<i class="fas fa-trash-alt"></i>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% else %}
<p>Geen gebruikers gevonden</p>
<p>
Maak klant aan
</p>
{% endif %}
</div>
</div>
View:
class ManageUserView(SuperUserRequired, ListView):
model = User
template_name = 'webapp/klant/beheerklant.html'
#Query based upon contactName
def get_queryset(self, query):
#Contactnaam bevat QUERY EN superuser is vals.
object_list = self.model.objects.filter(contactNaam__icontains=query).exclude(is_superuser=True)
return object_list
def post(self, request, *args, **kwargs):
query = self.request.POST.get('query', '')
print(query)
self.get_queryset(query)
Jquery:
$('.zoekklanten').on('keyup', function(){
var url = $(this).data('url')
var query = $(this).val();
$.ajax({
url:url,
type:'POST',
data:{
'query':query
},
dataType: 'json',
beforeSend: function(xhr, settings){
if(!csrfSafeMethod(settings.type) && !this.crossDomain){
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
},
success: function(result){
},
error: function(data){
}
})})
I’m getting "get_queryset() missing 1 required positional argument: ‘query’ " error. I assume calling the get_queryset function inside a post request function isn’t the way to go about this.
What would be the best way to filter dynamically using Ajax in Django?
Your problem is that the method ListView.get_queryset() is normally defined without any extra parameter, so when it's called by Django implicitly, it does not pass any value to the new extra parameter query you have added there.
So either use self.request.POST.get('query', '') in get_queryset() directly instead of passing it there as a parameter or add a default value to the parameter.
However using a user input and passing it directly into SQL query is risky as it might lead to an SQL injection (even though Django tries to sanitize the value).
def get_queryset(self, query=None):
# either set default value for `query` here^
if not query:
return super().get_queryset()
# or take the value from `POST` here instead of passing it as a parameter to the method
query = self.request.POST.get('query')
object_list = self.model.objects.filter(contactNaam__icontains=query).exclude(is_superuser=True)
return object_list
Using bootstrap I am working with the collapse function to collapse my table sort of like this. I am hooking into the collapse js with the following:
<script type="text/javascript">
$('#demo1').on('shown.bs.collapse', function () {
that = $(this);
$.ajax({
url:'{{ (path('orders')) }}',
type: "POST",
dataType: "json",
data: {
"id": "1"
},
async: true,
success: function (data)
{
console.log(data)
$('div#demo1').html('<img src="data:image/png;base64,' + data.output + '" />');
}
});
return false;
});
</script>
How can I either pass a value to that function or get the name of the dom element that passed it (i.e. #demo1)? I basically need to pass a value to pass as the id value to the ajax POST.
I cannot statically map the values as I'm just generating the rows based off a database return. The twig is
{% for x in search%}
<tr data-toggle="collapse" data-target="#demo{{ loop.index }}" class="accordion-toggle">
<th>{{ x.id}}</th>
<tr>
<td colspan="5" class="hiddenRow">
<div class="accordian-body collapse" id="demo{{ loop.index }}">
Demo{{ loop.index }} data
</div>
</td>
</tr>
{% endfor %}
Basically in the above I'm needing access to x.id to pass to the bootstrap jQuery
If you're targeting multiple collapses, you should use class selector instead of the id selector:
$(function(){
$('.accordian-body.collapse').on('show.bs.collapse', function(e) {
...
});
});
You can get the collapsed DOM element id from e.target.id.
// If using your example:
// if you click on 1st row you will get:
demo1
// if you click on 2nd row you will get:
demo2
// if you click on 3rd row you will get:
demo3
If you have more data you need to grab other than the id, you can bind the data using data- when you construct your HTML, and then jQuery on the target object and go from there:
HTML
<div class="accordian-body collapse" id="demo{{ loop.index }}"
data-index="{{ loop.index }}">
Demo{{ loop.index }} data
</div>
JavaScript
$('.accordian-body.collapse').on('show.bs.collapse', function(e) {
let $collapsedTarget = $(e.target),
index = $collapsedTarget.data('index');
});
I am not sure if I have answered your question or not, as your question is not clear to me either.
This is not a clear question, as the answer depends...
It should be easy, in Javascript you set up a variable outside of the function and declare it so it is global. I.e.
<script>let variable;</script>
or
<script>let variable = 0;</script>
or
<script>let variable = 'word';</script>
Then whether hard code change it or change it via a form entry or select item, as it is global it will be accessible within your function as variable.
You could try something like this:
{% for x in search <script>let variable=x;</script> %}
Just in case someone wants to see the end code (based off David Liang's answer) for how to do this...
twig:
{% for x in search%}
<tr data-toggle="collapse" data-target="#demo{{ loop.index }}" class="accordion-toggle">
<th>{{ x.id}}</th>
<tr>
<td colspan="5" class="hiddenRow">
<div class="accordian-body collapse" id="demo{{ loop.index }}" data-index="{{ x.id }}">
Loading...
</div>
</td>
</tr>
{% endfor %}
script:
<script type="text/javascript">
$('.accordian-body.collapse').on('shown.bs.collapse', function (e) {
let $collapsedTarget = $(e.target),
record_id = $collapsedTarget.attr('data-index');
$.ajax({
url:'{{ (path('orders')) }}',
type: "POST",
dataType: "json",
data: {
"id": record_id.toString()
},
async: true,
success: function (data)
{
console.log('Record ID: ' + record_id);
console.log(data)
$(e.target).html('<img src="data:image/png;base64,' + data.output + '" />');
}
});
return false;
});
</script>
and it will replace "Loading..." with whatever you put in the .html() (in my case the img tag).
I am having problems trying to update a block of my site (which includes another templates) by issuing an Ajax call. The that needs to be updated works just fine, but the JS script that is inside that template does not work (before, I was just adding the full request to my template, but that caused to have twice the content of the parsed template, but JS scripts were working).
PD: I am kind new to JS and have some experience with Django (still just digging in the world of Web Apps development).
My template:
{% load staticfiles %}
<script>
$(document).ready(function() {
var current_item_id = null;
var current_item_title = null;
var selected_items = null;
// Selección en las tablas
$('.table tr').on('click', function() {
$(this).toggleClass('selected');
});
// Comportamiento del toolbar flotante
$('.table tr').on('click', function() {
selected_items = $('.selected');
current_item_id = $(this).attr('id');
current_item_title = $(this).attr('name');
if (selected_items.length === 0) {
$('.modify-big').attr('disabled', true);
$('.delete-big').attr('disabled', true);
}
else if (selected_items.length > 1) {
$('.modify-big').attr('disabled', true);
}
else {
$('.modify-big').attr('disabled', false);
$('.delete-big').attr('disabled', false);
}
});
});
</script>
<div id='notifications'>
{% if notifications %}
<table class="table">
<thead>
<tr>
<!--<th class="text-left table-id">ID del Item</th>-->
<th class="text-left">Item</th>
<th class="text-left">Link de descarga</th>
<th class="text-left">Plantilla</th>
</tr>
</thead>
<tbody class="table-hover">
{% for notification in notifications %}
<tr id='{{ notification.item_id }}' name='{{ notification.item_title }}'>
<!--<td class='text-left'>{{ notification.item_id }}</td>-->
<td class='text-left'>
<a class='tooltip-right' href='#' tooltip='Ver item'>
<img src="{% static 'images/icon_notification_details.png' %}">
</a>
{{ notification.item_title }}
</td>
<td class='text-left'>
{% if notification.download_link %}
<a href='{{ notification.download_link }}' target='_blank'>{{ notification.download_link }}</a>
{% else %}
---
{% endif %}
</td>
<td class='text-left'>{{ notification.email_template.name }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if first_time %}
<p class='info-msg first-time'>Últimas notificaciones agregadas.</p>
{% else %}
<div class="pagination">
<span class='step-links'>
{% if notifications.has_previous %}
<button class='previous' onclick='search_notifications("", "{{ notifications.previous_page_number }}");'></button>
{% else %}
<button class='previous' disabled></button>
{% endif %}
<span class='current'>
Página {{ notifications.number }} de {{ notifications.paginator.num_pages }}
</span>
{% if notifications.has_next %}
<button class='next' onclick='search_notifications("", "{{ notifications.next_page_number }}");'></button>
{% else %}
<button class='next' disabled></button>
{% endif %}
</span>
</div>
{% endif %}
{% else %}
<p class="info-msg info">No se han encontrado notificaciones.</p>
{% endif %}
</div>
Ajax Call:
search_notifications = function(first_time=false, page=null) {
show_div_loader($('#notifications'));
$.ajax({
url: "{% url 'notifications_loader' %}",
data: {
'search_notifications_query': $('#search-notifications-query').val(),
'search_notifications_listing': $('#search-notifications-listing option:selected').val(),
'first_time': first_time,
'page': page,
},
success: function(data){
// Solo necesitamos actualizar la sección #notifications
data = $(data).filter('#notifications').html();
notifications.html(data);
hide_div_loader($('#notifications-container'));
}
});
};
My view:
def notifications_loader(request):
[...]
return render(request, 'notifications_search_result.html', {'notifications': notifications, 'first_time': first_time})
As you can see in the Ajax sucess function, I do:
data = $(data).filter('#notifications').html();
notifications.html(data);
Before, I was doing:
notifications.html(data);
This last one was adding twice the parsed template but JS script inside it were working.
What I am doing wrong?
Thanks in advance.
EDIT:
I don't know if is the best way, but I've added a 'container' to my code and just insert parsed themplate there:
In my main template:
<div id='notifications-container'>
<!--{% include 'notifications_search_result.html' %}-->
</div>
JS scripts are working again and I don't have twice the parsed template. Now, for I was reading I think this is not the best way to work with Django and Ajax or I am wrong? Maybe I just need to return the view as JSON and replace only the needed data?
Still I have doubs about Ajax+Django and best practices way.
Thanks.
When doing AJAX using Django I do it this way:
Define a route(view) that will serve your default template containing your ajax call script.
Add another route(view) for the ajax call:
def auto_complete(request):
# do some actions and put the results in var
return HttpResponse(simplejson.dumps(var),content_type='application/json')
And you will call the second route in your ajax call
Hope it helps you
I am working on a web app which is built on flask,postgreSQL. It is used to collect lab data and manage the data collected. There are two pages in this app, which I have attached are used to show lab data of some labs chosen by the user by querying from a database.
One page(admin_select_lab_for_data.html) enables the user to select some existing labs and redirects the user to the other page(admin_edit_data.html), which shows all the data in the selected labs. When I select the labs and click the button go to lab, the browser fails to redirect to (admin_edit_data).
The outputs printed by python are all correct(which proves that in flask, the redirection happens,lab_ids are passed correctly and all the related data are queried correctly). The http request returns 200 ok. However, the browser just stay on the same page and ajax POST request alerts the error(which is inconsistent with the fact that the data have already been received by admin_select_lab_for_data).
main.py
# review/edit Data
#app.route('/admin_select_lab_for_data',methods=['GET','POST'])
def admin_select_lab_for_data():
if request.method=='POST':
lab_ids = []
jsonData = request.get_json()
lab_ids = jsonData['lab_ids']
return redirect(url_for('admin_edit_data',lab_ids={'lab_ids':lab_ids}))
lab_list = []
db_session = db.get_session()
for lab in db_session.query(schema.Lab_info).all():
data_num = 0
query_rows = db_session.query(schema.Lab_rows).filter(schema.Lab_rows.lab_id==lab.lab_id)
for r in query_rows:
data_num += db_session.query(schema.Lab_data).filter(schema.Lab_data.row_id==r.row_id).count()
lab_list.append({'lab_id':lab.lab_id,'lab_name':lab.lab_name,'class_name':lab.class_name,'prof_name':lab.prof_name,'data_num':data_num})
return render_template('admin_select_lab_for_data.html',lab_list=lab_list)
# review/edit Data
#app.route('/admin_edit_data')
def admin_edit_data():
# Get a list of lab_ids that are needed to be retrieved
lab_ids = ast.literal_eval(request.args['lab_ids'])['lab_ids']
# lab_ids = ['test1_101_C','test2_101_N']
lab_data = []
lab_data_by_student = []
row_names_list = []
err_msg = ''
db_session = db.get_session()
#Group row data according to row_name
query_rows = db_session.query(schema.Lab_rows).filter(schema.Lab_rows.lab_id==lab_ids[0]).order_by(schema.Lab_rows.row_order)
for r in query_rows:
lab_data.append({'row_name':r.row_name,'row_data_list':[]})
row_names_list.append(r.row_name)
for lab_id in lab_ids:
query_rows = db_session.query(schema.Lab_rows).filter(schema.Lab_rows.lab_id==lab_id).order_by(schema.Lab_rows.row_order)
index = 0
#Check whether these labs are compatitble with each other(the number of rows and the names of rows must be the same)
if query_rows.count()!=len(row_names_list):
err_msg = lab_ids[0]+' and '+lab_id+' are incompatible: the number of rows is different-'+str(query_rows.count())+' and '+str(len(row_names_list))
else:
for r in query_rows:
if (row_names_list[index]!=r.row_name):
err_msg = lab_ids[0]+' and '+lab_id+' are incompatible:'+row_names_list[index]+' and '+r.row_name+' are different row names'
break
else:
query_datas = db_session.query(schema.Lab_data).filter(schema.Lab_data.row_id==r.row_id).order_by(schema.Lab_data.data_id)
for data in query_datas:
lab_data[index]['row_data_list'].append({'lab_id':lab_id,'student_name':data.student_name,'data_id':data.data_id,'row_data':data.row_data})
index+=1
if err_msg!='':
return render_template('admin_edit_data.html',lab_data=lab_data,student_data=lab_data_by_student,lab_ids=lab_ids,err_msg=err_msg)
#Group row data according to student_name
for row in lab_data:
#sort row_data_list to make all the data across different lists
sorted(row['row_data_list'],key=lambda element:element['data_id'])
# if list is empty, add student names into it
if not lab_data_by_student:
for data in row['row_data_list']:
lab_data_by_student.append({'student_name':data['student_name'],'lab_id':data['lab_id'],'row_data_list':[]})
for i in range(len(row['row_data_list'])):
data = row['row_data_list'][i]
lab_data_by_student[i]['row_data_list'].append({'row_name':row['row_name'],'row_data':data['row_data']})
print('\n\n\n')
print(lab_ids)
print('\n\n\n')
print(lab_data)
print(lab_data_by_student)
print(lab_ids)
print(err_msg)
print('\n\n\n')
return render_template('admin_edit_data.html',lab_data=lab_data,student_data=lab_data_by_student,lab_id=lab_id,err_msg=err_msg)
admin_select_lab_for_data.html
{% extends "admin_home.html" %}
{% block head %}
<meta charset="UTF-8">
<title>Select lab for data</title>
<script>
$(document).ready(function(){
$('button[name=go_to_lab]').click(function(){
var lab_ids = [];
var checkboxes = document.getElementsByTagName('input')
for (var i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].type == 'checkbox' && checkboxes[i].checked) {
lab_ids.push($(checkboxes[i]).data('labid'));
}
}
if (lab_ids.length==0){
$('#error_message_no_lab_choose').show().delay(1000).fadeOut();
}
else{
$.ajax({
type: 'POST',
contentType: 'application/json',
dataType: 'json',
url: 'http://127.0.0.1:5000/admin_select_lab_for_data',
data: JSON.stringify({'lab_ids':lab_ids}),
success: function(result){
alert('Submit successfully');
},
error : function(result){
alert('Fail to submit');
console.log(result)
}
});
}
});
});
</script>
{% endblock %}
{% block content %}
{% if (lab_list|length)==0 %}
No lab exists<br>
{% else %}
<table class="table">
<thead>
<tr>
<th class="tg-yw4l">Lab Index</th>
<th class="tg-yw4l">Lab Name</th>
<th class="tg-yw4l">Class Name</th>
<th class="tg-yw4l">Professor Name</th>
<th class="tg-yw4l">Number of data available</th>
<th class="tg-yw4l">Choose</th>
</tr>
</thead>
<tbody>
{% for lab in lab_list %}
<tr>
<th class="tg-yw4l">{{loop.index}}</th>
<td class="tg-yw4l">{{lab.lab_name}}</td>
<td class="tg-yw4l">{{lab.class_name}}</td>
<td class="tg-yw4l">{{lab.prof_name}}</td>
<td class="tg-yw4l">{{lab.data_num}}</td>
<td class="tg-yw4l"><input type="checkbox" data-labid="{{lab.lab_id}}"></td>
</tr>
{% endfor %}
</tbody>
</table>
<br>
<button name="go_to_lab">Go to lab</button>
<div class="temporary_message" id="error_message_no_lab_choose">You need to select one lab</div>
{% endif %}
{% endblock %}
admin_edit_data.html
{% extends "admin_home.html" %}
{% block head %}
<meta charset="UTF-8">
<title>Edit Data</title>
{% endblock %}
{% block content %}
{% if lab_data|length==0 %}
<h1> No data available for this lab </h1>
{% elif err_msg!="" %}
<h1> {{err_msg}} <h1>
{% else %}
<table class="table" id={{lab_id}} name={{lab_data|length}}>
<thead>
<tr>
<th>Index</th>
<th>Student Name</th>
<th>Lab ID</th>
{% for r in lab_data %}
<th>{{r["row_name"]}}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for s in student_data %}
<tr>
<th>{{loop.index}}</th>
<td><input value={{s["student_name"]}}></td>
<td>{{s["lab_id"]}}</td>
{% for i in range(lab_data|length) %}
<td><input value={{s["row_data_list"][i]["row_data"]}}></td>
{% endfor %}
<td><button name=save_all>Save All</button></td>
<td><button name=delete>Delete</button></td>
</tr>
{% endfor %}
</tbody>
</table>
<button>Return</button>
{% endif%}
{% endblock %}
You can never achieve a page redirect as a result of an Ajax post request.
You either have to submit a regular form post and do the redirect or you need to return a result from the Ajax call and change the page using window.location in the browser.
I'm trying to develop a simple task list with PHP. I have a list of tasks using twig http://twig.sensiolabs.org/ for each loop. The checkbox is checked if the task is marked as done. My question is how can I attach to each checkbox a script to make ajax calls with different parameters so the server knows which task is being checked?
<table>
<thead>
<tr>
<th>Task</th>
<th>Valmis</th>
</tr>
</thead>
<tbody>
{% for task in tasks %}
<tr>
<td>{{task.description}}</td>
<td><input type="checkbox" {{ task.done ? "checked" : "" }} ></td>
</tr>
{% endfor %}
</tbody>
</table>
I add a class for enable ajax call to a form, and make it via a POST to the server and manage the response, like this example:
Javascript code:
$(document).ready(function() {
$('.lista-proroghe-item').each(function(){
$(this).find('.perform-ajax-request-button').click(function(){
var form = $(this).closest("form"), action = form.attr('action');
var currentButton = $(this);
var currentMessage = $(this).next('.proroga-messaggio');
$(this).attr('disabled', 'disabled');
$(this).html('Processing...');
$.ajax({
url: action,
type: "POST",
data: form.serialize(),
success: function(data) {
// console.log(data);
$(currentButton).hide();
$(currentMessage).html('<span>'+data.description+'<br>'+data.extendedMessage+'</span>');
}
});
e.preventDefault();
return false;
})
})
});
Twig code:
{% for extension in assignation.contract.contractModificationDurations %}
<li class="lista-proroghe-item">
<form action="{{ path('contractExtension', {'idContractModification' : extension.idContractModification, 'userid':user.id }) }}">
Element
<button class="button small perform-ajax-request-button right"><span class="refresh">to task</span></button>
<div class="proroga-messaggio"></div>
</form>
</li>
{% else %}
<li>
Nessuna proroga trovata
</li>
{% endfor %}
Hope this help.