[Solved]-Free-form input for ForeignKey Field on a Django ModelForm

14👍

Something like this should work:

class TestForm(ModelForm):
  attribution = forms.CharField(max_length=100)

  def save(self, commit=True):
      attribution_name = self.cleaned_data['attribution']
      attribution = TestSource.objects.get_or_create(name=attribution_name)[0]  # returns (instance, <created?-boolean>)
      self.instance.attribution = attribution

      return super(TestForm, self).save(commit)

  class Meta:
    model=TestModel
    exclude = ('attribution')

1👍

There are a few problems here.

Firstly, you have defined a field, not a widget, so you can’t use it in the widgets dictionary. You’ll need to override the field declaration at the top level of the form.

Secondly get_or_create returns two values: the object retrieved or created, and a boolean to show whether or not it was created. You really just want to return the first of those values from your to_python method.

I’m not sure if either of those caused your actual error though. You need to post the actual traceback for us to be sure.

0👍

TestForm.attribution expects int value – key to TestSource model.

Maybe this version of the model will be more convenient for you:

class TestSource(models.Model):
    name = models.CharField(max_length=100, primary_key=True)

0👍

Taken from:

How to make a modelform editable foreign key field in a django template?

class CompanyForm(forms.ModelForm):
s_address = forms.CharField(label='Address', max_length=500, required=False)

def __init__(self, *args, **kwargs):
    super(CompanyForm, self).__init__(*args, **kwargs)
    try:
        self.fields['s_address'].initial = self.instance.address.address1
    except ObjectDoesNotExist:
        self.fields['s_address'].initial = 'looks like no instance was passed in'

def save(self, commit=True):
    model = super(CompanyForm, self).save(commit=False)
    saddr = self.cleaned_data['s_address']
    if saddr:
        if model.address:
            model.address.address1 = saddr
            model.address.save()
        else:
            model.address = Address.objects.create(address1=saddr)
            # or you can try to look for appropriate address in Address table first
            # try:
            #     model.address = Address.objects.get(address1=saddr)
            # except Address.DoesNotExist:
            #     model.address = Address.objects.create(address1=saddr)

    if commit:
        model.save()

    return model

class Meta:
    exclude = ('address',) # exclude form own address field

This version sets the initial data of the s_address field as the FK from self, during init , that way, if you pass an instance to the form it will load the FK in your char-field – I added a try and except to avoid an ObjectDoesNotExist error so that it worked with or without data being passed to the form.

Although, I would love to know if there is a simpler built in Django override.

Leave a comment