Live Form Field Calculations in Django Templates - javascript

I am creating a stock market site and would like to show the total purchase value live as the user is typing rather than as an error during form clean. The calculation involves multiplying two fields. The first is a quantity field which is just a number the user inputs. The second is the stock which is a drop down for a foreign key. The user selects a stock and an attribute of the stock is the price. I would like to multiply these fields and display the result each time a user alters a field. I would also like to do this with a model form if possible.
I have tried using {{ form.field.value }} within the template to get one the fields but I cannot get it to update for a change. For the stock field, I think my best bet is to create a matching array in javascript and once I can get a live updating form value match it to the stock price from the array. Another possibility may be using getElementById with the field id but I have been unable to get that to work so far as well.
Here is an example not made by me that is close to what I want to do Real Time Javascript Calculation Based On Form Inputs
Any help is much appreciated!
Models.py
class Stock(models.Model):
name = models.CharField(max_length=30, primary_key=True, blank=True)
#property
def price():
do some calculations
class Transaction(models.Model):
transaction_types = (
('buy', 'Buy'),
('sell', 'Sell'),
)
user = models.ForeignKey(User, on_delete=models.CASCADE)
stock = models.ForeignKey(Stock, on_delete=models.CASCADE)
transaction_type = models.CharField(max_length=4, choices=transaction_types)
quantity = models.BigIntegerField()
purchase_price = models.DecimalField(decimal_places=2, max_digits=30)
time = models.DateTimeField()
Forms.py
class TransactionForm(forms.ModelForm):
class Meta:
model = Transaction
fields = ['transaction_type', 'stock', 'quantity']
widgets = {
'quantity': forms.NumberInput(attrs={'id': 'quant', 'name': 'quant'})
}
def clean(self):
super(TransactionForm, self).clean()
quantity = self.cleaned_data.get('quantity')
stock= self.cleaned_data.get('stock')
transaction_type = self.cleaned_data.get('transaction_type')
purchase_price = stock.price
if quantity < 1:
self._errors['quantity'] = self.error_class(['You must purchase at least one share.'])
elif quantity * purchase_price > self.user.profile.buying_power and transaction_type == 'buy':
self._errors['quantity'] = self.error_class([
f'Your purchase value (${round(quantity * purchase_price, 2)}) must be less than or equal to your buying power '
f'(${round(self.user.profile.buying_power, 2)}). You can afford a maximum of '
f'{floor(self.user.profile.buying_power / purchase_price)} share(s).'])
Trade.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div>
<form method="POST" id="trade_form">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Trade</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-success" type="submit">Submit</button>
</div>
</form>
<p>Quantity: <span id="quantity"></span></p>
<script>
$('input').keyup(function () { // run anytime the value changes
let quant = Number($('#quant').val());
$('#quantity').html(quant);
});
</script>
</div>
{% endblock %}
Failed attempt
<script>
var stock_prices = new Array();
{% for stock in stocks %}
stock_prices ["{{ stock .name }}"] = {{ stock .price }};
{% endfor %}
function getStockPrice() {
var price = 0;
var selectedStock = document.getElementById("id_stock");
for (var i = 0; i < selectedStock.length; i++) {
if (selectedStock[i].checked) {
price = stock_prices [selectedStock[i].value];
break;
}
}
return price;
}
function getQuantity() {
var quantity = document.getElementById("quant");
var howmany = 0;
howmany = parseInt(quantity.value);
return howmany;
}
function getTotal() {
var total = getStockPrice() * getQuantity();
document.getElementById('totalPrice').innerHTML =
"Total Price: $" + total;
}
</script>

I figured out how to do this. Basically, I made an api using Django Rest Framework. From there, I used Vue to make my form a v-model. I used watch to call the api every time the v-model changed.

Related

Django smart selects doesn't work with JS cloning

