Search code examples
pythondjangovalidationdjango-formsdjango-generic-views

Django form validation fails if value set using form.instance


I am trying to assign currently logged in user as an attribute to a field of a modelform instance using generic views. I have referred to this SO post and did override post method but it was giving me an error that CreatedBy field was required. Also I have gone through mro on ccbv many times. View as below.

class PostCreate(LoginRequiredMixin, CreateView):
    model = Post
    form_class = PostForm
    success_message = "Post Updated"
    success_url = '/home/'

    def get_form(self, form_class=None):
        """
        Returns an instance of the form to be used in this view.
        """
        form = super(PostCreate, self).get_form(form_class)
        form.instance.CreatedBy=User.objects.get(id=self.request.user.id)     
        return form

    def get_initial(self):
        """
        Returns the initial data to use for forms on this view.
        """
        self.initial = {'CreatedBy':  User.objects.get(id=self.request.user.id)}
        return self.initial.copy()

    def get_form_kwargs(self):
        """
        Returns the keyword arguments for instantiating the form.
        """
        kwargs = {
        'initial': self.get_initial(),
        'prefix': self.get_prefix(),
        }
        if self.request.method in ('POST', 'PUT'):
            kwargs.update({
            'data': self.request.POST,
            'files': self.request.FILES,
        })

        return kwargs

The reason I did override all these methods is to check the availability of initial dict in every method. It is available in every method but still form validation fails. But I think its better not to set values in get_initial(self) since I do not prepopulate form fields with existing data of any saved object, rather I want to update a field of an initialized form with User object of logged in user. I have gone through flow of control in ipdb but enden up with same error.

ipdb> form.initial={'CreatedBy':User.objects.get(id=request.user.id)}
ipdb> form.fields['CreatedBy']=User.objects.get(id=request.user.id) 
ipdb> form.instance.CreatedBy=User.objects.get(id=request.user.id)
ipdb> form.is_valid()
False
ipdb> form.instance.CreatedBy
<User: userone>
ipdb> form.fields['CreatedBy']
<User: userone>
ipdb> form.initial
{'CreatedBy': <User: userone>}

Please notice that I am not looking for a manipulated form_valid(self) method. I am caught up with this for hours. Any help is much appreciated.


Solution

  • The reason that created_by is still required is because you have not excluded it from the form itself; you need to do that in PostForm.

    class PostForm(forms.ModelForm):
        class Meta:
            exclude = ['created_by']
    

    As for assigning it automatically, the docs on the editing views have a specific section describing exactly how to do this; you override form_valid():

    def form_valid(self, form):
        form.instance.created_by = self.request.user
        return super(PostCreate, self).form_valid(form)