Skip to content

Commit f31beec

Browse files
authored
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.
1 parent 496cc7c commit f31beec

File tree

4 files changed

+27
-7
lines changed

4 files changed

+27
-7
lines changed

django_select2/forms.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -444,11 +444,12 @@ def optgroups(self, name, value, attrs=None):
444444
c for c in selected_choices
445445
if c not in self.choices.field.empty_values
446446
}
447-
choices = (
448-
(obj.pk, self.label_from_instance(obj))
449-
for obj in self.choices.queryset.filter(pk__in=selected_choices)
450-
)
451-
for option_value, option_label in choices:
447+
field_name = self.choices.field.to_field_name or 'pk'
448+
query = Q(**{'%s__in' % field_name: selected_choices})
449+
for obj in self.choices.queryset.filter(query):
450+
option_value = self.choices.choice(obj)[0]
451+
option_label = self.label_from_instance(obj)
452+
452453
selected = (
453454
str(option_value) in value and
454455
(has_selected is False or self.allow_multiple_selected)

tests/test_forms.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from tests.testapp.forms import (
2323
NUMBER_CHOICES, HeavySelect2MultipleWidgetForm, TitleModelSelect2Widget
2424
)
25-
from tests.testapp.models import City, Country, Genre
25+
from tests.testapp.models import Artist, City, Country, Genre, Groupie
2626

2727
try:
2828
from django.urls import reverse
@@ -362,6 +362,12 @@ def test_get_url(self):
362362
widget = ModelSelect2Widget(queryset=Genre.objects.all(), search_fields=['title__icontains'])
363363
assert isinstance(widget.get_url(), text_type)
364364

365+
def test_custom_to_field_name(self):
366+
the_best_band_in_the_world = Artist.objects.create(title='Take That')
367+
groupie = Groupie.objects.create(obsession=the_best_band_in_the_world)
368+
form = forms.GroupieForm(instance=groupie)
369+
assert '<option value="Take That" selected>TAKE THAT</option>' in form.as_p()
370+
365371

366372
class TestHeavySelect2TagWidget(TestHeavySelect2Mixin):
367373

tests/testapp/forms.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,12 @@ class AddressChainedSelect2WidgetForm(forms.Form):
195195
max_results=500,
196196
)
197197
)
198+
199+
200+
class GroupieForm(forms.ModelForm):
201+
class Meta:
202+
model = models.Groupie
203+
fields = '__all__'
204+
widgets = {
205+
'obsession': ArtistCustomTitleWidget
206+
}

tests/testapp/models.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def __str__(self):
1212

1313

1414
class Artist(models.Model):
15-
title = models.CharField(max_length=50)
15+
title = models.CharField(max_length=50, unique=True)
1616
genres = models.ManyToManyField(Genre)
1717

1818
class Meta:
@@ -56,3 +56,7 @@ class Meta:
5656

5757
def __str__(self):
5858
return self.name
59+
60+
61+
class Groupie(models.Model):
62+
obsession = models.ForeignKey(Artist, to_field='title', on_delete=models.CASCADE)

0 commit comments

Comments
 (0)