After searching for several days and trying different options, I decided to finally post the issue and question.
I have a template that has a form and 2 different formsets.
One of the formsets uses an intermediate model with a GenericForeignKey that will reference two other models. For the formset, I am using an inlineformset and adding a CharField which is used with Select2 to make an ajax call to check the two other models. The value returned by the ajax call will be a json/dict with 3 key/value pairs.
The issue I am having is that when the template is submitted and there are errors, how can I redisplay the value that was entered in the Select2 CharField when the template is presented again?
The value is in self.data and is sent back to the template. However, everything I've tried so far will not redisplay the select2 field with the value selected previously or the values that were submitted.
The submitted values are returned to the template in a json/dict, key/value, format under form.fieldname.value but I am not sure how I can use that to repopulate the select2 field.
I appreciate any suggestions or links. If there is an alternate way to set this up, I am interested to hear.
Thank you.
UPDATE: 2021-03-18
Here is, hopefully all, the relevant bits from the various files.
models.py
class SiteDomain(models.Model):
website = models.ForeignKey(
WebSite,
on_delete=models.CASCADE,
)
domain_model = models.ForeignKey(
ContentType,
on_delete=models.CASCADE,
help_text=(
"The model that the website entry is related to. eg: Domain or SubDomain"
),
)
object_id = models.PositiveIntegerField(
help_text="The ID of the model object the entry is related to."
)
content_object = GenericForeignKey("domain_model", "object_id")
content_object.short_description = "Domain Name:"
views.py
class AddWebsite(View):
def get(self, request, *args, **kwargs):
domain_formset = inlineformset_factory(
WebSite,
SiteDomain,
formset=SiteDomainInlineFormSet,
fields=(),
extra=3,
)
forms.py
class SiteDomainInlineFormSet(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
self.account = kwargs.pop('account', None)
super(SiteDomainInlineFormSet, self).__init__(*args, **kwargs)
def add_fields(self, form, index):
super().add_fields(form, index)
form.fields["domain_name"] = forms.CharField(
max_length=255,
widget=forms.Select(),
required=False,
)
template
<script type="text/javascript">
function s2search() {
$('.domain-lookup-ajax').select2({
width: 'style',
ajax: {
url: "{% url 'accounts_ajax:website_domain_lookup' %}",
dataType: 'json',
delay: 250,
data: function (params) {
var query = {
term: params.term,
acct_id: '{{ account.id }}',
}
return query;
},
processResults: function (data, params) {
return {
results: data,
};
},
cache: true
},
placeholder: 'Enter at least 2 characters for search.',
minimumInputLength: 2,
});
}
</script>
<form action="" method="post">
{% csrf_token %}
{{ domain_formset.management_form }}
{{ app_formset.management_form }}
{% for form in domain_formset %}
<div class="domainfieldWrapper" id="row_{{ forloop.counter0 }}">
<select id="id_dform-{{ forloop.counter0 }}-domain_name" class="domain_name domain-lookup-ajax" name="dform-{{ forloop.counter0 }}-domain_name"></select>
<button id="id_dform-{{ forloop.counter0 }}-button" class="button" type="button" onclick="clearSelect('id_dform-{{ forloop.counter0 }}-domain_name')">Clear</button>
</div>
{% endfor %}
</form>
The ajax call will return something like:
{"model_id":"74", "domain_id":"177", "name":"alfa.first-example.com"}
A side note: I also tested the select2 field in the second formset and it does not get repopulated either when the template is reloaded if there are any form errors. Which I kind of expected since it basically uses the same setup except for the value returned by the ajax call which is for a normal ModelChoiceField.
Using a combination of https://select2.org/programmatic-control/add-select-clear-items#preselecting-options-in-an-remotely-sourced-ajax-select2, js and Django template tags I was able to get something that works for me.