Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change Django Form Data before it's processed

I have a form for a model which has two fields: field_A and field_B. I want to:

  • when the form is rendered: only field_A is displayed
  • when I call form.save() field_B is saved in the model with the value from field_A

What I've tried:

field_A = forms.CharField(required=True)
field_B = forms.CharField(required=False)

def save(self, *args, **kwargs):
     """
     Overriding save, so call the parent form save and return the new_user
     object.
     """
     self.data["field_B"] = self.data["field_A"]
     self.cleaned_data["username"] = self.cleaned_data["email"]
     super(MyParentClass*, self).save(*args, **kwargs) 

*both fields are inherited from ParentClass, where they are required

like image 623
Victor Avatar asked Sep 29 '10 02:09

Victor


1 Answers

Here is a solution that worked for me.

# models.py
class MyModel(models.Model):
    field_A = models.CharField(max_length = 255)
    field_B = models.CharField(max_length = 255)

    def __unicode__(self):
        return "%s: %s, %s" % (self.pk, self.field_A, self.field_B)

# forms.py
class MyModelForm(ModelForm):
    class Meta:
        model = MyModel
        exclude = ('field_B',)

    def save(self, *args, **kwargs):
        commit = kwargs.pop('commit', True)
        instance = super(MyModelForm, self).save(*args, commit = False, **kwargs)
        instance.field_B = self.cleaned_data['field_A']
        if commit:
            instance.save()
        return instance

Explanation of the form's Meta

exclude = ('field_B',)

Ensures that your first condition is met. When the form is shown to the user only field_A is displayed. The second condition is addressed in the overridden save() method.

Explanation of save()

commit = kwargs.pop('commit', True)

Extract the commit keyword argument if supplied.

instance = super(MyModelForm, self).save(*args, commit = False, **kwargs)

Create an instance of the underlying model. Explicitly pass commit=False to ensure that this instance is not saved to the database yet.

instance.field_B = self.cleaned_data['field_A']

Assign the value of field_A from cleaned_data to the instance's field_B. Remember that you should have called form.is_valid() before this. Otherwise there will be no cleaned_data.

        if commit:
            instance.save()

If the user wants the instance to be saved to the database then do that.

return instance

Finally, return the instance.

Sample Usage

In [1]: from app.forms import MyModelForm

In [2]: f = MyModelForm(data = {'field_A': 'Value A'})

In [3]: f.is_valid()
Out[3]: True

In [4]: f.save()
Out[4]: <MyModel: 3: Value A, Value A>

In [5]: f = MyModelForm(data = {'field_A': 'VA'})

In [6]: f.is_valid()
Out[6]: True

In [7]: f.save(commit = False)
Out[7]: <MyModel: None: VA, VA>

In [8]: 
like image 52
Manoj Govindan Avatar answered Sep 23 '22 14:09

Manoj Govindan