How to update prefix id of a formset - javascript

I'm new at python, but no at programing. I'm using Django to create a webapp and in some part, I have to create products. A product has a name, brand and type, but this product can have many "inventories". For example: The product Iphone has a brand Apple, type Phone and since I have 3 in my stock, there is another table that saves 3 individual codes for them (and the price and state as well).
For this solution I have used formsets, and it was working fine without the JS. What I mean is that the process is ok, but when I want to dinamically add a new row to add a new Inventory, is added visually but then when I tried to save it, it just saves the last row. I have looked into the debug console of chrome and it seems that the problem is that the name and ID, of the new row, remains the same as the original one (the prefix I believe), but I don't know how to change it. Here is my code: Note in this image that the prefix of the second row is 0
views.py:
class ProductoInline(InlineFormSetFactory):
model= Inventario
fields = ['codigo', 'Producto', 'Precio', 'Estado']
factory_kwargs = {'extra':1 , 'max_num': 10,
'can_order': False, 'can_delete': False}
class InventarioCreateView(LoginRequiredMixin, CreateWithInlinesView):
model = Producto
inlines = [ProductoInline]
fields = '__all__'
factory_kwargs = {'extra': 3, 'max_num': 10,'can_order': False,
'can_delete': False}
template_name = 'ventas/inventario_form.html'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
inventario_form.html:
{%extends "ventas/base.html" %}
{% load staticfiles %}
{% load crispy_forms_tags %}
{%block content %}
<section class="card">
<header class="card-header">Crea/actualiza producto</header>
<div class="card-body">
<form method="POST">
{% csrf_token %}
{{ form|crispy }}
{% for formset in inlines %}
<table class="table table-bordered table-responsive-md table-striped text-center">
<tr>
<th class="text-center">Código</th>
<th class="text-center">Precio</th>
<th class="text-center">Estado</th>
<th class="text-center">Eliminar</th>
</tr>
{{formset.management_form}}
<tbody>
{% for form2 in formset %}
<tr id="{{ form2.prefix }}-row" class="dynamic-form">
<td>{{ form2.codigo }}</td>
<td>{{ form2.Precio }}</td>
<td>{{ form2.Estado }}</td>
<td><a id="remove-{{ form2.prefix }}-row" href="javascript:void(0)" class="delete-row">Eliminar</a></td>
</tr>
{% endfor %}
<tr>
<td colspan="4">Añadir producto</td>
</tr>
</tbody>
</table>
{% endfor %}
<button type="submit">Crear producto</button>
</form>
</div>
{%endblock content %}
formset.js:
function updateElementIndex(el, prefix, ndx) {
var id_regex = new RegExp('(' + prefix + '-\\d+)');
var replacement = prefix + '-' + ndx;
if ($(el).attr("for")) $(el).attr("for",
$(el).attr("for").replace(id_regex, replacement));
if (el.id) el.id = el.id.replace(id_regex, replacement);
if (el.name) el.name = el.name.replace(id_regex, replacement);
}
function addForm(btn, prefix) {
var formCount = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());
var row = $('.dynamic-form:first').clone(true).get(0);
$(row).removeAttr('id').insertAfter($('.dynamic-form:last')).children('.hidden').removeClass('hidden');
$(row).children().not(':last').children().each(function() {
updateElementIndex(this, prefix, formCount);
$(this).val('');
});
$(row).find('.delete-row').click(function() {
deleteForm(this, prefix);
});
$('#id_' + prefix + '-TOTAL_FORMS').val(formCount + 1);
return false;
}
function deleteForm(btn, prefix) {
$(btn).parents('.dynamic-form').remove();
var forms = $('.dynamic-form');
$('#id_' + prefix + '-TOTAL_FORMS').val(forms.length);
for (var i=0, formCount=forms.length; i<formCount; i++) {
$(forms.get(i)).children().not(':last').children().each(function() {
updateElementIndex(this, prefix, i);
});
}
return false;
}

Related

How do I pass django variables to javascript in a for statement?

