Django dynamic formset only saving last entry - javascript

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

Related

How can i save django dynamic formset data using forms and views

I am trying to save formset extra fields data using forms and views.
Eg:-Team has no.of players. so i want to add new player by click on add more button.The code i tried below. the problem is if i add more than one player at a time...it is saving last object value only
o/p for below code
models.py
from django.db import models
class Player(models.Model):
pname = models.CharField(max_length=50)
hscore = models.IntegerField()
age = models.IntegerField()
def __str__(self):
return self.pname
class Team(models.Model):
tname = models.CharField(max_length=100)
player= models.ManyToManyField(Player)
def __str__(self):
return self.tname
forms.py
from django import forms
from django.forms.formsets import formset_factory
class PlayerForm(forms.Form):
pname = forms.CharField()
hscore= forms.IntegerField()
age = forms.IntegerField()
PlayerFormset= formset_factory(PlayerForm)
class TeamForm(forms.Form):
tname= forms.CharField()
player= PlayerFormset()
views.py
from django.shortcuts import render, get_object_or_404,redirect
from .models import Player,Team
from .forms import TeamForm,PlayerFormset
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django import forms
from django.forms import formset_factory
def post(request):
if request.POST:
form = TeamForm(request.POST)
form.player_instances = PlayerFormset(request.POST)
if form.is_valid():
team= Team()
team.tname= form.cleaned_data['tname']
team.save()
if form.player_instances.cleaned_data is not None:
for item in form.player_instances.cleaned_data:
player = Player()
player.pname= item['pname']
player.hscore= item['hscore']
player.age= item['age']
player.save()
team.player.add(player)
team.save()
else:
form = TeamForm()
return render(request, 'new.html', {'form':form})
template: new.html
<html>
<head>
<title>gffdfdf</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<form action="" method="post" class="">
{% csrf_token %}
<h2> Team</h2>
{% for field in form %}
{{ field.errors }}
{{ field.label_tag }} : {{ field }}
{% endfor %}
{{ form.players.management_form }}
<h3> Product Instance(s)</h3>
<table id="table-product" class="table">
<thead>
<tr>
<th>player name</th>
<th>highest score</th>
<th>age</th>
</tr>
</thead>
{% for player in form.players %}
<tbody class="player-instances">
<tr>
<td>{{ player.pname }}</td>
<td>{{ player.hscore }}</td>
<td>{{ player.age }}</td>
<td> <input id="input_add" type="button" name="add" value=" Add More " class="tr_clone_add btn data_input"> </td>
</tr>
</tbody>
{% endfor %}
</table><button type="submit" class="btn btn-primary">save</button>
</form>
</div>
<script>
var i = 1;
$("#input_add").click(function() {
$("tbody tr:first").clone().find(".data_input").each(function() {
if ($(this).attr('class')== 'tr_clone_add btn data_input'){
$(this).attr({
'id': function(_, id) { return "remove_button" },
'name': function(_, name) { return "name_remove" +i },
'value': 'Remove'
}).on("click", function(){
var a = $(this).parent();
var b= a.parent();
i=i-1
$('#id_form-TOTAL_FORMS').val(i);
b.remove();
$('.player-instances tr').each(function(index, value){
$(this).find('.data_input').each(function(){
$(this).attr({
'id': function (_, id) {
var idData= id;
var splitV= String(idData).split('-');
var fData= splitV[0];
var tData= splitV[2];
return fData+ "-" +index + "-" + tData
},
'name': function (_, name) {
var nameData= name;
var splitV= String(nameData).split('-');
var fData= splitV[0];
var tData= splitV[2];
return fData+ "-" +index + "-" + tData
}
});
})
})
})
}
else{
$(this).attr({
'id': function (_, id) {
var idData= id;
var splitV= String(idData).split('-');
var fData= splitV[0];
var tData= splitV[2];
return fData+ "-" +i + "-" + tData
},
'name': function (_, name) {
var nameData= name;
var splitV= String(nameData).split('-');
var fData= splitV[0];
var tData= splitV[2];
return fData+ "-" +i + "-" + tData
}
});
}
}).end().appendTo("tbody");
$('#id_form-TOTAL_FORMS').val(1+i);
i++;
});
</script>
</body>
</html>
===============================================================
I am unable to save all objects. please correct it
The reason why you only can save first one of items is your added line id and name still is id_form-0-pname not change to id_form-1-pname(pattern is id_form-i-pname).
Another way to achieve this,use lib django-dynamic-formset,code is here:
news.html
<html>
<head>
<title>gffdfdf</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="/static/jquery.formset.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<form id="myForm" action="" method="post" class="">
{% csrf_token %}
<h2> Team</h2>
{% for field in form %}
{{ field.errors }}
{{ field.label_tag }} : {{ field }}
{% endfor %}
{{ form.player.management_form }}
<h3> Product Instance(s)</h3>
<table id="table-product" class="table">
<thead>
<tr>
<th>player name</th>
<th>highest score</th>
<th>age</th>
</tr>
</thead>
{% for player in form.player %}
<tbody class="player-instances">
<tr>
<td>{{ player.pname }}</td>
<td>{{ player.hscore }}</td>
<td>{{ player.age }}</td>
</tr>
</tbody>
{% endfor %}
</table>
<button type="submit" class="btn btn-primary">save</button>
</form>
</div>
<script>
$(function () {
$('#myForm tbody tr').formset();
})
</script>
</body>
</html>
It's look like
it's simple and work.
jquery.formset.js can be downloaded from github in here.Doc is here.
You can change the text and css of add\remove link with conf jquery.formset.js like:
/* Setup plugin defaults */
$.fn.formset.defaults = {
prefix: 'form', // The form prefix for your django formset
formTemplate: null, // The jQuery selection cloned to generate new form instances
addText: 'add another', // Text for the add link
deleteText: 'remove', // Text for the delete link
addCssClass: 'add-row', // CSS class applied to the add link
deleteCssClass: 'delete-row', // CSS class applied to the delete link
formCssClass: 'dynamic-form', // CSS class applied to each form in a formset
extraClasses: [], // Additional CSS classes, which will be applied to each form in turn
keepFieldValues: '', // jQuery selector for fields whose values should be kept when the form is cloned
added: null, // Function called each time a new form is added
removed: null // Function called each time a form is deleted
};
You need to loop over form.player_instances.
if request.POST:
form = TeamForm(request.POST)
form.player_instances = PlayerFormset(request.POST)
if form.is_valid():
team= Team()
team.tname= form.cleaned_data['tname']
team.save()
if form.player_instances.is_valid():
for item in form.player_instances:
player = Player()
player.pname= item.cleaned_data['pname']
player.hscore= item.cleaned_data['hscore']
player.age= item.cleaned_data['age']
player.save()
team.player.add(player)
team.save()
**Use this following for your new.html file and keep your other files unchanged like forms and models **
<html>
<head>
<title>gffdfdf</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.formset/1.2.2/jquery.formset.js"></script>
</head>
<body>
<div class="container">
<form method="post" class="">
{% csrf_token %}
<h2> Team</h2>
{% for field in form %}
{{ field.errors }}
{{ field.label_tag }} : {{ field }}
{% endfor %}
{{ form.players.management_form }}
<div style="display: none" > {{ form.players.empty_form }}</div>
<h3> Product Instance(s)</h3>
<table id="table-product" class="table">
<thead>
<tr>
<th>player name</th>
<th>highest score</th>
<th>age</th>
</tr>
</thead>
{% for player in form.players %}
<tbody class="player-instances" id="playerFrmTableId">
<tr>
<td>{{ player.pname }}</td>
<td>{{ player.hscore }}</td>
<td>{{ player.age }}</td>
<td> <input id="input_add" type="button" name="add" value=" Add More " class="tr_clone_add btn data_input"> </td>
</tr>
</tbody>
{% endfor %}
</table><button type="submit" class="btn btn-primary">save</button>
</form>
</div>
<script>
$("#input_add").click(function() {
let formCount = parseInt($('#id_form-TOTAL_FORMS').val());
var html = `<tr>
<td>{{ form.players.empty_form.pname }}</td>
<td>{{ form.players.empty_form.hscore }}</td>
<td>{{ form.players.empty_form.age }}</td>
<td> <input id="input_add" type="button" name="add" value=" Add More " class="tr_clone_add btn data_input"> </td>
</tr>`;
html = html.replace(/__prefix__/g, formCount);
$('#id_form-TOTAL_FORMS').val(formCount + 1);
$('#playerFrmTableId').append(html);
});
</script>
</body>
</html>

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

