Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Free-form input for ForeignKey Field on a Django ModelForm

I have two models related by a foreign key:

# models.py    
class TestSource(models.Model):
  name        = models.CharField(max_length=100)

class TestModel(models.Model):
  name        = models.CharField(max_length=100)
  attribution = models.ForeignKey(TestSource, null=True)

By default, a django ModelForm will present this as a <select> with <option>s; however I would prefer that this function as a free form input, <input type="text"/>, and behind the scenes get or create the necessary TestSource object and then relate it to the TestModel object.

I have tried to define a custom ModelForm and Field to accomplish this:

# forms.py
class TestField(forms.TextInput):
  def to_python(self, value):
    return TestSource.objects.get_or_create(name=value)

class TestForm(ModelForm):
  class Meta:
    model=TestModel
    widgets = {
      'attribution' : TestField(attrs={'maxlength':'100'}),
    }

Unfortunately, I am getting: invalid literal for int() with base 10: 'test3' when attempting to check is_valid on the submitted form. Where am I going wrong? Is their and easier way to accomplish this?

like image 368
brown145 Avatar asked Nov 09 '10 18:11

brown145


People also ask

What is the difference between form and ModelForm in Django?

The similarities are that they both generate sets of form inputs using widgets, and both validate data sent by the browser. The differences are that ModelForm gets its field definition from a specified model class, and also has methods that deal with saving of the underlying model to the database. Save this answer.

What is forms ModelForm in Django?

Django Model Form It is a class which is used to create an HTML form by using the Model. It is an efficient way to create a form without writing HTML code. Django automatically does it for us to reduce the application development time.

What is form Is_valid () in Django?

The is_valid() method is used to perform validation for each field of the form, it is defined in Django Form class. It returns True if data is valid and place all data into a cleaned_data attribute.


2 Answers

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')
like image 111
Sam Dolan Avatar answered Nov 15 '22 23:11

Sam Dolan


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.

like image 21
Daniel Roseman Avatar answered Nov 15 '22 22:11

Daniel Roseman