django class-based view - UpdateView - How to access the request user while processing a form?
Solution 1
Hacking around like passing a hidden field doesn't make sense as this truly has nothing to do with the client - this classic "associate with logged in user" problem should definitely be handled on the server side.
I'd put this behavior in the form_valid
method.
class MyUpdateView(UpdateView):
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
super(MyUpdateView, self).save(form)
# the default implementation of form_valid is...
# def form_valid(self, form):
# self.object = form.save()
# return HttpResponseRedirect(self.get_success_url())
Solution 2
Must return an HttpResponse object. The code below works:
class MyUpdateView(UpdateView):
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
return super(MyUpdateView, self).form_valid(form)
Solution 3
We can also do like
class MyUpdateView(UpdateView):
form_class = SomeModelForm
def form_valid(self, form):
form.instance.user = self.request.user
return super(MyUpdateView, self).form_valid(form)
![un33k](https://i.stack.imgur.com/CsGzQ.png?s=256&g=1)
un33k
Val Neekman is a Solutions Architect with a passion for Typescript-Angular, Python-Django, UX and Performant Software. He is the Principal Consultant at Neekware Inc.
Updated on July 19, 2022Comments
-
un33k almost 2 years
In a class-base UpdateView in Django, I exclude the user field as it is internal to the system and I won't ask for it. Now what is the proper Django way of passing the user into the form. (How I do it now, is I pass the user into the init of the form and then override the form's save() method. But I bet that there is a proper way of doing this. Something like a hidden field or things of that nature.
# models.py class Entry(models.Model): user = models.ForeignKey( User, related_name="%(class)s", null=False ) name = models.CharField( blank=False, max_length=58, ) is_active = models.BooleanField(default=False) class Meta: ordering = ['name',] def __unicode__(self): return u'%s' % self.name # forms.py class EntryForm(forms.ModelForm): class Meta: model = Entry exclude = ('user',) # views.py class UpdateEntry(UpdateView): model = Entry form_class = EntryForm template_name = "entry/entry_update.html" success_url = reverse_lazy('entry_update') @method_decorator(login_required) def dispatch(self, *args, **kwargs): return super(UpdateEntry, self).dispatch(*args, **kwargs) # urls.py url(r'^entry/edit/(?P<pk>\d+)/$', UpdateEntry.as_view(), name='entry_update' ),
-
Bryson about 12 yearsI recommend removing the last two lines (
.save()
andreturn
) and replacing them withsuper(MyUpdateView, self).form_valid(form)
. This will simply execute the default form_valid() (identical to your last two lines) upon your modified form instance, keeping your code in sync with any future changes the Django project makes to the this method. -
Yuji 'Tomita' Tomita about 12 years@Bryson, nice! I've actually never done that before. I suppose it's not immediately intuitive that the instance is referenced on form.instance and thus modifying affects the future save() call again.
-
Bryson about 12 yearsSomething I missed: instead of using just
instance =
i believe you should be usingform.instance =
. This will add the user from the request object (assuming your form was made with this field, otherwise it'll error something like "Form has no attribute 'user'") to the form instance, which will then be passed back through thesuper()
. As it's written write now, the modified form with the user data is saved in a variable calledinstance
that never leaves your version of theform_valid
, so because you switched tosuper()
it never gets saved. Sorry it got complicated. haha -
Yuji 'Tomita' Tomita about 12 years@Bryson, the returned instance (variable:
instance
) refers to the same python object asform.instance
(not a copy) so it works. I thought that was what was so cool about your solution! I originally thought it wouldn't work either :) -
Amyth over 10 yearsJust to add, If you use
commit=false
and your model has many to many fields as well, you'll need to explicitly call theform.save_m2m()
to save many to many objects. -
Peterino over 7 yearsThis case is now covered by the Django docs in the "Class-based Views" topic. Note that instead of the decorator you can also use the LoginRequiredMixin from
django.contrib.auth.mixins
.