diff --git a/django_select2/forms.py b/django_select2/forms.py index bf9026a8..02860075 100644 --- a/django_select2/forms.py +++ b/django_select2/forms.py @@ -554,6 +554,28 @@ def label_from_instance(obj): """ return str(obj) + def result_from_instance(self, obj, request): + """ + Return a dictionary representing the object. + + Can be overridden to change the result returned by + :class:`.AutoResponseView` for each object. + + The request passed in will correspond to the request sent to the + :class:`.AutoResponseView` by the widget. + + Example usage:: + + class MyWidget(ModelSelect2Widget): + def result_from_instance(obj, request): + return { + 'id': obj.pk, + 'text': self.label_from_instance(obj), + 'extra_data': obj.extra_data, + } + """ + return {"id": obj.pk, "text": self.label_from_instance(obj)} + class ModelSelect2Widget(ModelSelect2Mixin, HeavySelect2Widget): """ diff --git a/django_select2/views.py b/django_select2/views.py index 64bff43e..f78f19ea 100644 --- a/django_select2/views.py +++ b/django_select2/views.py @@ -21,6 +21,9 @@ def get(self, request, *args, **kwargs): """ Return a :class:`.django.http.JsonResponse`. + Each result will be rendered by the widget's + :func:`django_select2.forms.ModelSelect2Mixin.result_from_instance` method. + Example:: { @@ -41,7 +44,7 @@ def get(self, request, *args, **kwargs): return JsonResponse( { "results": [ - {"text": self.widget.label_from_instance(obj), "id": obj.pk} + self.widget.result_from_instance(obj, request) for obj in context["object_list"] ], "more": context["page_obj"].has_next(), diff --git a/tests/test_forms.py b/tests/test_forms.py index c7281949..ddb0f328 100644 --- a/tests/test_forms.py +++ b/tests/test_forms.py @@ -438,6 +438,15 @@ def test_get_queryset(self): widget.queryset = Genre.objects.all() assert isinstance(widget.get_queryset(), QuerySet) + def test_result_from_instance_ModelSelect2Widget(self, genres): + widget = ModelSelect2Widget() + widget.model = Genre + genre = Genre.objects.first() + assert widget.result_from_instance(genre, request=None) == { + "id": genre.pk, + "text": str(genre), + } + def test_tag_attrs_Select2Widget(self): widget = Select2Widget() output = widget.render("name", "value") diff --git a/tests/test_views.py b/tests/test_views.py index e63ffe80..745dbfad 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -4,7 +4,11 @@ from django_select2.cache import cache from django_select2.forms import ModelSelect2Widget -from tests.testapp.forms import AlbumModelSelect2WidgetForm, ArtistCustomTitleWidget +from tests.testapp.forms import ( + AlbumModelSelect2WidgetForm, + ArtistCustomTitleWidget, + CityForm, +) from tests.testapp.models import Genre try: @@ -84,6 +88,23 @@ def test_label_from_instance(self, artists, client): "results" ] + def test_result_from_instance(self, cities, client): + url = reverse("django_select2:auto-json") + + form = CityForm() + assert form.as_p() + field_id = form.fields["city"].widget.field_id + city = cities[0] + response = client.get(url, {"field_id": field_id, "term": city.name}) + assert response.status_code == 200 + data = json.loads(response.content.decode("utf-8")) + assert data["results"] + assert { + "id": city.pk, + "text": smart_str(city), + "country": smart_str(city.country), + } in data["results"] + def test_url_check(self, client, artists): artist = artists[0] form = AlbumModelSelect2WidgetForm() diff --git a/tests/testapp/forms.py b/tests/testapp/forms.py index ed90f222..0b115339 100644 --- a/tests/testapp/forms.py +++ b/tests/testapp/forms.py @@ -232,3 +232,17 @@ class Meta: model = models.Groupie fields = "__all__" widgets = {"obsession": ArtistCustomTitleWidget} + + +class CityModelSelect2Widget(ModelSelect2Widget): + model = City + search_fields = ["name"] + + def result_from_instance(self, obj, request): + return {"id": obj.pk, "text": obj.name, "country": str(obj.country)} + + +class CityForm(forms.Form): + city = forms.ModelChoiceField( + queryset=City.objects.all(), widget=CityModelSelect2Widget(), required=False + )