How can I set inputs dynamically in Django using JavaScript? - javascript

I have the following models.py file which has the table and modelform. I'm working with has and the HTML template and I want some JavaScript code to multiply the value of rate input and quantity input then set it dynamically into total cost input before I click on submit.
ModelForm:
class ExpenseBasedTransactionForm(ModelForm):
def __init__(self, *args, **kwargs):
super(ExpenseBasedTransactionForm, self).__init__(*args, **kwargs)
eb_client = Clientele.objects.filter(clientele_type__icontains='eb client').values_list("id", "clientele_name")
eb_main = []
for i in eb_client:
k = list(i)
k[0] = str(k[0])
l = (tuple(k))
eb_main.append(l)
self.fields['eb_client'] = ChoiceField(choices=eb_main, label='EB Client', )
class Meta:
model = ExpenseBasedTransaction
# fields = '__all__' #to include all the fields in the form
# specify what fields to be included in the form
fields = ['date_initiated', 'eb_client', 'rate',
'quantity', 'total_cost']
widgets = {
'date_initiated': AdminDateWidget(),
'rate': NumberInput(attrs={'class': 'form-control', 'id': 'rate'}),
'quantity': NumberInput(attrs={'class': 'form-control', 'id': 'quantity'}),
'total_cost': NumberInput(attrs={'class': 'form-control', 'id': 'total'}),
}
html:
{% extends "base.html" %}
{% load bootstrap3%}
{% block content %}
<div class="content-wrapper" id="tab">
<div class="content">
<div class="head">
<div class="row">
<div class="col-sm-7 title" >
<span><i class="fa fa-pencil"></i> Record An Expense</span>
</div>
</div>
</div>
<div class="content padding table-responsive" >
<div class="wrapper">
<form method="post">
{% csrf_token %}
{% bootstrap_form form %}
<br>
<button type="reset" class="btn btn-warning m-sm-right"><i class="fa fa-trash-o fa-lg
white" aria-hidden="true"></i> Clear</button>
<button id="proceed" class="btn btn-primary m-sm-right">Proceed <i class="fa fa-
arrow-circle-right fa-lg white" aria-hidden="true"></i></button>
</form>
</div>
</div>
</div> <!-- end of content -->
</div> <!--end of content-wrapper -->
<script type="text/javascript">
</script>
{% endblock content %}

