Skip to content

Commit

Permalink
Add ModelSelect2Mixin.result_from_instance method (#272)
Browse files Browse the repository at this point in the history
Enable users to easily override the result JSON per widget without implementing a separate view.

---------

Co-authored-by: Johannes Maron <johannes@maron.family>
  • Loading branch information
sdolemelipone and codingjoe committed Jun 29, 2024
1 parent 06a437d commit 07ea4b8
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 2 deletions.
22 changes: 22 additions & 0 deletions django_select2/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""
Expand Down
5 changes: 4 additions & 1 deletion django_select2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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::
{
Expand All @@ -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(),
Expand Down
9 changes: 9 additions & 0 deletions tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
23 changes: 22 additions & 1 deletion tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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()
Expand Down
14 changes: 14 additions & 0 deletions tests/testapp/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

0 comments on commit 07ea4b8

Please sign in to comment.