yourlabs / django-autocomplete-light

A fresh approach to autocomplete implementations, specially for Django. Status: v4 alpha, v3 stable, v2 & v1 deprecated.
https://django-autocomplete-light.readthedocs.io
MIT License
1.79k stars 466 forks source link

DAL generates multiple selectboxes for declarative field #1340

Open ColeDCrawford opened 8 months ago

ColeDCrawford commented 8 months ago

Crossposting from StackOverflow, didn't get any traction there and coming back to this problem. https://stackoverflow.com/questions/77001904/django-autocomplete-light-select2-generates-multiple-selectboxes-for-declarative

I was expecting to see a single functioning widget, but instead have two partially functioning ones.

I have a Work and a Parent Work; each Work can have 0 or 1 ParentWork.

models.py

class ParentWork(UUIDModel):
  url_name = "parent_work_detail"

class Work(models.Model):
  parent_work = models.ForeignKey(ParentWork, models.SET_NULL, null=True, blank=True)
  ... other fields

I am using DAL for forms: forms.py

class ParentWorkForm(forms.ModelForm):
    related_works = forms.ModelMultipleChoiceField(
        label="Related Works",
        queryset=Work.objects.all().order_by("title"),
        required=False,
        widget=autocomplete.ModelSelect2Multiple(url="autocomplete_work"),
    )

    class Meta:
        model = ParentWork
        exclude = []
        required = ["name"]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # for the Parent Works update form, set the initial value of the related_works field
        try:
            instance = kwargs.get("instance")
            if instance:
                self.fields["related_works"].initial = instance.work_set.all()
        except Exception as e:
            print(e)

I have to declare related_works on the ModelForm as it is stored on the Work, not ParentWork.

Relevant snippet from parent_work_form.html template:

{% block content %}

  <div class="admin-container">
    <form method="post" action="." class="mt-4" enctype="multipart/form-data">
      {% csrf_token %}
      {{ form.as_p }}

      <div class="mt-10 w-full flex justify-between">
        {% if object %}
          <a href="{% url "parent_works_delete" object.uuid %}" class="text-gray-400 my-auto">Delete</a>
        {% else %}
          <span></span>
        {% endif %}
        <span>
          <a href="{% url "parent_works_list" %}" class="cancel">Cancel</a>
          <button>Save</button>
        </span>
      </div>
    </div>
  </form>
{% endblock %}

{% block extra_scripts %}
  <!-- parent form -->
  {% load static %}
  <script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
  {{ form.media }}
{% endblock %}

Other forms inheriting from Meta.widgets on ModelForms are fine, but this declared field is generating two widgets rather than one.

Two widgets for "Related Works": Two widgets for Related Works

The top box seems to function correctly in terms of search: top box with functioning autocomplete

But the bottom box only shows options that have already been selected in the top box. bottom box - no results bottom box - only options from top box selections

On the flip side, if I submit my form with selected options in the top box but not the bottom, nothing is saved. Rendered HTML screenshot

jpic commented 6 months ago

Interesting, did you find any solution?