please try this:
update your widgets in Modelform (note that you needn't set custom id in your modelform widget, just add onchange event:
widgets = {
'date_initiated': AdminDateWidget(),
'rate': NumberInput(attrs={'class': 'form-control', 'onchange': 'calculateTotalCost()'}),
'quantity': NumberInput(attrs={'class': 'form-control', 'onchange': 'calculateTotalCost()'}),
'total_cost': NumberInput(attrs={'class': 'form-control'}),
}
and then describe this event-function in your template (add this code to your template between script tag:
function calculateTotalCost () {
var rate = Number(document.getElementById('id_rate').value);
var quantity = Number(document.getElementById('id_quantity').value);
if (rate && quantity) {
document.getElementById('id_total_cost').value = rate * quantity
}
}

Related

when i submit my form My Comment form dosen't save in database

I create a 5 star rating system using Django with Javascript and I want to user comment like this:
I want to click on the stars and then return the value that is an integer
this is my models:
class Review(models.Model):
course = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='reviews')
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
rating = models.IntegerField(null=True, validators=[MinValueValidator(1), MaxValueValidator(5)])
comment = models.TextField()
created = models.DateField(auto_now_add=True)
active = models.BooleanField(default=False)
def __str__(self):
return f'{self.first_name} {self.last_name}
my views:
#csrf_exempt
def productDetailView(request, id, slug):
product = get_object_or_404(Product, id=id, slug=slug, available=True)
new_comment = None
if request.method == 'POST':
form = ReviewForm(request.POST)
if form.is_valid():
new_comment = form.save(commit=False)
new_comment.course = product
new_comment.rating = request.POST['rating']
new_comment.save()
else:
form = ReviewForm()
return render(request, 'shop/product_detail.html', {'product': product, 'form': form})
JS function:
$(document).ready(function(){
$('.rate .rate-item').on('click', function(){
var value = $(this).data('value');
$.ajax({
url: '{{ product.get_absolute_url }}',
type: 'POST',
data: {'rating': value},
success: function(response){
alert('Rating saved successfully!');
}
});
});
});
and my template:
<form method="post">
<div class="form-singel">
{{ form.first_name|attr:" placeholder:Fast name" }}
</div>
<div class="form-singel">
{{ form.first_name|attr:" placeholder:Last Name"}}
</div>
<div class="rate-label">Your Rating:</div>
<div class="rate">
<div data-value="1" class="rate-item"><i class="fa fa-star" aria-hidden="true"></i></div>
<div data-value="2" class="rate-item"><i class="fa fa-star" aria-hidden="true"></i></div>
<div data-value="3" class="rate-item"><i class="fa fa-star" aria-hidden="true"></i></div>
<div data-value="4" class="rate-item"><i class="fa fa-star" aria-hidden="true"></i></div>
<div data-value="5" class="rate-item"><i class="fa fa-star" aria-hidden="true"></i></div>
</form>
<div class="form-singel">
{{ form.first_name|attr:" placeholder:Comment" }}
</div>
<div class="form-singel">
<button type="submit" class="main-btn">Post Comment</button>
</div>
How do i fix it?
You do not need AJAX to send rating value separately. Instead you can use JavaScript to update the form field rating value (while working with star elements feature), and submit it once it is ready:
forms.py
class ReviewForm(forms.ModelForm):
class Meta:
model = Review
fields = ['first_name', 'last_name', 'comment', 'rating', 'course']
labels = {
'rating': '',
'course': '',
}
widgets = {
'rating': forms.HiddenInput(),
'course': forms.HiddenInput(),
'first_name': forms.TextInput(attrs={'placeholder': 'First Name'}),
'last_name': forms.TextInput(attrs={'placeholder': 'Last Name'}),
'comment': forms.Textarea(attrs={'placeholder': 'Comment'}),
}
views.py
def product_detail(request, id, slug=None):
product = get_object_or_404(Product, id=id, available=True)
if request.method == 'POST':
form = ReviewForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, "Comment saved successfully")
# Probably want to redirect somewhere else
return redirect(reverse('core:product_detail', kwargs={'id': product.id}))
else:
form = ReviewForm(initial={'course': product})
return render(request, 'shop/product_detail.html', {'product': product, 'form': form})
A few notes:
Good practice is functions in snake_case and classes in CamelCase.
You do no need two fields to retrieve the object. Either go with
id or slug. Since you did not share all models I am not sure if
slug is also unique so I just used id as usual.
Ensure csrftoken presence in your requests, specially when
modifying the database, it is a security matter. Of course it is also
possible to use it with AJAX.
shop/product_detail.html:
<form method="POST" action="">
{% csrf_token %}
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
<label for="{{ field_name.label_tag }}">{{ field.label_tag }}</label><br>
{{ field }}
</div>
{% endfor %}
<div class="rate-label">Your Rating:</div>
<div id="rate" style="display: flex; flex-flow: row nowrap;">
<div data-value="1" class="rate-item"><i class="far fa-star" aria-hidden="true"></i></div>
<div data-value="2" class="rate-item"><i class="far fa-star" aria-hidden="true"></i></div>
<div data-value="3" class="rate-item"><i class="far fa-star" aria-hidden="true"></i></div>
<div data-value="4" class="rate-item"><i class="far fa-star" aria-hidden="true"></i></div>
<div data-value="5" class="rate-item"><i class="far fa-star" aria-hidden="true"></i></div>
</div>
<br>
<input type="submit" value="Post Comment">
</form>
<script>
const filledStarClass = 'fas fa-star';
const emptyStarClass = 'far fa-star';
var starsCount = 0
$( document ).ready(function() {
$('#submit').prop('disabled', true);
});
$('.rate-item').on('click', function(){
var value = $(this).data('value');
$('#submit').removeAttr('disabled');
updateStars(value);
$('#id_rating').val(value)
});
function updateStars(value) {
var childDivs = $('#rate').children();
for( i=0; i< value; i++ ) {
var star = childDivs[i].children[0];
star.className = filledStarClass;
}
if (value < 5) {
for( i=value; i< childDivs.length; i++ ) {
var star = childDivs[i].children[0];
star.className = emptyStarClass;
}
}
}
</script>
On template, the main point is at $('#id_rating').val(value) where we update the form value based on the star item data-value.

