Flask: don't redirect to a url() - javascript

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.

Related

using inline javascript inside a python loop

I have a list of entries. Insider the list of entries I have a link to individual entries. I have an update form that updates one field.
I want to open the update form inside a new window. The user then submits the form is directed to a success page which currently has a timed close.
The issue I am encountering is with the inline JavaScript which opens the form in a new window. When I attempt to assign the kwarg on the URL it the JavaScript does not change with each entry in the list.
I have limited JavaScript, and jQuery knowledge.
I just want the JavaScript to open a new window for each update form.
I have included the template below.
<thead>
<tr>
<th>Script No.</th>
<th>Entered on</th>
<th>Patient</th>
<th>Email</th>
<th>Product</th>
<th>Form</th>
<th>Units</th>
<th>Dispensing Price</th>
<th>Status</th>
<th>Dispatch Date</th>
<th>Entered by</th>
<th></th>
</tr>
</thead>
<tbody>
{% for script in scripts %}
<tr>
<td>{{script.pk}}</td>
<td>{{script.entered_on}}</td>
<td>{{script.patient}}</td>
<td>{{script.patient.email}}</td>
<td>{{script.product}}</td>
<td>{{script.product.form.description}}</td>
<td>{{script.quantity}}</td>
<td>$ {{ script.dispensing_price }}</td>
{% if script.dispatch_date is not none %}
<td>{{script.get_script_status_display}}</td>
{% else %}
<td>{{script.get_script_status_display}}</td>
{% endif %}
<td><a href ="#" onclick="updateStatus()" style="float:right;">
<script>var url = "{% url "script:script_update_status" script.pk %}";
function updateStatus() {var myWindow = window.open(url, "_blank", "toolbar=yes,scrollbars=yes,resizable=yes,top=500,left=500,width=400,height=250");}
</script></td>
{% if script.dispatch_date is none %}
<td>Not Sent</td>
{% else %}
<td>{{script.dispatch_date}}</td>
{% endif %}
<td>{{script.created_by.first_name}} {{script.created_by.last_name}}</td>
<td><small>View | Edit | Lab Label | <a href="{% url 'label:dispense_label' script.pk %}">Dispense Label
</a> | Shipping Label | Repeat</small></td>
</tr>
{% endfor %}
</tbody>
</table> ```
Kind Regards
AC

Sort bootstrap table by column specified with index

By default bootstrap table is sorted using the first column. This setting can be changed with data-sort-name="column_name" data-sort-order="asc/desc" as mentioned here link.
In my scenario the column names are created dynamically based on a python script. As such the data-sort-name option is not helpful.
Is it possible to specify the column I want the default sorting to be done by the column index?
<table id="table_example"
class="table table-striped"
data-toggle="table"
<thead>
<tr>
{% for col in column_names %}
<th>{{col}}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in row_data %}
<tr>
{% for col, row_ in zip(column_names, row) %}
<td>{{row_}}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
I've solved the problem by adding the following script with column index 1:
<script>
$(document).ready(function () {
$('#table_example').DataTable({
"order": [[ 1, "desc" ]]
});
});
</script>

Django dynamic formset only saving last entry

I have created a page where users can enter details to forms relating to a parent model (Patient) and two child models (CurrentMed and PastMed) linked to that parent.
Both the child forms use dynamic formsets where the user can add or delete rows to the form. My problem is only the last row of the currentmed and pastmed formsets is saving to my database when the user submits the form?
models.py
class Patient(TimeStampedModel):
# get a unique id for each patient - could perhaps use this as slug if needed
patient_id = models.UUIDField(primary_key=True, unique=True, default=uuid.uuid4, editable=False)
name = models.CharField("Patient Name", max_length=255)
creator = models.ForeignKey(
settings.AUTH_USER_MODEL,
null=True,
on_delete=models.SET_NULL)
class Med(TimeStampedModel):
med_name = models.CharField(“med name“, max_length=20)
dose = models.IntegerField("Dose (mg)", default=0)
timepoint = models.CharField(
"timepoint", max_length=20,
choices=[('current','current'), ('past', 'past')], default='unspecified')
patient = models.ForeignKey(Patient, on_delete=models.CASCADE)
class Meta:
abstract = True
class CurrentMed(Med):
timepoint = models.CharField(
"", max_length=20,
choices=[('current','current'), ('past', 'past')], default='current')
class PastMed(Med):
timepoint = models.CharField(
"", max_length=20,
choices=[('current','current'), ('past', 'past')], default='past')
forms.py
from .models import CurrentMed, PastMed, Patient
CurrentmedFormSet = inlineformset_factory(
Patient, CurrentMed, fields=("med_name", "dose",), extra=2)
PastmedFormSet = inlineformset_factory(
Patient, PastMed, fields=("med_name", "dose",), extra=2)
class PatientForm(ModelForm):
class Meta:
model = Patient
fields = ['name', 'sex', 'age', 'diagnosis']
views.py
class PatientAddView(LoginRequiredMixin,TemplateView):
model = Patient
template_name = "../templates/patient/add.html"
def get(self, *args, **kwargs):
patient_form = PatientForm
currentmed_formset = CurrentmedFormSet(queryset=CurrentMed.objects.none())
pastmed_formset = PastmedFormSet(queryset=PastMed.objects.none())
return self.render_to_response({'currentmed_formset': currentmed_formset,
'pastmed_formset': pastmed_formset,
'patient_form': patient_form})
def post(self, *args, **kwargs):
form = PatientForm(data=self.request.POST)
currentmed_formset = CurrentmedFormSet(data=self.request.POST)
pastmed_formset = PastmedFormSet(data=self.request.POST)
if form.is_valid():
patient_instance = form.save()
if currentmed_formset.is_valid():
med_name = currentmed_formset.save(commit=False)
for med in med_name:
med.patient = patient_instance
med.save()
if pastmed_formset.is_valid():
med_name = pastmed_formset.save(commit=False)
for med in med_name:
med.patient = patient_instance
med.save()
return redirect(reverse(
'patient:treatment_detail',
kwargs={"patient_id": patient_instance.patient_id}))
html
{% extends "base.html" %}
{% load static %}
{% load crispy_forms_tags %}
{% block javascript %}
<script type="text/javascript" src="{% static 'js/jquery/dist/jquery-1.3.2.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/jquery.formset.js' %}"></script>
<script type="text/javascript">
$(function() {
$('#currentmeds_table tbody tr').formset({
prefix: 'current_meds'
})
});
$(function() {
$('#pastmeds_table tbody tr').formset({
prefix: 'past_meds'
})
});
</script>
<style type="text/css">
.delete-row {
margin-left:5px;
}
</style>
{% endblock %}
{% block content %}
<div>
<div class="entry">
<form id="form" method="POST">
<h1>Patient Details</h1>
{% csrf_token %}
<h3>Demographics</h3>
{{patient_form}}
<h3>Current Medication</h3>
<table id="currentmeds_table" border="0" cellpadding="0" cellspacing="5">
<thead>
<tr>
<th scope="col">Medication</th>
<th scope="col">Dose</th>
</tr>
</thead>
<tbody>
{% for form in currentmed_formset %}
<tr id="{{ form.prefix }}-row"></tr>
<td>
{% for fld in form.hidden_fields %}{{ fld }}{% endfor %}
{% if form.instance.pk %}{{ form.DELETE }}{% endif %}
{{ form.med_name}}
</td>
<td>{{ form.dose }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{{currentmed_formset.management_form}}
<h3>Past Medication</h3>
<table id="pastmeds_table" border="0" cellpadding="0" cellspacing="5">
<thead>
<tr>
<th scope="col">Medication</th>
<th scope="col">Dose</th>
</tr>
</thead>
<tbody>
{% for form in pastmed_formset %}
<tr id="{{ form.prefix }}-row"></tr>
<td>
{% for fld in form.hidden_fields %}{{ fld }}{% endfor %}
{% if form.instance.pk %}{{ form.DELETE }}{% endif %}
{{ form.med_name}}
</td>
<td>{{ form.dose }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{{pastmed_formset.management_form}}
<button type="submit">Create Patient</button>
</form>
</div>
</div>
{% endblock %}
The prefixes in the javascript were incorrect, found the correct ones by looking at the html source, instead of
$('#currentmeds_table tbody tr').formset({
prefix: 'current_meds'
})
Should have been
$('#currentmeds_table tbody tr').formset({
prefix: 'currentmed_set'
})

Problems using Ajax with Django

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

Extra Data error HTML Javascript Django

I'm trying to get my inputted data from the text boxes in the table in which will be turned into an array. After building this array, I need to convert it to JSON to decode in django/python and pass it along with an HTTP POST. The HTML code is below:
<html>
<head>
<script type="text/javascript">
function getValues(){
var arr = new Array(); //make an array
arr = document.getElementsByName("data_from_table"); //get all of the textboxs that were populated below using django
var newArr = new Array(); //make an array that we will transfer back to django with user edits
for(var i = 0; i < arr.length; i++){
newArr.push([arr[i].value]); //push the value of that row on to the array
}
JSON.dumps(newArr);
document.getElementById('list').value = JSON.stringify(newArr);
$.post('/login/', newArr);
}
</script>
</head>
<body>
<!-- <h1>{{ title }} is working!</h1> --> <!-- Keeping these for debugging purposes-->
<h1>Welcome Customer!</h1></br></br>
<form method="post">
{% csrf_token %}
<button type="submit" value="Logout" name='logout'>Logout</button>
<button type="submit" value="Check Out" name='checkout' onclick="getValues()">Checkout</button>
<input type="text" id="list" name="list" style="display:none">
</br>
</form>
<form method="get">
<table border="1">
<thead>
<tr>
<th>Item ID</th>
<th>Item</th>
<th>How Many We Have</th>
<th>Price</th>
<th>How Many?</th>
</tr>
</thead>
<tbody>
<tr>
<ul>
{% for table in products %}
<tr>
{% for data in table %}
{% if forloop.counter0 == 0 or forloop.counter0 == 1 or forloop.counter0 == 2 or forloop.counter0 == 3 %}
<td align="center"><input type="text" value='{{ data }}' readonly></td>
{% else %}
<td><input type="text" name="data_from_table" value=0></td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</ul>
</tr>
</tbody>
</table>
</form>
</body>
I get this strange error:
Extra data: line 1 column 3 - line 1 column 33 (char 2 - 32).
From research that I've done, I've found load(), loads() and dump(). I've try them and I can't seem to get them to work. Maybe I'm doing it wrong(highly possible). Any help would be greatly appreciated.

Categories