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.
Related
In my template i'm using a formset to add a "Raum"-form dynamic.
In each row of my table, there is a button "delete row", so that i can delete the row again.
the java script code is as far as i can say is working quite well...
Adding and saving the forms works fine.
Also deleting with the java script code works fine.
Even when i delete the last row of the table, saving the forms works fine.
But when f.e. i add 3 rows (rooms), row1-row2 and row3, and i delete row 1 or row 2, i can not save my forms, because i think in view.py the is_valid method = false...
maybe i have to update some kind of index of my forms??? and how ??
any help is really apprecheated...
html template for adding rooms with the table and forms:
<table id="tableHeadToModify" class="table table-bordered">
<thead class="hg-rot schrift-weiss">
<tr>
<th colspan="2">Räume</th>
</tr>
{% for raum in raum_formset %}
{% if forloop.first %}
<tr>
<th>{{ raum.name.label_tag }}</th>
<th>{{ raum.name.label_tag }}</th>
</tr>
{% endif %}
{% endfor %}
</thead>
<tbody id="tableToModify">
{{raum_formset.management_form}}
{% for raum in raum_formset %}
<tr id="rowToClone">
<td> {{ raum.name }} </td>
<td><button type="button" value="Delete" onclick="deleteRow(this)"></td>
</tr>
{% endfor %}
</tbody>
</table>
<button id="add-form" type="button" class="btn btn-outline-danger btn-md">add room</button>
<script>
let container = document.querySelector("#form-container")
let addButton = document.querySelector("#add-form")
let totalForms = document.querySelector("#id_form-TOTAL_FORMS")
let rowCount = document.getElementById('tableToModify').rows.length-1;
addButton.addEventListener('click', addForm)
function addForm(e){
e.preventDefault()
let formRegex = RegExp(`form-(\\d){1}-`,'g')
rowCount = document.getElementById('tableToModify').rows.length-1;
var row = document.getElementById("rowToClone"); // find row to copy
var table = document.getElementById("tableToModify"); // find table to append to
var clone = row.cloneNode(true); // copy children too
table.appendChild(clone); // add new row to end of table
rowCount++
clone.innerHTML = clone.innerHTML.replace(formRegex, `form-${rowCount}-`)
totalForms.setAttribute('value', `${rowCount+1}`)
}
function deleteRow(r) {
var i = r.parentNode.parentNode.rowIndex;
var total = parseInt($('#id_form-TOTAL_FORMS').val());
if (total > 1) {
var i = r.parentNode.parentNode.rowIndex;
document.getElementById("tableHeadToModify").deleteRow(i);
totalForms.setAttribute('value', `${rowCount}`)
}
}
}
views.py
formset_raum = RaumFormSet(data=self.request.POST)
#wenn die Eingabe der Formen passt
if kunde_form.is_valid() and angebot_form.is_valid() \
and objekt_form.is_valid() \
and formset_raum.is_valid():<--- this isn't valid
I'm trying to get the child nodes from a table, multiply that and show that on a input field. I have tried using textContext but it only returns first value not the later ones.
My Javascript code is:
function add() {
console.log("this is working")
var x = parseInt(document.getElementById("id_quantity").value);
console.log(x, "value of x");
var y = document.getElementById("quantity").textContent;
document.getElementById("id_quantity_p1").value = x * y;
This is my HTML markup:
<div class="col-md-6 col-sm-8 col-12">
<form method="post" id="MaterialRequestForm" data-kit-url="{% url 'employee:ajax_load_kit' %}" onkeyup="add()"
data-product-url="{% url 'employee:ajax_load_product' %}"
novalidate>
{% csrf_token %}
{{ form|crispy }}
<button type="submit">Save</button>
<div id="products-table" class="col-md-12 col-sm-8 col-12 product-table-ajax">
</div>
</form>
</div>
And This is my table's HTML code:
{% for product in products %}
<tr>
<td>{{ product.id }}</td>
<td>{{ product.product_name }}</td>
{% for i in quantities %}
{% if forloop.counter == forloop.parentloop.counter %}
<td id="quantity">{{ i }}</td>
{% endif %}
{% endfor %}
{% endfor %}
In this <td id-"quantity" returns multiple values and I want first two of it.
They are in django HTML template
In this I want to enter quantity and I want that it should get multiplied by content Std Qty in column and get filled in "quantity p1", "quantity p2", "quantity p3". eg. quantitystdQty1=quantityP1, quantitystdQty[2]=quantityP2, quantity*stdQty[3]=quantityP3 etc. For that I need specific elements in my <td>. Please help!
I did that with a different way:
I attached different ID's with the form and then instead of fetching child of I assigned different ID's to all the and then used getElementByID:
{% for product in products %}
<tr>
<td>{{ product.id }}</td>
<td>{{ product.product_name }}</td>
{% for i in quantities %}
{% if forloop.counter == forloop.parentloop.counter %}
<td id="q{{ forloop.counter }}">{{ i }}</td>
{% endif %}
{% endfor %}
{% endfor %}
</tr>
And then changed my js accordingly:
function add() {
console.log("this is working")
var x = parseInt(document.getElementById("id_quantity").value);
console.log(x, "value of x");
var y =document.getElementById("q1").textContent;
var y1 =document.getElementById("q2").textContent;
var y2 =document.getElementById("q3").textContent;
var y3 =document.getElementById("q4").textContent;
var y4 =document.getElementById("q5").textContent;
document.getElementById("id_quantity_p1").value = x * y;
document.getElementById("id_quantity_p2").value = x * y1;
document.getElementById("id_quantity_p3").value = x * y2;
document.getElementById("id_quantity_p4").value = x * y3;
document.getElementById("id_quantity_p5").value = x * y4;
It will obviously only give you back the first one due to the fact that you are using a body.getElementById function, which only returns the very first element with the said id you look for.
For getting all the values you need, I suggest you could instead remake "id_quantity" into a class instead.
With that being done, use body.getElementsByClassName instead, write a loop that stores the data from every node of "id_quantity" and loop it back into the fields where you multiply them.
Here's a very quick example of what I mean
HTML:
<div id="container">
<div id="color-div">
<table>
<thead>
<th>1</th>
<th>2</th>
</thead>
<tbody>
<tr>
<td class="input">5</td>
<td class="output"></td>
</tr>
<tr>
<td class="input">7</td>
<td class="output"></td>
</tr>
<tr>
<td class="input">10</td>
<td class="output"></td>
</tr>
</tbody>
</table>
</div>
</div>
Javasript:
let arr = [];
const $input = document.getElementsByClassName("input");
const $output = document.getElementsByClassName("output");
for (let i = 0; i < $input.length; i += 1) {
arr.push($input[i].innerHTML);
}
for (let i = 0; i < $output.length; i += 1) {
$output[i].innerHTML = arr[i] * 5;
}
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>
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 have a fruits form that has one FieldList object for the bananas:
bananas = FieldList(FormField(BananaForm))
In the frontend, initially, I add one of those fields to the FieldList
form.append_entry()
Now with Javascript I managed to create functions, that can dynamically add (plus button) or remove (minus button) the number of BananaForm fields that can be filled with information.
FielstList automatically creates ids for all of its fields. So to do dynamical adding with js, I duplicate the HTML code and set the field id += 1, like:
first field:
<tr>
<td><input id="bananas-0-originCountry" type="text" /></td>
</tr>
duplicated field with += 1:
<tr>
<td><input id="bananas-1-originCountry" type="text" /></td>
</tr>
When I name them accordingly like this and submitting the form, WTForms will automatically recognize the added fields in the backend (works fine).
So far so good, but here is my problem:
For a form to be valid, I have to add CSRF-fields to every WTForm. In the Jinja template I do this with:
{{ form.hidden_tag() }}
However, when I just copy the HTML with my js function, I'm missing the CSRF-fields (because until submitted, the backend form object doesn't know about the added FormFields). So how can I generate these CSRF-fields dynamically? (An Ajax Request? If yes, how?)
This should be a standard use case with forms and flask. I hope my description was understandable, if not please let me know. Any help appreciated!
UPDATE: Here is my code
JS-functions
function addBanana(){
// clone and insert banana node
var node = document.getElementById("fruitTable");
var trs = node.getElementsByTagName("tr");
var tr = trs[trs.length-2];
var tr2 = tr.cloneNode(true);
tr.parentNode.insertBefore(tr2, tr);
// in order to increment label and input field ids
function plusone(str){
return str.replace(
new RegExp("-(\\d+)-", "gi"),
function($0, $1){
var i = parseInt($1) + 1;
return "-" + i + "-";
}
);
}
// change inputs
var inputs = tr.getElementsByTagName("input");
for (var i = 0; i < inputs.length; i++){
inputs[i].setAttribute("id", plusone(inputs[i].getAttribute("id")));
}
var minusbutton =
['<td>',
'<button class="btn" type="button" onClick="removeBanana()"><i class="icon-black icon-minus"></i></button>',
'</td>'
].join('\n');
// only append at the first add
// second add automatically copies minus button
if (trs.length < 6){
tr.innerHTML += minusbutton
}
}
function removeBanana(){
var node = document.getElementById("fruitTable");
var trs = node.getElementsByTagName("tr");
var tr = trs[trs.length-2];
var trParent = tr.parentNode;
trParent.removeChild(tr);
}
Jinja Template:
<form method="POST" action="newsubmit">
{{ form.hidden_tag() }}
<table id="fruitTable" class="table">
{{ render_field(form.description) }}
<tr><td><h3>Bananas</h3></td></tr>
{% set counter = 0 %}
{% for banana in form.bananas %}
<tr>
{{ banana.hidden_tag() }}
{% set counter = counter + 1%}
{% for field in banana if field.widget.input_type != 'hidden' %}
{{ render_field_oneline(field) }}
{% endfor %}
{% if counter > 1 %}
<td>
<button class="btn" type="button" onClick="removeBanana()"><i class="icon-black icon-minus"></i></button>
</td>
{% endif %}
</tr>
{% endfor %}
<tr><td></td><td><button class="btn" type="button" onClick="addBanana()"><i class="icon-black icon-plus"></i></button></td></tr>
</table>
<input class="btn btn-primary" style="margin-left:300px;"type="submit" value="Submit" />
</form>
Jinja Template Macros:
{% macro render_field_oneline(field) %}
<td>{{ field.label }}</td>
<td>{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</td>
{% endmacro %}
{% macro render_field(field) %}
<tr>
{{ render_field_oneline(field) }}
</tr>
{% endmacro %}
I discovered how it works:
The CSRF-Tag can simply be copied. The id must be changed and incremented accordingly, but the hash may stay the same.
I didn't think it was possible to have many Fields with the same CSRF-Tag hash, but it actually does!