Data not displaying from the flask sqlalchemy database

I want to display the data of a person that is inputted via a pop-up form. But for whatever reason, the data isn't showing up on the HTML page.
Here is the HTML code for the home page
{% block title %}Home{% endblock %}
{% block content %}
<!-- <script type="text/javascript" src="js/jquery.js"></script> -->
<!-- onclick="window.location.href='add-users'" -->
<head>
<link rel="stylesheet" href="{{ url_for('static',filename='add-users.css') }}" type="text/css"/>
<!-- <h4>Log out</h4> -->
<h1>
<div class="dropdown" style="position:absolute; top:80px; right:170px;">
<button class="btn btn-info dropdown-toggle" type="button" id="dropdownMenu2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ user.name }}
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenu2">
<a style="text-decoration: none;"href="/logout"><button class="dropdown-item" type="button">Log Out</button></a>
<!-- <button class="dropdown-item" type="button">Switch Users</button>
<button class="dropdown-item" type="button">Something else here</button> -->
</div>
</div></h1>
<h1><button style="position:absolute; top:80px; right:100px; text-decoration: none"title="Add people" class="button" data-modal="modalOne"><span style="font-size:36px;">⊕</span></button></h1>
<div id="modalOne" class="modal">
<div class="modal-content">
<div class="contact-form">
<a class="close">⊗</a>
<form method="POST" action="/">
<h2>Add person</h2>
<br>
<!-- 2 column grid layout with text inputs for the first and last names -->
<div class="row mb-4">
<div class="col">
<div class="form-outline">
<input type="text" name="first_name" id="first_name" class="form-control" placeholder="First name"/>
</div>
</div>
<div class="col">
<div class="form-outline">
<input type="text" name="last_name" id="last_name" class="form-control" placeholder="Last name"/>
</div>
</div>
</div>
<!-- Email input -->
<div class="form-outline">
<input type="email" name="person_email" id="person_email" placeholder="Email" class="form-control" />
</div>
<!-- Address input -->
<div class="form-outline">
<input type="address" name="address" placeholder="Address" class="form-control" />
</div>
<div class="form-outline">
<input type="company" name="company" placeholder="Company" class="form-control" />
</div>
<div class="form-outline">
<input type="city" name="city" placeholder="City" class="form-control" />
</div>
<div class="form-outline">
<input type="county" name="county" placeholder="County" class="form-control" />
</div>
<!-- Submit button -->
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
</div>
</div>
</head>
<body>
<div id=container>
<ul class="list-group list-group-flush">
{% for item in user.person %}
{{ item.first_name }}
{% endfor %}
</ul>
</div>
</body>
<script type="text/javascript">
let modalBtns = [...document.querySelectorAll(".button")];
modalBtns.forEach(function (btn) {
btn.onclick = function () {
let modal = btn.getAttribute("data-modal");
document.getElementById(modal).style.display = "block";
};
});
let closeBtns = [...document.querySelectorAll(".close")];
closeBtns.forEach(function (btn) {
btn.onclick = function () {
let modal = btn.closest(".modal");
modal.style.display = "none";
};
});
window.onclick = function (event) {
if (event.target.className === "modal") {
event.target.style.display = "none";
}
};
if ( window.history.replaceState ) {
window.history.replaceState( null, null, window.location.href );
}
</script>
{% endblock %}
Here is the code for models.py
from sqlalchemy import null
from . import __innit__ # importing from the package (website) the db
from flask_login import UserMixin
db = __innit__.db
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(20), unique=False, nullable=False)
last_name = db.Column(db.String(), unique=False, nullable=False)
# person_email = db.Column(db.String(), unique=False, nullable=False)
company = db.Column(db.String(), unique=False)
address = db.Column(db.String(), unique=False)
county = db.Column(db.String(), unique=False)
city = db.Column(db.String(), unique=False)
#image = db.Column(db.String(), unique=True, default="default.jpg")
user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), unique=True)
email = db.Column(db.String(), unique=True)
password = db.Column(db.String(150), nullable=False)
person = db.relationship("Person")
Here is the code for views.py
#views.route('/', methods=["GET", "POST"])
#login_required
def home(new_person):
if request.method == "POST":
first_name = request.form.get("first_name")
last_name = request.form.get("last_name")
person_email = request.form.get("person_email")
company = request.form.get("company")
address = request.form.get("address")
city = request.form.get("city")
county = request.form.get("county")
print(f"The first name is {first_name}")
if first_name == "":
flash("First name field empty", category="error")
elif last_name == "":
flash("Last name field empty", category="error")
elif person_email == "":
flash("Email field empty", category="error")
elif company == "":
flash("Company field empty", category="error")
elif address == "":
flash("Address field empty", category="error")
elif city == "":
flash("City field empty", category="error")
elif county == "":
flash("County field empty", category="error")
else:
new_person = Person(first_name=first_name, last_name=last_name, person_email=person_email, company=company, address=address, city=city, county=county, user_id=current_user.id)
db.session.add(new_person)
db.session.commit()
print("USER ADDED")
flash("New person added", category="success")
redirect(url_for("views.home"))
return render_template("second-home.html", user=current_user, new_person=new_person)
By the way the statement print(f"THe first name is {first_name}") isn't working, meaning the form isn't being accessed however when I hit the submit button, the terminal outputs a POST request
Here is the HTML page in which the data should be shown
It might be that you need to explicitly set the id value for each of the inputs in your modal. You have name specified, but I think id is a more stable option when collecting form data.
If you're running this locally in dev mode (via terminal/cmd line) I would absolutely suggest throwing a breakpoint() in before print(f"The first name is {first_name}") and look at what values request.form actually returns.

