From f31beec0c42aeb3df2a0381db4fa3a0ee41dba45 Mon Sep 17 00:00:00 2001 From: Johannes Hoppe Date: Sat, 30 Jun 2018 14:56:32 +0200 Subject: [PATCH] Add support for custom to_field names for foreign keys (#486) Django's ForeignKey supports custom to_fields. The to field is the primary key by default, but can be modified. The to field is also used by forms to reduce database lookups. This patch add support for custom to_field names on both model or form layer. --- django_select2/forms.py | 11 ++++++----- tests/test_forms.py | 8 +++++++- tests/testapp/forms.py | 9 +++++++++ tests/testapp/models.py | 6 +++++- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/django_select2/forms.py b/django_select2/forms.py index 88f04db..dbac21b 100644 --- a/django_select2/forms.py +++ b/django_select2/forms.py @@ -444,11 +444,12 @@ class ModelSelect2Mixin(object): c for c in selected_choices if c not in self.choices.field.empty_values } - choices = ( - (obj.pk, self.label_from_instance(obj)) - for obj in self.choices.queryset.filter(pk__in=selected_choices) - ) - for option_value, option_label in choices: + field_name = self.choices.field.to_field_name or 'pk' + query = Q(**{'%s__in' % field_name: selected_choices}) + for obj in self.choices.queryset.filter(query): + option_value = self.choices.choice(obj)[0] + option_label = self.label_from_instance(obj) + selected = ( str(option_value) in value and (has_selected is False or self.allow_multiple_selected) diff --git a/tests/test_forms.py b/tests/test_forms.py index 15a48db..9010862 100644 --- a/tests/test_forms.py +++ b/tests/test_forms.py @@ -22,7 +22,7 @@ from tests.testapp import forms from tests.testapp.forms import ( NUMBER_CHOICES, HeavySelect2MultipleWidgetForm, TitleModelSelect2Widget ) -from tests.testapp.models import City, Country, Genre +from tests.testapp.models import Artist, City, Country, Genre, Groupie try: from django.urls import reverse @@ -362,6 +362,12 @@ class TestModelSelect2Mixin(TestHeavySelect2Mixin): widget = ModelSelect2Widget(queryset=Genre.objects.all(), search_fields=['title__icontains']) assert isinstance(widget.get_url(), text_type) + def test_custom_to_field_name(self): + the_best_band_in_the_world = Artist.objects.create(title='Take That') + groupie = Groupie.objects.create(obsession=the_best_band_in_the_world) + form = forms.GroupieForm(instance=groupie) + assert '' in form.as_p() + class TestHeavySelect2TagWidget(TestHeavySelect2Mixin): diff --git a/tests/testapp/forms.py b/tests/testapp/forms.py index bffb301..cacfb75 100644 --- a/tests/testapp/forms.py +++ b/tests/testapp/forms.py @@ -195,3 +195,12 @@ class AddressChainedSelect2WidgetForm(forms.Form): max_results=500, ) ) + + +class GroupieForm(forms.ModelForm): + class Meta: + model = models.Groupie + fields = '__all__' + widgets = { + 'obsession': ArtistCustomTitleWidget + } diff --git a/tests/testapp/models.py b/tests/testapp/models.py index 6687550..c324852 100644 --- a/tests/testapp/models.py +++ b/tests/testapp/models.py @@ -12,7 +12,7 @@ class Genre(models.Model): class Artist(models.Model): - title = models.CharField(max_length=50) + title = models.CharField(max_length=50, unique=True) genres = models.ManyToManyField(Genre) class Meta: @@ -56,3 +56,7 @@ class City(models.Model): def __str__(self): return self.name + + +class Groupie(models.Model): + obsession = models.ForeignKey(Artist, to_field='title', on_delete=models.CASCADE)