How do I pass django variables to javascript in a for statement I want to pass c.tv.tv_id in a for statement in javascript. I want to pass it to javascript in each for statement, but I don't know how to do it.
class Comment_List_TV(ListView):
template_name = 'account/user_comment_list_tv.html'
def get_queryset(self):
Comment_list_query = Comment_tv.objects.none()
if self.request.user.is_authenticated:
Comment_list_query = Comment_tv.objects.filter(user=self.request.user)
return Comment_list_query
{% extends 'base.html' %}
{%load static%}
<link rel="stylesheet" href="{% static 'css\common_movie_tv.css' %}">
{% block content %}
<table>
<tr>
<tr>
<th>name</th>
<th>created_at</th>
<th>comment</th>
<th>evaluation</th>
</tr>
{% for c in object_list %}
<tr>
<th id = "trendings"></th>
<td>{{ c.user.nickname }}</td>
<td>{{c.tv.tv_id}}</td>
<td>{{ c.created_at }} </td>
<td>{{ c.comment }}</td>
<td><h2 class = "rate" style="--rating:{{c.stars}}">{{c.stars}}</h2></td>
</tr>
{% endfor %}
</table>
<script>
const tv_id = {{c.tv.tv_id}}
fetch(`https://api.themoviedb.org/3/tv/${tv_id}?api_key=${TMDB_API_KEY}&language=en-US`, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
)
.then(res => res.json())
.then(data => {
var mainDiv = document.createElement("div");
mainDiv.setAttribute("class", "card");
mainDiv.setAttribute("style", "width: 18rem;");
var img = document.createElement("img");
img.setAttribute("src", "https://image.tmdb.org/t/p/w200" + data.poster_path);
img.setAttribute("class", "card-img-top");
img.setAttribute("alt", "...");
var body = document.createElement("div");
body.setAttribute("class", "card-body");
var title = document.createElement("h5");
title.setAttribute("class", "card-title");
if (data.name) {
title.innerHTML = data.name;
} else {
title.innerHTML = data.title;
}
//var text = document.createElement("p");
//text.setAttribute("class", "card-text");
//text.innerHTML = data.results[i].overview;
var link = document.createElement("a");
link.setAttribute("href", "/" + "tv" + "/" + data.id + "/");
link.setAttribute("class", "btn btn-primary");
link.innerHTML = "View Details";
body.appendChild(title);
//body.appendChild(text);
body.appendChild(link);
mainDiv.appendChild(img);
mainDiv.appendChild(body);
document.getElementById("trendings").appendChild(mainDiv);
})
</script>
{% endblock %}
How do I pass django variables to javascript in a for statement I want to pass c.tv.tv_id in a for statement in javascript. I want to pass it to javascript in each for statement, but I don't know how to do it.
You need put your query result in a dict and send it as a Json response.
Pre-Django 1.7 you'd return it like this:
return HttpResponse(json.dumps(response_data), content_type="application/json")
For Django 1.7+, use JsonResponse as shown in this SO answer like so :
from django.http import JsonResponse
return JsonResponse({'foo':'bar'})

Load html table with Flask, jinja2, python and js dynamically