Duplicated element after Ajax run

I am trying to Auto-complete form fields using Ajax and Jquery.
First I used Django and the views.py function is:
def CreateWellMon(request):
if request.method == 'POST':
form = SurveillanceDesPuits_F(request.POST or None)
if form.is_valid():
form.instance.author = request.user
form.save()
return redirect('WellMonitor')
else:
try:
PUITS_id = request.GET.get('PUITS')
record = SurveillanceDesPuits.objects.filter(PUITS_id__id__exact=PUITS_id)[:1]
record2 = SurveillanceDesPuits.objects.get(id= record[0].id)
form = SurveillanceDesPuits_F(instance=record2)
return render(request, 'measure/Surveill_Wells/Add_wellMntr2.html', {'form': form})
except:
record2 = SurveillanceDesPuits.objects.all().first()
form = SurveillanceDesPuits_F(instance=record2)
return render(request, 'measure/Surveill_Wells/Add_wellMntr2.html', {'form': form})
So here I just selected the last record from the database at first. After when the user chooses a Well it reloads the last record of the element.
my HTML page code is:
{% extends 'Home/base2.html' %}
{% block content %}
{% load crispy_forms_tags %}
<div class="w3-panel w3-border w3-light-grey w3-round-large sizeA"> <h2>Well Information</h2> <h2 class="border-bottom pb-1 mb-3"><b>Add New montoring record 3</b></h2> </div>
{% if form %}
<div class="border p-3 mb-3 mt-3 w3-round-large w3-light-grey border-dark">
<form method="POST" id="SurveillanceDesPuits_F" data-record-url="{% url 'Add_wellMntr' %}">
{% csrf_token %}
<!-- form from views.py-->
<div class="border p-2 mb-3 mt-3 border-secondary">
<div class="form-row">
<div id= "PUITS" class="form-group col-md-3 mb-0">
{{form.PUITS|as_crispy_field}}
</div>
<div class="form-group col-md-3 mb-0">
{{ form.CS |as_crispy_field}}
</div>
<div class="form-group col-md-3 mb-0">
{{ form.MODE|as_crispy_field}}
</div>
<div class="form-group col-md-3 mb-0">
{{ form.SITUATION |as_crispy_field}}
</div>
</div>
<div class="form-row">
<div class="form-group col-md-3 mb-0">
{{ form.DATE_TEST|as_crispy_field }}
</div>
<div id='DUSE' class="form-group col-md-3 mb-0">
{{ form.DUSE|as_crispy_field }}
</div>
<div class="form-group col-md-3 mb-0">
{{ form.PRES_TBG|as_crispy_field }}
</div>
<div class="form-group col-md-3 mb-0">
{{ form.PRES_CSG|as_crispy_field }}
</div>
</div>
<div class="form-row">
<div class="form-group col-md-8 mb-0">
{{ form.OBSERVATION|as_crispy_field }}
</div>
</div>
</div>
<input class="btn btn-success mb-4" type="submit" value="ADD Record">
</form>
</div>
{% endif %}
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script type="text/javascript"> $(document).ready(function(){
$("#id_PUITS").change(function(){
var url = $("#SurveillanceDesPuits_F").attr("data-record-url");
var PUITSId = $(this).val();
$.ajax({
type: 'GET' ,
url: url,
data: {
'PUITS': PUITSId
},
success: function (data){
$('#SurveillanceDesPuits_F').html(data);
}
});
});
return false;
}); </script>
{% endblock content %}
The problem is that after selecting a well the AJAX duplicates some elements in the page as described in the photo.
and how I do to solve this and keep the selected well because it is a choice field?
First I made a change in models.py, I deleted to_field='WellID', so it will keep the selected well after the AJAX call.
class SurveillanceDesPuits(models.Model):
PUITS = models.ForeignKey(Surveill_Wells , on_delete=models.CASCADE)
DATE_TEST= models.DateField()
....
then I changed the views.py:
def CreateWellMon(request):
if request.method == 'POST':
form = SurveillanceDesPuits_F(request.POST or None)
if form.is_valid():
form.instance.author = request.user
form.save()
return redirect('WellMonitor')
else:
try:
PUITS_id = request.GET.get('PUITS')
record2 = SurveillanceDesPuits.objects.filter(PUITS_id__id__exact=PUITS_id)[:1]
return JsonResponse({'record2': list(record2.values())}, safe=False)
except:
form = SurveillanceDesPuits_F()
return render(request, 'measure/Surveill_Wells/Add_wellMntr2.html', {'form': form})
the last change is in the HTML page and the ajax code call will be as:
<script type="text/javascript">
$.ajax({
type: 'GET' ,
url: url,
data: {'PUITS': PUITSId },
dataType: "json",
success: function (response){
var response = JSON.parse(response);
const object = response[0]
$("#id_PUITS").val(object.fields.PUITS);
$("#id_DATE_TEST").val(object.fields.DATE_TEST);
$("#id_MODE").val(object.fields.MODE);
$("#id_CS").val(object.fields.CS);
$("#id_SITUATION").val(object.fields.SITUATION);
$("#id_DUSE").val(object.fields.DUSE);
$("#id_PRES_TBG").val(object.fields.PRES_TBG);
$("#id_PRES_CSG").val(object.fields.PRES_CSG);
$("#id_PRES_AVD").val(object.fields.PRES_AVD);
$("#id_RESEAU_GL").val(object.fields.RESEAU_GL);
$("#id_ANNULAIRE_TECH").val(object.fields.ANNULAIRE_TECH);
$("#id_OBSERVATION").val(object.fields.OBSERVATION);
$("#id_Controle_Pression_ENSP").val(object.fields.Controle_Pression_ENSP);
$("#id_Test_Puits").val(object.fields.Test_Puits);
$("#id_Controle_Pression_DP").val(object.fields.Controle_Pression_DP);
},
});
return false;
});
</script>
this will avoid the duplicated page caused by the
$("#SurveillanceDesPuits_F").html(response);
Many thanks

