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.
This commit is contained in:
Johannes Hoppe 2018-06-30 14:56:32 +02:00 committed by GitHub
parent 496cc7c502
commit f31beec0c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 27 additions and 7 deletions

View file

@ -444,11 +444,12 @@ class ModelSelect2Mixin(object):
c for c in selected_choices c for c in selected_choices
if c not in self.choices.field.empty_values if c not in self.choices.field.empty_values
} }
choices = ( field_name = self.choices.field.to_field_name or 'pk'
(obj.pk, self.label_from_instance(obj)) query = Q(**{'%s__in' % field_name: selected_choices})
for obj in self.choices.queryset.filter(pk__in=selected_choices) for obj in self.choices.queryset.filter(query):
) option_value = self.choices.choice(obj)[0]
for option_value, option_label in choices: option_label = self.label_from_instance(obj)
selected = ( selected = (
str(option_value) in value and str(option_value) in value and
(has_selected is False or self.allow_multiple_selected) (has_selected is False or self.allow_multiple_selected)

View file

@ -22,7 +22,7 @@ from tests.testapp import forms
from tests.testapp.forms import ( from tests.testapp.forms import (
NUMBER_CHOICES, HeavySelect2MultipleWidgetForm, TitleModelSelect2Widget NUMBER_CHOICES, HeavySelect2MultipleWidgetForm, TitleModelSelect2Widget
) )
from tests.testapp.models import City, Country, Genre from tests.testapp.models import Artist, City, Country, Genre, Groupie
try: try:
from django.urls import reverse from django.urls import reverse
@ -362,6 +362,12 @@ class TestModelSelect2Mixin(TestHeavySelect2Mixin):
widget = ModelSelect2Widget(queryset=Genre.objects.all(), search_fields=['title__icontains']) widget = ModelSelect2Widget(queryset=Genre.objects.all(), search_fields=['title__icontains'])
assert isinstance(widget.get_url(), text_type) 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 '<option value="Take That" selected>TAKE THAT</option>' in form.as_p()
class TestHeavySelect2TagWidget(TestHeavySelect2Mixin): class TestHeavySelect2TagWidget(TestHeavySelect2Mixin):

View file

@ -195,3 +195,12 @@ class AddressChainedSelect2WidgetForm(forms.Form):
max_results=500, max_results=500,
) )
) )
class GroupieForm(forms.ModelForm):
class Meta:
model = models.Groupie
fields = '__all__'
widgets = {
'obsession': ArtistCustomTitleWidget
}

View file

@ -12,7 +12,7 @@ class Genre(models.Model):
class Artist(models.Model): class Artist(models.Model):
title = models.CharField(max_length=50) title = models.CharField(max_length=50, unique=True)
genres = models.ManyToManyField(Genre) genres = models.ManyToManyField(Genre)
class Meta: class Meta:
@ -56,3 +56,7 @@ class City(models.Model):
def __str__(self): def __str__(self):
return self.name return self.name
class Groupie(models.Model):
obsession = models.ForeignKey(Artist, to_field='title', on_delete=models.CASCADE)