Does not work with `to_field`
Hi @codingjoe, I faced an issue using to_field option in the model field. Form can't be validated at all.
class ModelA(models.Model):
id = models.AutoField(primary_key=True)
slug = models.CharField(max_length=4, unique=True)
class ModelB(models.Model):
id = models.AutoField(primary_key=True)
a_slug = models.ForeignKey(ModelA, verbose_name=_("Select A"), on_delete=models.CASCADE, to_field='slug')
note = models.CharField(_("Note"), max_length=50)
class ModelAPickWidget(s2forms.ModelSelect2Widget):
model = ModelA
search_fields = [
"slug__icontains",
]
def label_from_instance(self, obj):
return str(f'{obj.slug}')
class ModelBForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# self.fields['a_slug'].widget = ModelAPickWidget()
# self.fields['a_slug'].widget.attrs['class'] = 'form-control'
# self.fields['a_slug'].to_field_name = 'slug'
# self.fields['a_slug'].queryset = ModelA.objects.all()
class Meta:
model = ModelB
fields = ['a_slug', 'note', ]
widgets = {
"a_slug": ModelAPickWidget(attrs={
'class': 'form-control',
}),
"note": widgets.TextInput(attrs={
'class': 'form-control',
}),
}
If I going to disable ModelAPickWidget to use default Django's select widget it works and saves the form.
There is some debugging information.
-
ModelSelect2Mixinin theoptgroupsmethod value is alwayspk -
ModelSelect2Mixinin theoptgroupsline
query = Q(**{"%s__in" % field_name: selected_choices})
print(query)
>>> (AND: ('slug__in', {'196'}))
- I tried to change
query = Q(**{"%s__in" % field_name: selected_choices})
to
query = Q(**{"%s__in" % 'pk': selected_choices})
And I got some success, form get failed validation on first submission but saves if I re-submit it again.
Form validation error raised by Django's ModelChoiceField in the to_python method
Select a valid choice. That choice is not one of the available choices.
because it gets pk value instead slug.
I performed a lot experiments with the optgroups method but have no success, by some reason first form submission get failed validation and validates successfully on the next submit.
A very dirty hack to make it works (nothing related to select2 module, just maybe it will helps to faster understand core of the issue.
class ModelChoiceField2(ChoiceField):
def to_python(self, value):
if value in self.empty_values:
return None
try:
key = self.to_field_name or 'pk'
if isinstance(value, self.queryset.model):
value = getattr(value, key)
value = self.queryset.get(**{'pk': value}) <<<<< HERE changed key to 'pk'
value = value
except (ValueError, TypeError, self.queryset.model.DoesNotExist):
raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice')
return value
def valid_value(self, value):
# Override validation to check is selected value exists in the db
text_value = str(value)
if not self.queryset.model(slug=text_value):
return False
return True
class ModelBForm(ModelForm):
a_slug = ModelChoiceField2()
...
I recently fixed the same issue in Django's autocomplete field. I'd be delighted if you could try to backport this patch to this repo as well. I am pretty overwhelmed with work right now, so I'd appreciate the help.
You can find my Django commit here https://github.com/django/django/commit/3071660acfbdf4b5c59457c8e9dc345d5e8894c5