-
Notifications
You must be signed in to change notification settings - Fork 468
Description
I'm working with a large FormWizard, and the way that users will interact with the data after form submission is such that Foreign Key fields in the model would be a problem. So, I'm using the plain Select2, not ModelSelect2.
The Select2 works! You can submit the form, and the selection comes through just fine. But, if you go to edit, the initial values populate for everything but the example Select2 field. It's not just a display issue, if you submit the edit, the value is blanked out in the database.
My question, is this supported and not working as a bug, or implementation failure on my part? Or is this not supported? If not, how might this be accomplished? I've tried setting the initial in the choice field manually, but that didn't appear to be the answer like in other issues I saw while searching my problem.
-- EDIT START --
I solved my problem, it's not supported, using ModelSelect2 is specifically what's required to get the information needed to set the initial values. I used a combination of seemingly hacky nonsense to get it to work without using a foreign key. Is there a more proper way to do this? If so, would that be a good addition to the documentation?
hacky forms.py snippet
class CustomModelChoiceField(forms.ModelChoiceField):
def to_python(self, value):
if value in self.empty_values:
return None
try:
key = self.to_field_name or 'pk'
value = self.queryset.get(**{key: value})
except (ValueError, TypeError, self.queryset.model.DoesNotExist):
raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice')
return value.name
class Form1(autocomplete.FutureModelForm):
example = CustomModelChoiceField(widget=CustomSelect2(url='autocomplete-example'),
queryset=ExampleChoices.objects.all(),
to_field_name='name')
hacky widgets.py snippet (just the filter_choices_to_render)
class Select2BootstrapWidgetMixin(object):
class Media:
extend = True
css = {'all': ('css/select2-bootstrap4.css',)}
def build_attrs(self, *args, **kwargs):
attrs = super(Select2BootstrapWidgetMixin, self).build_attrs(*args, **kwargs)
attrs.setdefault('data-theme',
'bootstrap4')
return attrs
def filter_choices_to_render(self, selected_choices):
"""Filter out un-selected choices if choices is a QuerySet."""
self.choices.queryset = self.choices.queryset.filter(
choice__in=[c for c in selected_choices if c]
-- EDIT END --
models.py snippet
class Example(models.Model):
example = models.CharField(max_length=500, blank=True, null=True)
forms.py snippet (CustomSelect2 is just Select2 with media for bootstrap styling)
class Form1(autocomplete.FutureModelForm):
example = forms.ChoiceField(widget=CustomSelect2(url='autocomplete-site'))
class Meta:
model = Example
forms.py alternate snippet (This is the failed setting initial attempt)
class Form1(autocomplete.FutureModelForm):
def __init__(self, *args, **kwargs):
super(Form1, self).__init__(*args, **kwargs)
example = self.initial.get('example', None)
if example:
self.fields['example'] = forms.CharField(widget=CustomSelect2(url='autocomplete-site', attrs={'data-placeholder': f'Un-edited Value: {example}'}), initial=example)
views.py snippet
class ExampleAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
qs = Select2Choices.objects.filter(field='example').only('name').order_by('name')
if self.q:
qs = qs.filter(choice__istartswith=self.q)
return qs
def get_result_value(self, result):
"""Return the value of a result."""
return str(result.name)