I'm trying to create a page with ability to add any amount of form-copy.
I use django-smart-selects to make my form's field chained. It works fine if I have only 1 form on page.
Then I'm using javascript to make a function to clone form instance by pressing button and addind new form's ids to these clones.
The problem is when I press "+" button I get new cloned form on page, but chained-selection doesn't work anymore(only first one still works), and it seems that this clone copying all my choices from the firs form and remembering this state.
I see in terminal this response every time I choose any selection in chained fields:
[31/Oct/2022 16:42:27] "GET /chaining/filter/cash_table/LevelOne/flowtype/cash_table/CashFlowPattern/level_one/1/ HTTP/1.1" 200 115
[31/Oct/2022 16:42:29] "GET /chaining/filter/cash_table/LevelOne/flowtype/cash_table/CashFlowPattern/level_one/2/ HTTP/1.1" 200 105
But in cloned forms it doesn't happen.
My Formset is:
forms.py
from django import forms
from .models import CashFlowPattern
from django.forms import modelformset_factory
class CashFlowForm(forms.ModelForm):
class Meta:
model = CashFlowPattern
fields = '__all__'
CashFowFormSet = modelformset_factory(
CashFlowPattern,
fields=(
'flow_type',
'level_one',
'level_two',
'eom',
'amount',
'comment'
),
extra=1
)
views.py
class FormAddView(TemplateView):
template_name = 'cash_table/index.html'
def get(self, *args, **kwargs):
formset = CashFowFormSet(queryset=CashFlowPattern.objects.none())
return self.render_to_response({'formset': formset})
def post(self, *args, **kwargs):
end_of_month = (datetime.datetime.now() + relativedelta(day=31)).strftime('%Y-%m-%d')
formset = CashFowFormSet(data=self.request.POST)
if formset.is_valid():
forms = formset.save(commit=False)
for form in forms:
form.eom = end_of_month
form.user = self.request.user
form.save()
# return redirect(reverse_lazy("bird_list"))
return self.render_to_response({'formset': formset})
template:
<form id="form-container" method="POST">
{% csrf_token %}
{{ formset.management_form }}
<div class="empty-form">
{% for form in formset %}
{{ form.media.js }}
<p>{{ form }}</p>
{% endfor %}
</div>
<button id="add-form" type="button">+</button>
<button type="submit" class="btn btn-primary">Отправить</button>
</form>
<script>
let emptyForm = document.querySelectorAll(".empty-form")
let container = document.querySelector("#form-container")
let addButton = document.querySelector("#add-form")
let totalForms = document.querySelector("#id_form-TOTAL_FORMS")
let formNum = emptyForm.length-1
addButton.addEventListener('click', addForm)
function addForm(e) {
e.preventDefault()
let newForm = emptyForm[0].cloneNode(true) //Clone the form
let formRegex = RegExp(`form-(\\d){1}-`,'g') //Regex to find all instances of the form number
formNum++ //Increment the form number
newForm.innerHTML = newForm.innerHTML.replace(formRegex, `form-${formNum}-`) //Update the new form to have the correct form number
container.insertBefore(newForm, addButton) //Insert the new form at the end of the list of forms
totalForms.setAttribute('value', `${formNum+1}`) //Increment the number of total forms in the management form
}
</script>

How to check if an input value is exists and validate if yes in Django

I made a text input filed with jquery autocomplete where I query the users. So if I start typing the name of a user it shows the related users. It works fine but I like to avoid
that if the user like not to choose from the pupped up list and possibly type all the name and it has a mistake.
So I like to make a function that checks if the added value of the field is equals with any of the users in the database. How to do that?
html
<input type="text" class="form-control" placeholder="Type name here" name="kap_bar_01" id="kap_bar_01">
<script>
$(function() {
var names = [
{% for u in related_users %}
"{{ u.user.last_name }} {{ u.user.first_name }}",
{% endfor %}
];
$( "#kap_bar_01" ).autocomplete({
source: names
});
});
</script>
models.py
class Kapcsolodasok(models.Model):
def __str__(self):
return str(self.user_name)
user_name = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
date = models.DateField(auto_now_add=True, auto_now=False, blank=True)
kap_bar_01 = models.TextField(max_length=200)
views.py
def kapcsolodasok(request):
profile = Profile.objects.get(user=request.user)
related_users = Profile.objects.filter(projekt=profile.projekt)
context = {
'related_users': related_users,
'profile': profile,
}
#lots of stuffs here
return render(request, 'stressz/kapcsolodasok.html', context)
Thank you in advance!
from django.contrib.auth.models import User
userList = User.objects.values()
The above code will get the usernames as list and loopover to check the username entered.But i think you want it to be checked in realtime so you have to use channels or ajax etc.Otherwise just pass the username value as url url/ and check it