Display QuerySet result as Array

I'm asking a question about my HTML template. I displayed results from some QuerySet with Django and I would like to modify the display user aspect.
I don't know if I can make this kind of things with Django or if JavaScript lets to make that.
I have an HTML template :
<h2 align="center"> Affichage de toutes les fiches individuelles </align> </h2>
<br></br>
{% block content %}
<h4> Récapitulatif des 10 dernières fiches individuelles créées: </h4>
<ul>
{% for item in identity %}
<li>{{ item }}</li>
{% endfor %}
</ul>
<h4> Récapitulatif des 10 dernières fiches individuelles créées habitant en France: </h4>
<ul>
{% for item in identity_France %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endblock %}
From this view :
def Consultation(request) :
identity = Identity.objects.all().order_by("-id")[:10] #Les 10 dernières fiches créées
identity_France = Identity.objects.filter(country='64').order_by("-id")[:10] #Les 10 dernières fiches où la personne habite en France
context = {
"identity" : identity,
"identity_France" : identity_France,
}
return render(request, 'resume.html', context)
Is it possible to display the result as an array ? With column, tags for each column etc ... ?
Something like that :
Thank you !
EDIT :
I found that JQuery makes that : JQuery Array
You can do whatever you want inside {% for %} {% endfor %} template tags.
like, you could put your objects inside a table
<table>
<thead>
<tr>
<th>Field 1 Name</th>
<th>Field 2 Name</th>
</tr>
</thead>
<tbody>
{% for item in identity_France %}
<tr>
<td>{{ item.field1 }}</td>
<td>{{ item.field2 }}</td>
<tr>
{% endfor %}
<tbody>
</table>

