Dependent field does not work in formset
Describe the bug
Hi, dependent fields functionality does not work in the formset.
I debugged this and found an issue in django_select2.js file, I added the description as a comment.
var dependentFields = $element.data('select2-dependent-fields')
if (dependentFields) {
dependentFields = dependentFields.trim().split(/\s+/)
$.each(dependentFields, function (i, dependentField) {
// dependentField variable contains field name "fieldname"
// but actually this field have name: form_prefix-0-fieldname
result[dependentField] = $('[name=' + dependentField + ']', $element.closest('form')).val()
})
}
I fixed this on my side by overriding this part by dirty hack
result[dependentField] = $('[name$=' + dependentField + ']', $element.closest('.card-body')).val()
name$= - $ symbol means wildcard selector $element.closest('.card-body') - because my formset forms framed by div with class card-body
Actually I have no good idea how to solve this issue in a good way, maybe better to use some randomly generated data attribute on the dependent field to pick its value or so?
Hi @rez0n, thanks for you the detailed description. Since you are already so invested, maybe you want to also provide a patch? I am currently very busy on other projects and would probably take me a little while until I get time to fix this. Best Joe!
Overriding the form field and the widget worked for me. It's not the ideal solution, but it seems better than modifying django_select2.js, at least in my case.
for form in formset:
form_prefix = form.prefix
form.fields['model'] = forms.ModelChoiceField(
queryset=MyModel.objects.all(),
widget=MyModelSearchableSelect(
dependent_fields={f'{form_prefix}-dependent-field': 'dependent-field'}
))
@sgeorgiev1995 could you please support on inline formsets? In my case, I add the form to the inline formset. And I receive errors 404.
# view.py
def invoice_line_create_view(request, pk):
InvoiceLineFormSet = inlineformset_factory(Invoice2, InvoiceLine2, form=LineForm, extra=10)
invoice = get_object_or_404(Invoice2, pk=pk)
formset = InvoiceLineFormSet(instance=invoice)
if request.method == 'POST':
# print(f"{get_cache_key(request)} kod")
formset = InvoiceLineFormSet(request.POST, instance=invoice)
if formset.is_valid():
formset.save()
# Инвалидация кэша после сохранения формы
# cache.delete(get_cache_key(request))
return redirect('invoice_detail', pk=invoice.pk)
context = {
'invoice': invoice,
'formset': formset,
}
return render(request, './invoice/invoice_line_new.html', context)
# froms.py
class LineForm(forms.ModelForm):
class Meta:
model = InvoiceLine2
# fields = ["name", "type_field"]
fields = "__all__"
name = forms.ModelChoiceField(
queryset=Name2.objects.all(),
label="Наименование",
widget=s2forms.ModelSelect2Widget(
model=Name2,
search_fields=['name__icontains'],
attrs={
"data-minimum-input-length": 0,
"data-placeholder": "Выберите Наименование",
"data-close-on-select": "true",
"data-allow-clear" : "true",
},
# dependent_fields={'type_field': 'types'},
)
)
type_field = forms.ModelChoiceField(
queryset=Price2.objects.all(),
label="Тип",
widget=s2forms.ModelSelect2Widget(
model=Price2,
search_fields=['type_field__icontains'],
attrs={
"data-minimum-input-length": 0,
"data-placeholder": "Выберите Тип",
"data-close-on-select": "true",
"data-allow-clear" : "true",
},
dependent_fields={'name': 'name'},
max_results=500,
)
)
<!-- template.html -->
{% extends 'base.html' %}
{% block script %}
{% comment %} Фильтры по ключевикам {% endcomment %}
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
{% comment %} Select2 {% endcomment %}
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/select2.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.min.js"></script>
{% endblock script %}
{% block content %}
{% if user.is_authenticated %}
{% load crispy_forms_tags %}
{% comment %} <p>Введите ключевое слово (ввод не зависит от регистра, но слова должны быть корректно написаны).</p>
<p>Одновременно можно фильтровать по нескольким критериям (до 5)</p> {% endcomment %}
{% comment %} <input class="form-control" id="myInput1" type="text" placeholder="Поиск 1">
<input class="form-control" id="myInput2" type="text" placeholder="Поиск 2">
<input class="form-control" id="myInput3" type="text" placeholder="Поиск 3">
<input class="form-control" id="myInput4" type="text" placeholder="Поиск 4">
<input class="form-control" id="myInput5" type="text" placeholder="Поиск 5"> {% endcomment %}
<h2>Изменение содержимого КП №{{ invoice.invoice_number }} для {{ invoice.customer }} от {{ invoice.date_issued }}</h2>
<form method="post" class="form-horizontal" enctype="multipart/form-data">
{% csrf_token %}
{{ formset.management_form }}
<table class="table">
<thead>
<tr>
<th scope="col">№</th>
<th scope="col">Тип</th>
<th scope="col">Тип цены</th>
<th scope="col">Количество</th>
<th scope="col">Удалить</th>
</tr>
</thead>
<tbody>
{% for form in formset %}
{{ form.id }}
<tr>
<th scope="row">{{ forloop.counter }}</th>
<td>{{ form.type_field }}</td>
<td>{{ form.price_type }}</td>
<td>
<div class="form-outline">
{{ form.quantity }}
</div>
</td>
<td>{{ form.DELETE }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<input type="submit" class="btn btn-primary" value="Сохранить">
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
{{ form.media.js }}
{% else %}
<p>Вы не вошли в ваш аккаунт. Функционал доступен только для зарегистрированных пользователей.</p>
<a href="{% url "login" %}">Войти</a>
{% comment %} <a href="{% url "signup" %}">Регистрация</a> {% endcomment %}
{% endif %}
{% endblock %}