Add row dynamically in django formset

In my django app I have two models i.e Player and Team which are connected by many to many relationship. To add the data dynamically in my tables I want to use javascript to add Add row or Remove Row button in my forms but unable to do so.
Here are the details:
Models.py
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
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
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})
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="/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>
How can I use the javascript to add or delete the rows connected by many to many relationship ?
The above code gives us the following:
To keep it simple and generic, I reduced the OP's example to a single model and a basic formset, without the Team-Player many-to-many relation. The principle of the JavaScript part remains the same. If you do want to implement the many-to-many relation, you could use e.g. an inline formset, as explained here.
So, suppose we have a simple model:
class Player(models.Model):
name = models.CharField(max_length=50)
age = models.IntegerField()
Our view could look like this (based on the example in the docs):
def my_formset_view(request):
response = None
formset_class = modelformset_factory(
model=Player, fields=('name', 'age'), extra=0, can_delete=True)
if request.method == 'POST':
formset = formset_class(data=request.POST)
if formset.is_valid():
formset.save()
response = redirect(to='my_success_view')
else:
formset = formset_class()
if response is None:
response = render(
request, 'myapp/my_formset_template.html', dict(formset=formset))
return response
The my_formset_template.html django template below (skipping the boilerplate) enables us to add and remove formset-forms:
...
<template id="id_formset_empty_form">{{ formset.empty_form }}</template>
<form method="post" id="id_html_form" autocomplete="off">
{% csrf_token %}
<table id="id_formset_container">
{{ formset }}
</table>
<div id="id_formset_add_button" style="text-decoration: underline; cursor: pointer;">Add</div>
<input id="id_formset_submit_button" type="submit" value="Submit">
</form>
...
The HTML <template> element makes it easy to copy the content from formset.empty_form.
Side note: If we don't set autocomplete="off", the browser will cache the TOTAL_FORMS value on the management form, even after reloading the page.
Now, the following JavaScript does the job for me (no attempt was made to optimize, I just tried to make it easy to read):
window.addEventListener('load', (event) => {
// get form template and total number of forms from management form
const templateForm = document.getElementById('id_formset_empty_form');
const inputTotalForms = document.querySelector('input[id$="-TOTAL_FORMS"]');
const inputInitialForms = document.querySelector('input[id$="-INITIAL_FORMS"]');
// get our container (e.g. <table>, <ul>, or <div>) and "Add" button
const containerFormSet = document.getElementById('id_formset_container');
const buttonAdd = document.getElementById('id_formset_add_button');
const buttonSubmit = document.getElementById('id_formset_submit_button');
// event handlers
buttonAdd.onclick = addForm;
buttonSubmit.onclick = updateNameAttributes;
// form counters (note: proper form index bookkeeping is necessary
// because django's formset will create empty forms for any missing
// indices, and will discard forms with indices >= TOTAL_FORMS, which can
// lead to funny behavior in some edge cases)
const initialForms = Number(inputInitialForms.value);
let extraFormIndices = [];
let nextFormIndex = initialForms;
function addForm () {
// create DocumentFragment from template
const formFragment = templateForm.content.cloneNode(true);
// a django form is rendered as_table (default), as_ul, or as_p, so
// the fragment will contain one or more <tr>, <li>, or <p> elements,
// respectively.
for (let element of formFragment.children) {
// replace the __prefix__ placeholders from the empty form by the
// actual form index
element.innerHTML = element.innerHTML.replace(
/(?<=\w+-)(__prefix__|\d+)(?=-\w+)/g,
nextFormIndex.toString());
// add a custom attribute to simplify bookkeeping
element.dataset.formIndex = nextFormIndex.toString();
// add a delete click handler (if formset can_delete)
setDeleteHandler(element);
}
// move the fragment's children onto the DOM
// (the fragment is empty afterwards)
containerFormSet.appendChild(formFragment);
// keep track of form indices
extraFormIndices.push(nextFormIndex++);
}
function removeForm (event) {
// remove all elements with form-index matching that of the delete-input
const formIndex = event.target.dataset.formIndex;
for (let element of getFormElements(formIndex)) {
element.remove();
}
// remove form index from array
let indexIndex = extraFormIndices.indexOf(Number(formIndex));
if (indexIndex > -1) {
extraFormIndices.splice(indexIndex, 1);
}
}
function setDeleteHandler (containerElement) {
// modify DELETE checkbox in containerElement, if the checkbox exists
// (these checboxes are added by formset if can_delete)
const inputDelete = containerElement.querySelector('input[id$="-DELETE"]');
if (inputDelete) {
// duplicate the form index instead of relying on parentElement (more robust)
inputDelete.dataset.formIndex = containerElement.dataset.formIndex;
inputDelete.onclick = removeForm;
}
}
function getFormElements(index) {
// the data-form-index attribute is available as dataset.formIndex
// https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes#javascript_access
return containerFormSet.querySelectorAll('[data-form-index="' + index + '"]');
}
function updateNameAttributes (event) {
// make sure the name indices are consecutive and smaller than
// TOTAL_FORMS (the name attributes end up as dict keys on the server)
// note we do not need to update the indices in the id attributes etc.
for (let [consecutiveIndex, formIndex] of extraFormIndices.entries()) {
for (let formElement of getFormElements(formIndex)){
for (let element of formElement.querySelectorAll('input, select')) {
if ('name' in element) {
element.name = element.name.replace(
/(?<=\w+-)(__prefix__|\d+)(?=-\w+)/g,
(initialForms + consecutiveIndex).toString());
}
}
}
}
updateTotalFormCount();
}
function updateTotalFormCount (event) {
// note we could simply do initialForms + extraFormIndices.length
// to get the total form count, but that does not work if we have
// validation errors on forms that were added dynamically
const firstElement = templateForm.content.querySelector('input, select');
// select the first input or select element, then count how many ids
// with the same suffix occur in the formset container
if (firstElement) {
let suffix = firstElement.id.split('__prefix__')[1];
let selector = firstElement.tagName.toLowerCase() + '[id$="' + suffix + '"]';
let allElementsForId = containerFormSet.querySelectorAll(selector);
// update total form count
inputTotalForms.value = allElementsForId.length;
}
}
}, false);
Note that simply adding and removing formset forms is not that complicated, until something goes wrong: Approximately half the lines above have to do with handling edge cases, such as failed validation on forms that were added dynamically.