Dynamically save the Django form

I am trying not to use formset in my form. Instead of that, I'm trying to create the form dynamically and save all forms data in the DB.
I can create a dynamic form, but whenever I create multiple forms in "create order", it always saves the last forms data. For example, once I create 3 forms, and after saving the form, the table shows me only 3rd form data, which means it does not save all data together.
views.py
def create_order(request):
from django import forms
form = OrderForm()
if request.method == 'POST':
forms = OrderForm(request.POST)
if forms.is_valid():
po_id = forms.cleaned_data['po_id']
supplier = forms.cleaned_data['supplier']
product = forms.cleaned_data['product']
part = forms.cleaned_data['part']
order = Order.objects.create(
po_id=po_id,
supplier=supplier,
product=product,
part=part,
)
forms.save()
return redirect('order-list')
context = {
'form': form
}
return render(request, 'store/addOrder.html', context)
forms.py
class OrderForm(forms.ModelForm):
class Meta:
model = Order
fields = ['supplier', 'product', 'part','po_id']
widgets = {
'supplier': forms.Select(attrs={'class': 'form-control', 'id': 'supplier'}),
'product': forms.Select(attrs={'class': 'form-control', 'id': 'product'}),
'part': forms.Select(attrs={'class': 'form-control', 'id': 'part'}),
}
HTML
<form action="#" method="post" id="form-container" novalidate="novalidate">
{% csrf_token %}
<div class="form">
<div class="form-group">
<label for="po_id" class="control-label mb-1">ID</label>
{{ form.po_id }}
</div>
<div class="form-group">
<label for="supplier" class="control-label mb-1">Supplier</label>
{{ form.supplier }}
</div>
<div class="form-group">
<label for="product" class="control-label mb-1">Product</label>
{{ form.product }}
</div>
<div class="form-group">
<label for="part" class="control-label mb-1">Part Name</label>
{{ form.part }}
</div>
</div>
<button id="add-form" type="button">Add Another Order</button>
<div>
<button id="payment-button" type="submit" class="btn btn-lg btn-success btn-block">
<span id="payment-button-amount">Save</span>
</button>
</div>
</form>
<script>
let poForm = document.querySelectorAll(".form")
let container = document.querySelector("#form-container")
let addButton = document.querySelector("#add-form")
let totalForms = document.querySelector("#id_form-TOTAL_FORMS")
let formNum = poForm.length-1
addButton.addEventListener('click', addForm)
function addForm(e){
e.preventDefault()
let newForm = poForm[0].cloneNode(true)
let formRegex = RegExp(`form-(\\d){1}-`,'g')
formNum++
newForm.innerHTML = newForm.innerHTML.replace(formRegex, `form-${formNum}-`)
container.insertBefore(newForm, addButton)
totalForms.setAttribute('value', `${formNum+1}`)
}
</script>
What causes this problem? How can I fix it?

