45

Say I have a form that looks like this:

forms.py

class CreateASomethingForm(ModelForm):
    class Meta:
        model = Something
        fields = ['field2', 'field3', 'field4']

I want the form to have these three fields. However my Somethingclass also has field1. My question is - how do I add data to field1, if I am not using the ModelForm to collect the data. I tried doing something like this, but it isn't working and I am unsure on the proper way to solve this:

views.py

def create_something_view(request):
    if (request.method == 'POST'):
        # Create an object of the form based on POST data
        obj = CreateASomething(request.POST)
        # ** Add data into the blank field1 ** (Throwing an error)
        obj['field1'] = request.user
        # ... validate, save, then redirect 

The error I receive is:

TypeError: 'CreateAClassForm' object does not support item assignment

In Django, what is the proper way to assign data to a ModelForm object before saving?

Joker
  • 2,119
  • 4
  • 27
  • 38

5 Answers5

90
form = CreateASomething(request.POST)
if form.is_valid():
    obj = form.save(commit=False)
    obj.field1 = request.user
    obj.save()
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • Thank you - a silly mistake on my end using a list access call to get an object's attribute. – Joker Jun 15 '13 at 22:11
  • 3
    For class-based views you can also [override the form_valid method](https://docs.djangoproject.com/en/1.5/topics/class-based-views/generic-editing/#basic-forms) to add this kind of information, then call `super(MyView, self).form_valid(form)` to achieve the same effect. – cms_mgr Jul 16 '13 at 10:32
  • Is there a way to overwrite the save method of the form to achieve the same thing? – JacobF Dec 19 '13 at 20:42
  • 8
    What about if the field is a required field? It never makes it to form_valid(). – trpt4him Jun 01 '14 at 02:13
  • @trpt4him check my answer – Emmanuel Osimosu Feb 23 '16 at 16:31
  • Don't forget to call `form.save_m2m()` after `obj.save()` if your model has M2M fields. – Mark Mishyn Jan 10 '17 at 08:59
  • 1
    Thank you, very cool! First it was giving me error that "Field is Required", but i sent that field's property "blank=True" in model. Works like a charm – Khateeb321 Nov 06 '18 at 14:10
  • @Khateeb321 Setting a required field to `blank=True` in your model is probably not the best idea as it makes the semantics of your model unclear. A better solution when using class-based views would be to exclude your required field from the list of fields in the `ModelForm`, then the form will be valid and you can add the data for the required field in `form_valid()`. – wlo Dec 01 '22 at 10:34
7

Sometimes, the field might be required which means you can't make it past form.is_valid(). In that case, you can pass a dict object containing all fields to the form.

   if request.method == 'POST':
       data = {
        'fields1': request.user,
        'fields2': additional_data,
       }
       form = CreateASomethingForm(data)

    if form.is_valid():
        form.commit(save)
Emmanuel Osimosu
  • 5,625
  • 2
  • 38
  • 39
2

There are two ways given by Django official LINK : https://docs.djangoproject.com/en/3.0/topics/forms/modelforms/

Method 1]

author = Author(title='Mr')
form = PartialAuthorForm(request.POST, instance=author)
form.save()

Method 2]

form = PartialAuthorForm(request.POST)
author = form.save(commit=False)
author.title = 'Mr'
author.save()
Viraj Wadate
  • 5,447
  • 1
  • 31
  • 29
1

Here is a more suitable way to add data especially used during testing:

First convert an existing entry into a dictionary with the model_to_dict function

from django.forms.models import model_to_dict

...

valid_data = model_to_dict(entry)

Then add the new data into this dictionary

valid_data['finish_time'] = '18:44'

This works better than setting the value in the form

update_form.finish_time = '18:44'

Create the form with the valid data and the instance

update_form = UserEntryForm(valid_data, instance=entry)

Do any assertions you require:

self.assertTrue(update_form.is_valid())
entry = update_form.save()
self.assertEqual(
    entry.status,
    1
)
tread
  • 10,133
  • 17
  • 95
  • 170
0

To add an answer that works for Class Based View considering the amount of answers out there related to old method view - I felt the simplicity of this approach should be out there.

Let's take the generic UpdateView - and should also work for the generic CreateView

# Edit my_app/views.py

from django.views.generic import UpdateView
from my_app.models import MyModel

class MyUpdateView(UpdateView):
    model = MyModel
    ...

    def form_valid(self, form):
        # Note the instance after form.
        # Simply assigning the field on the form won't work.
        # nothing will complain, but it will not save. 
        # Assign the data to form.instance
        form.instance.updated_by_user = self.request.user
        super().form_valid(form)
S.D.
  • 2,486
  • 1
  • 16
  • 23