How to create Flask-Stripe Checkout and Charge for the multiple items

I am building an integration with Stripe by following the examples in its documentation, but I can't understand the part of creating a Charge for more than one product.
I was looking all over Stripe's docs and was searching for any articles / forums about the similar issue but was unable to find anything. I'd be very grateful to either some links to the articles on this matter or any tips to help me to understand how to solve it.
Here's a server side code:
```python
#app.route("/checkout", methods=["GET", "POST"])
def checkout():
if request.method == "POST":
# Process a JSON string with a checkout information:
# { item_id: item_quantity, ... }
# Build the SQL query based on it
items = {}
shopping_cart = request.form["cart_checkout"]
shopping_cart = shopping_cart.lstrip("{")
shopping_cart = shopping_cart.rstrip("}")
shopping_cart = shopping_cart.split(",")
sqlQuery = "SELECT * FROM Items WHERE item_id IN ("
for KeyValPair in shopping_cart:
Key = KeyValPair.split(":")[0]
Key = Key.strip('"')
sqlQuery = sqlQuery + Key + ","
Value = KeyValPair.split(":")[1]
items[Key] = Value
sqlQuery = sqlQuery.rstrip(",")
sqlQuery = sqlQuery + ") ORDER BY item_id ASC"
cart_items = sql_select(sqlQuery)
# Add a column about the quantity of items
for item in cart_items:
item["quantity"] = items[item["item_id"]]
# Build a Stripe checkout list
line_items_list = []
for item in cart_items:
line_item = {}
line_item["name"] = item["item_name"]
line_item["description"] = item["item_description"]
line_item["amount"] = item["price"]
line_item["currency"] = "usd"
line_item["quantity"] = item["quantity"]
line_items_list.append(dict(line_item))
stripe_session = stripe.checkout.Session.create(
submit_type="pay",
payment_method_types=["card"],
line_items=line_items_list,
success_url='https://example.com/success',
cancel_url='https://example.com/cancel',
)
return render_template("checkout.html",
stripe_id=stripe_session.id,
stripe_pk=stripe_keys["PUBLIC_KEY"])
return redirect("/")
```
And here's a part of HTML template:
```html
<form action="/checkout" method="post" id="form_checkout" onsubmit="return cart_info()"
...
<input type="hidden" name="cart_checkout" id="checkout_info" value="{{ cart_checkout }}">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="{{ stripe_pk }}"
data-name="Company Name"
data-image="https://stripe.com/img/documentation/checkout/marketplace.png"
data-description="A description of the product or service being purchased"
data-amount="999"
data-shipping-address="true"
data-zip-code="true"
data-allow-remember-me="true"
data-panel-label="Pay"
data-label="Checkout"
data-locale="auto">
</script>
</form>
```
If I do a simple example of Charge from Stripe docs, like this:
```python
#app.route('/charge', methods=['POST'])
def charge():
# Amount in cents
amount = 500
customer = stripe.Customer.create(
email='customer#example.com',
source=request.form['stripeToken']
)
charge = stripe.Charge.create(
customer=customer.id,
amount=amount,
currency='usd',
description='Flask Charge'
)
return render_template('charge.html', amount=amount)
```
Then I can create without any issues a successful test Charge, it displays with the success label in my Stripe's dashboard. If I use stripe.checkout.Session.create, Stripe dashboard properly creates an incomplete record about my Checkout session with the selected list of items, but I've no idea how to proceed from there to finalise the Charge for them.
As often happens, when I start asking questions, I eventually find the answers on my own, lol. I've had a "checkout.html" template but it didn't work, and no errors were displaying, so I assumed that I was missing some more code required for it all to work.
As it happened, all I was missing, was "" in a line of code. Here's a working Checkout session with the addition of a bit of JavaScript:
{% extends "layout.html" %}
{% block title %}Checkout{% endblock %}
{% block head %}
<script src="https://js.stripe.com/v3/"></script>
{% endblock %}
{% block main %}
<!-- Main content -->
<div class="wrapper main-content">
{% with messages = get_flashed_messages(with_categories=true) %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endwith %}
<h2 class="section-header">Checkout</h2>
<p id="result_msg"></p>
<button onclick="checkout()">Checkout</button>
</div>
<script type="text/javascript">
function checkout() {
var stripe = Stripe("{{ stripe_pk }}");
stripe.redirectToCheckout({
// Make the id field from the Checkout Session creation API response
// available to this file, so you can provide it as parameter here
// instead of the {{CHECKOUT_SESSION_ID}} placeholder.
sessionId: "{{CHECKOUT_SESSION_ID}}"
}).then(function (result) {
// If `redirectToCheckout` fails due to a browser or network
// error, display the localized error message to your customer
// using `result.error.message`.
document.getElementById("result_msg").innerHTML = result.error.message;
});
}
</script>
{% endblock %}