list all image button clicked change image

every one ,I am using django's template language with javascript to do "clicked image button change image" ,however,the result I got was only can change the last button.I do not know why what's wrong about the code , here is my
indext.html
{% extends 'base.html' %}
{% load static %}
{% block title %}
Homepage - {{ block.super }}
{% endblock title %}
{% block content %}
<script>
function setColor(e, btn, color) {
var target = e.target,
count = +target.dataset.count;
target.style.backgroundColor = count === 1 ? "#7FFF00" : '#FFFFFF';
target.dataset.count = count === 1 ? 0 : 1;
}
var img_array = [ src="{% static "img/published.png" %}" , src="{% static "img/draft.png" %}"];
i = 0;
{% for post in posts %}
function myFunction({{ post.model_number }}) {
i++;
document.getElementById("{{ post.model_number }}").src = img_array[i];
if (i == img_array.length - 1) {
i = -1;
}
}
{% endfor %}
</script>
<table class="table table-bordered">
<tr>
<td>產品編號</td><td>產品名稱</td><td>建立日期</td><td>產品圖片</td><td>最後修改日期</td><td>產品說明</td><td>建立者</td><td>修改者</td><td></td>
</tr>
{% for post in posts %}
<tr>
<td> {{ post.model_number }} </td>
<td>{{ post.name }}</td>
<td>{{ post.created }} </td>
<td> <img src="{% if post.image %} {{ post.image.url }}{% else %}{% static "img/no_image.png" %}{% endif %}" height="100" width="100"> </td>
<td>{{ post.modified }} </td>
<td>{{ post.feature }}</td>
<td>{{ post.user }}</td>
<td>{{ post.useredit }}</td>
{% for cp in post.cataloggroup.all %}
<td> {{cp }}</td>
{% endfor %}
<td><a href="{% url 'edit_thing' slug=post.slug %}"><input class="button0 button2" type="button" id="button" value="編輯" style="color:#fffff0" onclick="setColor(event, 'button', '#101010')"; data-count="1" /><a></td>
<td><button onclick="myFunction({{ post.model_number }})"><img id="{{ post.model_number }}" src="{% static "img/published.png" %}" width="50" height="30"></button></td>
</tr>
{% endfor %}
</table>
{% include "pagination.html" with page=posts %}
{% endblock content %}
and it will render the web
however, when I click the top button ,it will change the bottom button,I have to let every button clicked can change image
above image I clicked top button but changed the bottom button's image "published ---> draft"
I have to let every button clicked can changed it's own image,how can I do it?
It is due to the fact you have your myFunction() definintion inside a <script> tag that is entered into the HTML every iteration of your for loop in django.
This means the function is constantly being redefined and therefore myfunction functionality will always be that of the last iteration.
If you define myFunction() outside of your django loop and pass in the model number to the function (as below) then it should work as expected`
function myFunction(model_number) {
i++;
document.getElementById(model_number).src = img_array[i];
if (i == img_array.length - 1) {
i = -1;
}
}
ok,I solve the problem
index.html
<script>
...........
var img_array = [ src="{% static "img/published.png" %}" , src="{% static "img/draft.png" %}"];
i = 0;
{% for post in posts %}
function a{{ post.model_number }}() {
i++;
document.getElementById("{{ post.model_number }}").src = img_array[i];
if (i == img_array.length - 1) {
i = -1;
}
}
{% endfor %}
</script>
<table class="table table-bordered">
<tr>
{% for post in posts %}
<tr>
.........
<td><button onclick="a{{ post.model_number }}()"><img id="{{ post.model_number }}" src="{% static "img/published.png" %}" width="50" height="30"></button></td>
</tr>
{% endfor %}
</table>

Flask: don't redirect to a url()

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.

Categories