Динамическое обновление значений поля в зависимости от выбора, сделанного в другом поле в Django
У меня есть две таблицы.
Inventory
и Invoice
.
InventoryModel:
from django.db import models
class Inventory(models.Model):
product_number = models.IntegerField(primary_key=True)
product = models.TextField(max_length=3000, default='', blank=True, null=True)
title = models.CharField('Title', max_length=120, default='', blank=True, unique=True)
amount = models.IntegerField('Unit Price', default=0, blank=True, null=True)
def __str__(self):
return self.title
InvoiceModel:
У меня есть форма, где человек выбирает Inventory.title
в line_one
. Я хочу, чтобы line_one_unit_price
автоматически заполнялась в соответствии с опцией, выбранной в line_one
. То есть я хочу, чтобы отображалось количество этого товара.
Я попробовал использовать объект JSON и затем отправить его в шаблон.
views.py:
def add_invoice(request):
form = InvoiceForm(request.POST or None)
data = Inventory.objects.all()
dict_obj = model_to_dict(data)
serialized = json.dumps(dict_obj)
total_invoices = Invoice.objects.count()
queryset = Invoice.objects.order_by('-invoice_date')[:6]
if form.is_valid():
form.save()
messages.success(request, 'Successfully Saved')
return redirect('/invoice/list_invoice')
context = {
"form": form,
"title": "New Invoice",
"total_invoices": total_invoices,
"queryset": queryset,
"serialized":serialized,
}
return render(request, "entry.html", context)
Я не уверен, как я могу получить доступ к этому JSON файлу в javascript? Как будет называться этот JSON объект? Возможно ли получить к нему доступ в JavaScript? Как я могу использовать его для динамического обновления цены товара в формах? Спасибо.
Когда вы передаете dict_obj в ваш шаблон, вы можете получить json с помощью
{{ dict_obj|json_script:"hello-data" }}
const value = JSON.parse(document.getElementById('hello-data').textContent);
https://docs.djangoproject.com/en/4.0/ref/templates/builtins/#json-script
изменить значение в зависимости от выбранного элемента
const data = {
"apple": 2.3,
"orange": 1.7,
}
document.getElementById('inventory_item').onchange = function() {
document.getElementById('price').value = data[this.value];
};
<select name="InventoryDropdown" id="inventory_item">
<option value="" selected disabled>---</option>
<option value="apple">apple</option>
<option value="orange">orange</option>
</select>
<input type="text" id="price" >
если я правильно понял, вы получаете pk
инвентаря в этой строке
var line_one=document.getElementById('id_line_one').value
поэтому вам нужно выполнить итерацию по json, чтобы найти его - я проверил это с вашим распечатанным JSON-файлом
function getDictEntryByPk(dict_list, pk) {
for (let i=0; i < dict_list.length; i++) {
if (dict_list[i].pk == pk) {
return dict_list[i]
}
}
}
и использовать его следующим образом:
var line_one=document.getElementById('id_line_one').value;
var line_one_data = getDictEntryByPk(data, line_one);
document.getElementById('id_line_one_unit_price').value = line_one_data.fields.amount
РЕДИТ: то же самое с меньшим количеством кода:
function getDictEntryByPk(dict_list, pk) {
return dict_list.find(element => element.pk == pk);
}
Простой альтернативой может быть переопределение метода Inventory __str__
для включения цены за единицу, так что выпадающий список станет, например, 'Inventory #1 - ₹500'. После этого вам не нужно беспокоиться о JS, JSON или обновлении суммы. Общая сумма может обновляться каждый раз, когда форма отправляется с количеством. В противном случае вам понадобится JS для получения цены единицы товара и вычисления общей цены с учетом количества.
Кроме того, в ваших моделях есть строки, вручную добавленные в счет-фактуру - т.е. строка_один, строка_два и т.д. - это не лучший способ структурирования моделей и данных. Вы должны добавить промежуточную модель под названием 'InvoiceLine' или что-то подобное, которая может обрабатывать любое количество строк счета-фактуры и соединяет с помощью FKs ваши модели инвентаризации и счета-фактуры. Причина, по которой это особенно важно, заключается в том, что в настоящее время все ваши строки имеют models.CASCADE
в качестве опции on_delete, что означает, что счет-фактура с десятью строками может быть полностью удален, даже если будет удалена только одна из ваших моделей инвентаря с FK-связью!
Я выяснил, как добиться этой функциональности. Я хотел бы отдать должное одному человеку, но на самом деле это комбинация ответов многих людей.
в views.py
, который возвращает HTML страницу, где я хочу получить эту функциональность, я написал этот код, Он возвращает объекты Product в HTML файл:
model_data=Inventory.objects.values_list('product_number','amount')
data=[model for model in model_data.values()]
context = {
"data": data,
}
return render(request, "entry.html", context)
В entry.html
я получаю доступ к данным, используя: {{ data|json_script:"hello-data" }}
Тогда я получаю следующие данные:
[{"product_number": 1, "product": "Laptop", "title": "Lenovo", "amount": 50000}, {"product_number": 2, "product": "Single Table Tennis Ball", "title": "Table Tennis Ball Share", "amount": 4}]
В entry.html
я использовал немного javascript:
<script type="text/javascript">
var data = JSON.parse(document.getElementById('hello-data').textContent);
document.getElementById('id_line_one').onchange = function(event){
var data1 = data.find(({product_number}) => product_number == event.target.value);
document.getElementById('id_line_one_unit_price').value = data1 && data1.amount ? data1.amount : 0;
};
Итак, я разбираю данные JSON, а затем, когда пользователь нажимает на выпадающее меню, id которого id_line_one
, я проверяю, совпадает ли возвращаемое число (т.е. номер_продукта) с любым product_number
, присутствующим в переменной data
. и затем я изменяю сумму соответствующего поля.