Making a Javascript click function for model objects formatted in Django Tables2 that shows related divs

I'm trying to make a Javascript function involving a django-tables2 table featuring model objects that will show relevant information when clicked. Here is the relevant code:
models.py
class Student(models.Model):
student_id = models.CharField(max_length=128, unique=True, null=True, blank=True)
first_name = models.CharField(max_length=128)
last_name = models.CharField(max_length=128)
ssn = USSocialSecurityNumberField(null=False)
gender = models.CharField(max_length=128, choices=GENDER_CHOICES)
country = CountryField(default='US', blank=True)
primary_phone = models.CharField(max_length=128)
email = models.EmailField(max_length=254, validators=[validate_email])
background = models.CharField(max_length=128, choices=BACKGROUND_CHOICES)
location = models.CharField(max_length=128, choices=LOCATION_CHOICES, default='south_plainfield')
created_at = models.DateTimeField(null=True, auto_now_add=True)
tables.py
class StudentListTable(tables.Table):
name = tables.TemplateColumn('''{{ record.first_name }} {{ record.last_name }}''', verbose_name=u'Name')
manage = tables.TemplateColumn('''Update / Delete''')
assign = tables.TemplateColumn('''Courses / Employment / Counselor / Show''')
class Meta:
model = Student
fields = ('student_id', 'name', 'gender', 'ssn', 'email', 'primary_phone', 'created_at', 'manage', 'assign')
row_attrs = {
'id': lambda record: record.pk
}
attrs = {'class': 'table table-hover', 'id': 'student_list'}
views.py
def all_My_Student(request):
student_list = Student.objects.order_by('-created_at')
table = StudentListTable(student_list)
RequestConfig(request).configure(table)
return render(request, 'students/all_my_student.html', {'table': table, 'student_list': student_list})
all_my_student.html
{% block main_content %}
<div class="box box-primary" id="student_list_table">
{% if table %}
{% render_table table %}
{% endif %}
</div>
{% for student in student_list %}
<div class="detailed" id="{{ student.id }}">
{{ student.first_name }} {{ student.last_name }}
</div>
{% endfor %}
{% endblock %}
{% block custom_javascript %}
<script>
$(document).ready(function()
{
$('.detailed').hide();
}
);
</script>
{% endblock %}
What I want to do is make a Javascript function that's basically like this: As you can see, the "detailed" class divs are hidden from the start. When you click on a row in the model object table, the "detailed" class div with an id that matches that of the row (or basically corresponds with the same model object in both the table loop and the second loop) will show, and the rest will still be hidden. I hope that it's clear.
As a sidenote, if the queryset used in the table is the same as student_list there is no need to pass student_list separately to the template context, you can just use table.data.
I would hide the <div class="detailed"> blocks using CSS, and not using JavaScript, as if you do use JavaScript, they might be visible for a slight moment when the page loads.
You should try to avoid adding id attributes to too much html elements. In your example, you add the student id as an id attribute to both the table <tr>'s and the <div class="detailed"> elements. The HTML spec requires ID's to be unique in the whole document, so if you want to use them, make sure they are unique.
It's better to use data attributes. In the example below, I added the attribute data-student-id to both the table row and the <div class="detailed"> element. You can now select div[data-student-id="12"] to get the details div element for student with id = 12:
# table.py
class StudentListTable(tables.Table):
name = tables.TemplateColumn('''...''', verbose_name=u'Name')
manage = tables.TemplateColumn('''....''')
class Meta:
model = Student
fields = ('student_id', 'name', 'gender', 'ssn', 'email', 'primary_phone', 'created_at', 'manage', 'assign')
row_attrs = {
'data-student-id': lambda record: record.pk
}
attrs = {'class': 'table table-hover', 'id': 'student_list'}
{# django template #}
<style>
.detailed {
display: none;
}
</style>
{% for student in table.data %}
<div class="detailed" data-student-id="{{ student.id }}">
{{ student.first_name }} {{ student.last_name }}
</div>
{% endfor %}
{% block custom_javascript %}
<script>
$(document).ready(function() {
$('#student_list tr').on('click', function () {
var student_id = $(this).data('student-id');
$('div[data-student-id="' + student_id + '"]').show();
});
});
</script>
{% endblock %}
Alternatively, you could add the required data to data- attributes of the table and create the details view in JavaScript. That maybe would make the generated HTML a bit smaller.

Categories

Resources