After many hours of rearch, I have found a solution.
I tried many things, including overriding the clean() function on my form, however that required removing immutability and was messy to get the validation right.
Ultimately my solution was so sublcasss ModelChoiceField. In the model choice field, override the to_python() method with your logic to create related object if it does not exist. In addition, I passed this field a queryset paramater so that in my form I was able to pass the newly created object only to this form instance, but not show on every users form.
class FlexibleModelChoiceField(ModelChoiceField):
def __init__(self, queryset, *args, **kwargs):
super(FlexibleModelChoiceField, self).__init__(queryset, *args, **kwargs)
self.queryset = queryset
def to_python(self, value):
try:
# Logic to get or create the model instance object
return model_instance_object
except (ValueError, self.queryset.model.DoesNotExist):
raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice')