abisson
abisson

Reputation: 4425

Change Form Instance Django before Save

I am trying to change a form's instance before I save it. I need to set certain information within the view like this:

 class UploadedFile(models.Model):

     file = models.FileField(storage=s3store, upload_to=custom_upload_to)
     slug = models.SlugField(max_length=50, blank=True)
     bucket = models.ForeignKey(S3Bucket, blank=False)
     uploaded_by = models.ForeignKey(User, related_name='uploaded_by', blank=False)
     company = models.ForeignKey(Company, blank=False)

--

class UploadForm(ModelForm):

    class Meta:
        model = UploadedFile    

--

form = UploadForm(request.POST, request.FILES)
form.instance.company_id = r_user.company.id
form.instance.uploaded_by_id = r_user.id
form.instance.bucket_id = r_user.company.s3_bucket_id
if form.is_valid():
     form_object = form.save()

Now, I get that the form is not valid because company/uploaded/bucket are empty:

Errors Form:

  • company
  • This field is required.
  • bucket
  • This field is required.
  • uploaded_by
  • This field is required.
  • But I did set them! Do I need to make them blank=True, then do a save(commit=false), change them, and then resave? If yes, why is so? I did change the form ....

    Upvotes: 3

    Views: 6270

    Answers (3)

    east825
    east825

    Reputation: 909

    Actually you do here a couple of strange things.

    First: Why do you assign to company_id, uploaded_by_id and bucket_id? However yout got those fields in your model - it's names of columns in table created for this model - you should use ORM features directly here e.g. form.instance.company = r_user.company.

    Second: Form validation is applied to form.fields not to it's instance attribute, and you can't change them once form is created and bounded to supplied values.

    The recommended way to add some fields values to model instance created by form in Django (as I always thought) is to create custom ModelForm as you do and in Meta class specify all fields you want allow user to edit. For your case:

    class UploadForm(ModelForm):
    
        class Meta:
            model = UploadedFile
            fields = ('file', 'slug')
    

    Then instead of calling form_object = form.save() you call form_object = form.save(commit=False). Now you got your model instance with all fields from form filled already so you can add what you wish: e.g. form_object.company = r_user.company. Finally don't forget to call save() on model instance itself to store it in your database.

    This solution it described in Django documentation here

    Upvotes: 0

    Darwin
    Darwin

    Reputation: 1859

    Try this:

    data = request.POST.copy()
    data['company_id'] = r_user.company.id
    data['uploaded_by_id'] = r_user.id
    data['bucket_id'] = r_user.company.s3_bucket_id
    
    form = UploadForm(data, request.FILES)
    if form.is_valid():
         form_object = form.save()
    

    This way you are setting the data before creating the form.

    Upvotes: 2

    okm
    okm

    Reputation: 23871

    The assignments are late. In the __init__() of ModelForm, or in your code: the form = UploadForm(request.POST, request.FILES) line, an instance has been created and populated to initial of the form. Later modification upon the instance will not affect the value of form.initial.

    Also, if you want to fill in some fields automatically in backend, don't render them to user.

    Thus, yes, you could make them blank=True or exclude them from the form then follow the suggested way.

    Upvotes: 1

    Related Questions