django-select2 icon indicating copy to clipboard operation
django-select2 copied to clipboard

Dependent field does not work in formset

Open rez0n opened this issue 4 years ago • 3 comments

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?

rez0n avatar Apr 17 '22 01:04 rez0n

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!

codingjoe avatar Apr 25 '22 17:04 codingjoe

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 avatar Feb 09 '24 00:02 sgeorgiev1995

@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 %}

asqarsakenov avatar Feb 27 '24 09:02 asqarsakenov