I have create a test website using Flask that displays data from a postgres database, ordered by category, product, month and date. All the table rows below each category are hidden. Clicking on a row will expand/collapse 1 level of rows beneath it. So, clicking on a category will show all the products. Clicking on a product will show all the months with data.
Currently, as you can see in the code, all the records are retrieved when the page is loaded.
The test website is a simplification of a website that displays thousands of of records. I would like to use the website for a different db with millions of records.
Unfortunately, the page would take far to long to load and expand/collapse. Instead of collapsing/expanding the rows when clicked, I would like to query the db using Flask each time a category, product or month is expanded and add 1 level of rows to the table with the data returned by a query.
Can this be done with Flask, jinja2 and js?
html template with jinja2 code
<!DOCTYPE html>
<html>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type=text/javascript src="{{url_for('static', filename='js/test.js') }}"></script>
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='styles/test.css') }}">
<title>Test Table</title>
<body>
<table class = "products">
<thead>
<tr>
<th>Product</th>
<th>Month</th>
<th>Date</th>
</tr>
</thead>
<tbody>
{% for category in types %}
<tr class="category" data-level="0" vis_id = "{{ category.category}}">
<td colspan = 3 class="nosides">
<label>{{ category.category}}</label>
</td
</tr>
{% for product in category.products %}
<tr class="parent" data-level="1" vis_id = "{{ product.product }}" par_vis_id = "{{ category.category}}">
<td colspan = 3 class="nosides">
<label>{{ product.product }}</label>
</td>
</tr>
{% for month in product.months %}
<tr class="parent" data-level="2" vis_id = "{{ product.product }}_{{ month.month }}" par_vis_id = "{{ product.product }}">
<td colspan=1 class="noright"></td>
<td colspan=2 class="nosides">
<label>{{ month.month }}</label>
</td>
</tr>
{% for date in month.date %}
<tr class="child" data-level="3" vis_id = "{{ date.date }}" par_vis_id = "{{ product.product }}_{{ month.month }}">
<td colspan = 2></td>
<td>{{ date.date }}</td>
</tr>
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
</tbody>
</table>
</body>
</html>
Python Flask code
import sys, os
from flask import Flask, render_template
import pandas as pd
import pandas.io.sql as psql
#app.route('/test_table')
def test_table():
con = set_con('db')
sys_list = []
#select all the records
df = pd.read_sql("select category, product, TO_CHAR(date, 'Mon YYYY') as month, TO_CHAR(date, 'YYYY-MM-DD HH24:MI:SS') as date \
from db order by category, product, date desc", con)
query = "Select distinct product from db order by product"
#get a list of categories, products and months, then loop over each.
df_products = pd.read_sql(query, con)
query = "Select distinct category from products"
df_sys = pd.read_sql(query,con)
for category in df_sys['category'].values:
products_list = []
df_sys_tmp = df[df['category']==category]
if df_sys_tmp.empty:
continue
for product in df_products['product'].values:
df_product_tmp = df_sys_tmp[df_sys_tmp['product']==product]
if df_product_tmp.empty:
continue
months = df_product_tmp['month'].unique()
tmp_list_months = []
for month in months:
#loop over all the records for this particular category, product, and month.
df_recs = df_product_tmp[df_product_tmp['month']==month]
if df_recs.empty:
continue
tmp_list_recs = []
for i, row in df_recs.iterrows():
tmp_list_recs.append({'date':row['date']})
tmp_list_months.append({'month':month, 'date':tmp_list_recs})
products_list.append({'product':product, 'months':tmp_list_months})
sys_list.append({'category':category, 'products':products_list})
return render_template(test.html',types=sys_list)
if __name__ == '__main__':
main()
js
function toggleIf(element, condition) {
if (condition ===true || condition === 'true') { element.show(); }
else { element.hide(); }
};
$(document).ready(function() {
function getChildren($row) {
var children = [], level = $row.attr('data-level');
while($row.next().attr('data-level') > level) {
children.push($row.next());
$row = $row.next();
};
return children.sort((a, b) => parseFloat(a.attr('data-level')) - parseFloat(b.attr('data-level')));;
};
$('.parent').on('click', function() {
var children = getChildren($(this));
var datalevel = $(this).attr('data-level')
var vis_id = $(this).attr('vis_id')
$.each(children, function() {
par_vis_id = $(this).attr('par_vis_id')
if (datalevel==1){
if ($(this).attr('data-level')==2){
$(this).toggle();}
else{
if ($(this).is(":visible")){
$(this).toggle();}}}
else{$(this).toggle();}
sessvisible = $(this).is(":visible")
if (sessvisible) {
sessionStorage.setItem(par_vis_id, 'true')
}
else{
try {
sessionStorage.removeItem(par_vis_id);}
catch { };};});
});
$('.product').on('click', function() {
var children = getChildren($(this));
var vis_id = $(this).attr('vis_id')
$.each(children, function() {
item_visible = $(this).is(":visible")
if ($(this).attr('data-level')==1){
$(this).toggle();
product_visible = $(this).is(":visible")
if (!product_visible) {
try {
sessionStorage.removeItem($(this).attr('vis_id'));}
catch { };}} else{
if (item_visible){
$(this).toggle();}
try {
sessionStorage.removeItem($(this).attr('vis_id'));}
catch { };
}})
if (product_visible) {
sessionStorage.setItem(vis_id, 'true')
}
else{
try {
sessionStorage.removeItem(vis_id);}
catch { };};
});
$('.parent').on('custom', function() {
var children = getChildren($(this));
var datalevel = $(this).attr('data-level')
var vis_id = $(this).attr('vis_id')
$.each(children, function() {
if (datalevel==1){
if ($(this).attr('data-level')==2){
$(this).toggle();}
else{
if ($(this).is(":visible")){
$(this).toggle();}}}
else{$(this).toggle();}
});
});
$('.product').on('custom', function() {
var children = getChildren($(this));
var vis_id = $(this).attr('vis_id')
$.each(children, function() {
item_visible = $(this).is(":visible")
if ($(this).attr('data-level')==1){
$(this).toggle();}
else{
if (item_visible){
$(this).toggle();}}
});
});
$('.product').each(function(i, tr) {
var isvisible = $(this).is(":visible")
var children = getChildren($(this));
var vis_id = $(this).attr('vis_id')
if (sessionStorage.getItem(vis_id) !== null){
$(this).trigger('custom');
}
$.each(children, function() {
var datalevel = $(this).attr('data-level')
if (datalevel !== '3'){
var vis_id = $(this).attr('vis_id')
if (sessionStorage.getItem(vis_id) !== null){
$(this).trigger('custom');};};
});
});
});
I was able to do this using XMLHTTPRequests with Flask and JS.

How to apply highlight function on each table row?

I developed a web project where I can upload two files and django server execute a function to return the lines that don't match with other in a pandas dataframe which is renderized in a html template. Is returned a two columns table, file1 and file2. I would like to have another column with the text and text difference 'highlighted'. I have no knowledge about javascript programing, but I don't know how to apply the function on each table row.
My views.py
def index(request):
form = UploadFileForm(request.POST, request.FILES)
if request.method == 'POST':
if form.is_valid():
check = CheckFiles(form.cleaned_data["arquivo1"], form.cleaned_data["arquivo2"]).verifica_tamanho()
if type(check) == str:
return HttpResponse('The file are the same!')
#return HttpResponse(check.to_html())
return render(request, 'index2.html', {'check': check})
else:
print('Invalid Form!')
return render(request, 'index.html', {'form': form})
and in my index2.html:
<table class="table" border="1px">
<thead class="thead-dark">
<tr>
<th scope="col">Original File</th>
<th scope="col">Secondary File</th>
</tr>
</thead>
<tr>
{% for _, record in check.iterrows %}
<tr>
{% for value in record %}
<td>{{ value }}</td>
{% endfor %}
</tr>
{% endfor %}
</tr>
</table>
and the highlight javascript function is:
highlight($("#new"), $("#old"));
function highlight(newElem, oldElem){
var oldText = oldElem.text(),
text = '';
newElem.text().split('').forEach(function(val, i){
if (val != oldText.charAt(i))
text += "<span class='highlight'>"+val+"</span>";
else
text += val;
});
newElem.html(text);
}
.highlight {background-color: #B4D5FF}
So, I need some help to understand how to apply this javascript function on each table row.
Thank you, have a nice day.
I can see you also use jquery so:
$(.table tr).each(function(index){
$(this).addClass("highlight");
});
that way you can apply highlight to every row.
It seems like your current highlight function "highlights" chars that have changed in the string but I have no idea what it has to do with rows... What is $("#new") ... what is $("#old")...
**UPDATE **
$("table tr").each(function(index){
a = $(this).find("td");
first_td = a.eq(0);
second_td = a.eq(1);
highlight(second_td, first_td);
});

Django template onclick inline html javascript alternative

I read that using onclick is deprecated and bad practice.
I have this do_something function that will proccess the data inside of onclick. How I can convert this to javascript:
{% for user in all_user %}
<tr>
<td>{{ user.code }}</td>
<td>
<button type="button" onclick="do_something('{{ user.name }}', '{{ user.age }}', 1, '{{ user.desc }}');">small</h6></button>
</td>
</tr>
{% endfor %}
Set a class selector on the buttons so you can apply the event listener to it via Javascript.
Your function requires parameters that are unique per user, so you'll need to set those parameters as data attributes on each button.
{% for user in all_user %}
<tr>
<td>
<button class="user-action-btn" type="button"
data-name="{{ user.name }}"
data-age="{{ user.age }}"
data-desc="{{ user.desc }}">small</button>
</td>
</tr>
{% endfor %}
In Javascript, get the elements by class name and set the event handler for each.
Here's an example of the code in Javascript
(function() {
var classname = document.getElementsByClassName("user-action-btn");
var myFunction = function(e) {
e.preventDefault();
var name = this.getAttribute("data-name");
var age = this.getAttribute("data-age");
var desc = this.getAttribute("data-desc");
do_something(name, age, 1, desc)
};
for (var i = 0; i < classname.length; i++) {
classname[i].addEventListener('click', myFunction, false);
}
})();
And here's the JQuery equivalent
$( document ).ready(function() {
$('.user-action-btn').on('click', function(e){
e.preventDefault();
var name = $(this).data('name');
var age = $(this).data('age');
var desc = $(this).data('desc');
do_something(name, age, 1, desc);
});

jQuery each function: not properly getting data as expected

For some reason, I cannot obtain the text/html from the 2nd TD as specified below
The template tags are Django.
{% block content %}
{% if host.host_scripts %}
<table class="table">
<button class="btn btn-info add" id="{{forloop.counter}}">Add to session</button>
{% for script in host.host_scripts %}
<tr>
<td id="script-name-{{forloop.counter}}">{{script.scriptName}}</td>
<td id="script-output-{{forloop.counter}}">{{script.scriptOutput|linebreaks}}</td>
</tr>
{% endfor %}
</table>
{% endif %}
<script>
$(".add").click(function(e) {
var id = $(this).attr('id');
scriptList = []
e.preventDefault();
$("#myTable-"+id+" tr ").each(function() {
var name = $(this).find('#script-name-'+id).html();
var output = $(this).find('#script-output-'+id).html();
var myList = [name, output];
console.log('Name -> ' + name);
console.log('Output -> ' + output);
scriptList.push(myList);
});
});
</script>
{% endblock %}
I can get the name variable just fine. For some reason I cannot get the scriptOutput variable.
I assumed this was due to |linebreaks but removing it has no influence on the behavior.
Desired behavior:
Per tr in the table, get the contents of the td's and add to the list.
Current behavior:
It only get's the name variable, skips the scriptOutput variable for some reason.
I managed to get 'some' output, now I get the same value returned for output for every scriptName. I'm baffled as I can't see what's wrong with the ID's.
{% block content %}
{% if host.host_scripts %}
<table class="table myTable">
<button class="btn btn-info add" id="{{forloop.counter}}">Add to session</button>
{% for script in host.host_scripts %}
<tr>
<td id="script-name-{{forloop.counter}}">{{script.scriptName}}</td>
<td id="script-output-{{forloop.counter}}">{{script.scriptOutput|linebreaks}}</td>
</tr>
{% endfor %}
</table>
{% endif %}
<script>
var scriptList = []
$("#myTable-"+id+" tr").each(function() {
var name = $(this).find('td:first').html();
var output = $('#script_output-'+id).html();
var script = [name, output];
scriptList.push(script);
console.log('Text: ' + output);
});
console.log(scriptList);
</script>
I am aware I should probably expand my ID to something of sorts:
<td id="script-output-{{forloop.parentloop.counter}}-{{forloop.counter}}">{{script.scriptOutput|linebreaks}}</td>
Yet I can't seem to figure it out, what am I doing wrong??
Try this instead:
<script>
$(".add").click(function(e) {
var id = $(this).attr('id');
scriptList = []
e.preventDefault();
$("#myTable-"+id+" tr ").each(function(index, el) {
var name = $(el).find('#script-name-'+id).html();
var output = $(el).find('#script-output-'+id).html();
var myList = [name, output];
console.log('Name -> ' + name);
console.log('Output -> ' + output);
scriptList.push(myList);
});
});
</script>

Categories