django-autocomplete-light icon indicating copy to clipboard operation
django-autocomplete-light copied to clipboard

Select2 preload bug while rendering after form error

Open jihoon796 opened this issue 6 years ago • 9 comments

Due to the fact that I can't change the default representation of the object (TestTarget), I've overloaded both the get_result_label and the get_selected_result_label methods on the autocomplete view function to use the full_name property on the object:

class TestTargetAutocomplete(autocomplete.Select2QuerySetView):
    def get_queryset(self):
        qs = models.TestTarget.objects.filter(active=True).all()

        if self.q:
            qs = qs.filter(name__icontains=self.q)
        return qs

    def get_result_label(self, item):
        return item.full_name

    def get_selected_result_label(self, item):
        return item.full_name
class TestTarget(models.Model):
    uuid = models.UUIDField(
        db_index=True,
        default=uuid_lib.uuid4,
        editable=False
    )
    name = models.CharField(max_length=255, null=True)
    active = models.BooleanField(default=True)

    class Meta:
        db_table = 'test_target'

    @property
    def full_name(self):
        return self.name

    def __str__(self):
        return "<TestTarget {}>".format(self.name)

If there is a form error upon submission, one would expect the get_result_label or the get_selected_result_label method to be called during the re-rendering of the form. However, it seems to be default to calling the __str__ method on the object as it re-renders.


Before submission:

image image

After submitting with a form error: image image

jihoon796 avatar Aug 31 '18 22:08 jihoon796

This is because initial rendering is done by the widget, not by the view !

jpic avatar Sep 07 '18 03:09 jpic

@jpic what is the appropriate solution to this issue then?

jihoon796 avatar Sep 07 '18 16:09 jihoon796

Same question, what is the appropriate solution to this issue ? How to manage the rendering of the initial value ? I'd like to have the same rendering as def get_result_label(self, item): that I have done. Especially in the admin view

More details : I defined in admin.py:

class TestChangeFormBase(...): 
...
  class Meta: 
    model = TestChangeFormBase
	widgets = some_widgets
	...
some_widgets = {
...
specific_user : SpecificUserAutocompleteWidget
...
}
SpecificUserAutocompleteWidget = autocomplete.ModelSelect2(
    url = 'specificuser-autocomplete',
    attrs = {
        'data-placeholder': _('SpecificUser?'),
        'data-minimum-input-length': 1,
        'data-html': True,
        'data-container-css-class': 'height-auto'
    }
)

in views.py

class SpecificUserAutocompleteView(FilteredAutocomplete):
    queryset = SpecificUser.objects.all()
    serializer_class = SpecificUserSerializer
    filter_backends = (SearchFilter,)
    search_fields = ('first_name', 'last_name')
    filters = {'is_specific': True, 'deleted__isnull': True}

    def get_result_label(self, item):
        context = {
            'choice': item,
        }
        return render_to_string('people/person_choice.html', context)

    def get_selected_result_label(self, result):
        context = {
            'choice': result,
        }
        return render_to_string('people/person_choice.html', context)
```	
This is working fine when selecting a user in the select box. But when I load the object, the select box contains the `__str__` value of the model of SpecificUser

Before I was using autocomplete 2.3.6 but I did the required refactoring to upgrade to 3.3.2 and it is not working as before.

I tried in `__init__` of my form to do : 
```python
        if 'specific_user' in self.fields:
            self.fields["specific_user"].label_from_instance =  self.label_from_instance

    @mark_safe
    def label_from_instance(self, obj):
        return format_html('<a href="{0}">{1}</a>',str(obj), str(obj)+" TEST "+str(obj.mobile_phone_number))
```	
But then it displays only text without html. Also It will be better to use my template `'people/person_choice.html'`.

pulse-mind avatar Dec 16 '18 17:12 pulse-mind

This is because v2 was using a metaclass that was a pain in the ass to maintain across django versions and there were not enough contributions to maintain it (i had to do it all by myself for free and I couldn't). Couldn't maintain the custom JS either. Hope to have the chance to give it another try one day when I find sponsor. Otherwise, you can participate to #1067

You can still make your rendering function and use it both in the widget rendering and the view rendering methods, but it's up to you.

Your last paste is not complete, are you defining label_from_instance in the form class ? How is that supposed to work ? Does it work when you define that method in the form field class ?

jpic avatar Dec 17 '18 09:12 jpic

Hello @jpic and thank you for the job done!! I understand that you did not want to maintain the old version this is why I decided to upgrade to the last version to avoid the "pain in the ass" ;).

==== Yes I defined label_from_instance in the form class. I took the idea from #964. Also I've seen that #607 is suggesting to override

class UserModelChoiceField(forms.ModelChoiceField):
    def label_from_instance(self, obj):
        return obj.get_full_name().title()

====

How is that supposed to work ? Does it work when you define that method in the form field class ?

It works but without HTML code, the results contains only text which is not what I want :/.

==== My template people/person_choice contains :

{% load i18n l10n %}
{% load static %}

<div class="block person" data-value="{{ choice.pk }}">  <strong>{{ choice.full_name }} </strong>
  <p style="padding: 2px 10px 2px 10px !important">
  {% if choice.phone_numbers %}
    {{ choice.phone_numbers|join:'/' }}
    <br/>
  {% endif %}
  {% if choice.work_email_address %}
    <a href="mailto:{{ choice.work_email_address }}">{{ choice.work_email_address }}</a>
  {% elif choice.home_email_address %}
    <a href="mailto:{{ choice.home_email_address }}">{{ choice.home_email_address }}</a>
  {% endif %}
  </p>
</div>

And is call this in the SpecificUserAutocompleteView (already copied in my previous message)

====

You can still make your rendering function and use it both in the widget rendering and the view rendering methods, but it's up to you.

Can you explain with more details what you mean, an example would be wonderful!

pulse-mind avatar Dec 17 '18 09:12 pulse-mind

All right, we can't have HTML in options so that's too bad for custom rendering.

However, you can deal with it in js, by putting your own js starter.

I really want to make a javascript-autocomplete-light package, based on jquery-autocomplete-light that we had, but it's far off my priorities for now, unfortunately. I think I had a kickstarter campaign ready to start but didn't proceed.

jpic avatar Dec 17 '18 12:12 jpic

Ok thank you for your answers. What is your kickstarter campaign ?

pulse-mind avatar Dec 17 '18 13:12 pulse-mind

I had the idea that if people are interested in seeing a port of jquery-autocomplete-light into modern javascript (with unit tests instead of selenium tests that would make it more maintainable), they would pay for it. But I don't really believe in that anymore, so, the rewrite is stuck at the bottom of my todo list until a customer asks for an autocomplete (this has surprisingly not happened for a while).

jpic avatar Dec 17 '18 13:12 jpic

Agreed, PR welcome

Le ven. 3 janv. 2020 à 00:26, pySilver [email protected] a écrit :

Solution is in the docs: https://select2.org/programmatic-control/add-select-clear-items#preselecting-options-in-an-remotely-sourced-ajax-select2

It would be nice to have generic view for this case.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/yourlabs/django-autocomplete-light/issues/1034?email_source=notifications&email_token=AAAXDLHGXMTP6LROC6WU2NTQ3ZZ2BA5CNFSM4FSXZWAKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEH7ZCQI#issuecomment-570396993, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAXDLENWWCYKQKYROQYZQTQ3ZZ2BANCNFSM4FSXZWAA .

jpic avatar Jan 03 '20 17:01 jpic