How to handle JavaScript event inside a inlineformset_factory with formset_media_js

I have an inlineformset_factory implemented with formset_media_js, these two by itself are working ok. What I need to implement is to be able to handle the enable and disable state of some checkboxes and input fields that are inside the inlineformset_factory.
I have a javascript that works on the first group of formset created on page load, but when a new formset is added by the user the javascript is not working.
How can I handle the new formsets input fields added by the user with javascript?
If "is chapter" is checked then "is subchapter" and "quantity" are disabled, by default the inlineformset_fatory creates 1 formset on page load, on this formset the javascript works. But when the user adds another formset with button "Add another Budget Item" the javascript is no longer working. If for example, I configure the inlineformser_factory to create 3 formset on page load the javascript works on those 3 formset but not on the formsets added by the user.
forms.py : at this forms.py i have the inlineformset_factory that is created every time the user adds a formset.
from django import forms
from django.forms import inlineformset_factory
from djangoformsetjs.utils import formset_media_js
from accounts.models import ProjectManager
from projects.models import Project, BudgetModel, BudgetModelItems
class CreateBudgetItem(forms.ModelForm):
class Media(object):
js = formset_media_js
class Meta:
model = BudgetModelItems
fields = ('budget_model',)
widgets = {
'budget_item_description': forms.Textarea(attrs={'rows': 2, 'cols': 50}),
'budget_item_item': forms.NumberInput(attrs={'size': 6}),
'budget_item_quantity': forms.NumberInput(attrs={'size': 6}),
}
BudgetItemFormset = inlineformset_factory(BudgetModel, BudgetModelItems,
form=CreateBudgetItem,
fields=('budget_model', 'budget_item_item',
'budget_item_description', 'budget_item_unit',
'budget_item_quantity', 'budget_item_is_chapter',
'budget_item_is_subchapter'),
extra=1,
can_delete=True,
can_order=True
)
views.py
from django.shortcuts import render, redirect
from django.forms import formset_factory
from accounts.models import ProjectManager
from projects.forms.create_project import CreateProjectForm
from projects.forms.create_budgetmodel import BudgetFormset, ProjectForBudgetModel
from projects.forms.create_budgetitem import CreateBudgetItem, BudgetItemFormset
from projects.models import BudgetModel, Project
def create_budget_item(request):
user = request.user.projectmanager
projects = Project.objects.filter(project_manager_id=user)
models = BudgetModel.objects.none()
project_form = ProjectForBudgetModel(user)
budget_item_form = CreateBudgetItem()
formset = BudgetItemFormset()
for project in projects:
models |= BudgetModel.objects.filter(project_id=project.pk)
budget_item_form.fields['budget_model'].queryset = models
if request.method == 'POST':
project_form = ProjectForBudgetModel(user, request.POST)
budget_item_form = CreateBudgetItem(request.POST)
if project_form.is_valid() and budget_item_form.is_valid():
# project_id = project_form.cleaned_data['project']
budget_model_id = budget_item_form.cleaned_data['budget_model']
formset = BudgetItemFormset(request.POST, instance=budget_model_id)
if formset.is_valid():
formset.save()
context = {'project_form': project_form,
'bi_form': budget_item_form,
'formset': formset,
'models': models}
return render(request, 'projects/create_budget_items.html', context)
budget_item_form.html: this form is called (included) at create_budget_items.html
<div data-formset-form>
<div class="card">
<div class="card-body">
<div class="row">
<div class="col">
<table class="table">
<thead class="thead-light">
<tr>
<th scope="col">Item</th>
<th scope="col">Description</th>
<th scope="col">Unit</th>
<th scope="col">Quantity</th>
<th scope="col">Is Chapter</th>
<th scope="col">Is SubChapter</th>
</tr>
</thead>
<tbody>
<tr>
<th>{{ form.budget_item_item }}</th>
<td>{{ form.budget_item_description }}</td>
<td>{{ form.budget_item_unit }}</td>
<td>{{ form.budget_item_quantity }}</td>
<td>{{ form.budget_item_is_chapter }}</td>
<td>{{ form.budget_item_is_subchapter }}</td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-auto">
{% if form.ORDER %}
<div class="row mt-1">
<div class="d-none">{{ form.ORDER }}</div>
<button class="btn btn-info btn-block" type="button" data-formset-move-up-button>
{% trans 'Move up' %}
</button>
</div>
<div class="row mt-1">
<button class="btn btn-info btn-block" type="button" data-formset-move-down-button>
{% trans 'Move down' %}
</button>
</div>
{% endif %}
</div>
<div class="col col-lg-2 mt-1">
{% if form.DELETE %}
<div class="d-none">{{ form.DELETE }}</div>
<button type="button" class="btn btn-danger btn-block h-100" data-formset-delete-button>
{% trans 'Delete' %}
</button>
{% endif %}
</div>
</div>
</div>
</div>
</div>
create_budget_items.html: On this template I have the javascript where I control the enable and disable states of checkboxes and input fields. I thought that by calling the script inside the for loop where the formset is being iterated I was going to be able to control the input fields of the formsets added by the user. Is only working on the formsets created on page load.
{% block dashboard_head %}
{{ formset.media }}
<script type="text/javascript">
function trackDisabled(trigger_id, ...targets) {
const checkbox = document.getElementById(trigger_id);
checkbox.addEventListener('change', e => {
console.log(e.target.checked);
{#console.log(trigger_id);#}
{#console.log(...targets);#}
if (e.target.checked === true) {
targets.forEach(x => {
const element = document.getElementById(x);
element.disabled = true;
element.checked = false;
element.value = ''
})
} else {
targets.forEach(x => document.getElementById(x).disabled = false)
}
})
}
</script>
{% endblock dashboard_head %}
{% block dashboard_content %}
<h1>Create Budget Items</h1>
<form method="post">
{% csrf_token %}
{{ project_form.project }}
<select name="budget_model" id="id_budget_model" class="form-control">
{% with value=bi_form.budget_model.value %}
{% for model in models %}
<option value="{{ model.pk }}" class="{{ model.project.pk }}"
{% if model.pk|slugify == value|slugify %}selected="selected"{% endif %}>
{{ model.budget_name }}
</option>
{% endfor %}
{% endwith %}
</select>
{% load formset_tags %}
<div id="formset" data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
<div data-formset-body>
{% for form in formset %}
{% include "projects/budget_item_form.html" with form=form only %}
<script>
trackDisabled(
'{{ form.budget_item_is_chapter.auto_id }}',
'{{ form.budget_item_is_subchapter.auto_id }}',
'{{ form.budget_item_quantity.auto_id }}'
);
console.log('{{ form.budget_item_is_chapter.auto_id }}');
</script>
{{ form.errors }}
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
{% include "projects/budget_item_form.html" with form=formset.empty_form only %}
{% endescapescript %}
</script>
<div class="row mt-3 mr-1 ml-1">
<!-- This button will add a new form when clicked -->
<div class="col text-center">
<input class="w-75 btn btn-info" type="button"
value="{% trans 'Add another Budget Item' %}" data-formset-add>
</div>
<div class="col text-center">
<button class="w-75 btn btn-success" type="submit">
{% trans 'Create Models' %}
</button>
</div>
</div>
</div>
</form>
{% endblock dashboard_content %}
This is the javascript that finally worked out, was written by a friend.. Thank you FunkyBob!
<script>
function isChapter() {
const root = document.getElementById('formset');
const prefix = root.dataset.formsetPrefix;
console.log({root, prefix});
// listen for all input changes
root.addEventListener('change', ev => {
// check if it matches out name pattern
console.log(ev.target.name);
console.log(ev.target.checked, !ev.target.checked);
console.log(`${prefix}-(\\d+)-budget_item_is_chapter`);
let m = ev.target.name.match(RegExp(`${prefix}-(\\d+)-budget_item_is_chapter`));
// if it's not {prefix}-*-budget_item_is_chapter ignore
if (!m) return;
console.log(m);
let idx = m[1]; // the matched regex group
// Find the related fields, and set them as enabled/disabled
root.querySelector(`[name="${prefix}-${idx}-budget_item_is_subchapter"]`).disabled = ev.target.checked;
root.querySelector(`[name="${prefix}-${idx}-budget_item_is_subchapter"]`).checked = false;
root.querySelector(`[name="${prefix}-${idx}-budget_item_unit"]`).disabled = ev.target.checked;
root.querySelector(`[name="${prefix}-${idx}-budget_item_unit"]`).value = ev.target.checked;
root.querySelector(`[name="${prefix}-${idx}-budget_item_quantity"]`).disabled = ev.target.checked;
root.querySelector(`[name="${prefix}-${idx}-budget_item_quantity"]`).value = ev.target.checked;
console.log("Done!")
});
}
isChapter();
</script>

Categories