Accessing request.user in class based generic view CreateView in order to set FK field in Django
Asked Answered
E

3

37

So I have a model that includes:

class Place(models.Model):
    ....
    created_by = models.ForeignKey(User)

My view is like so:

class PlaceFormView(CreateView):
    form_class = PlaceForm

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super(PlaceFormView, self).dispatch(*args, **kwargs)

Is there a way for me to access request.user and set created_by to that user? I've looked through the docs, but can't seem to find any hints toward this.

`

Execrable answered 26/4, 2011 at 3:53 Comment(0)
W
35

How about overriding form_valid which does the form saving? Save it yourself, do whatever you want to it, then do the redirect.

class PlaceFormView(CreateView):
    form_class = PlaceForm

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super(PlaceFormView, self).dispatch(*args, **kwargs)

    def form_valid(self, form):
        obj = form.save(commit=False)
        obj.created_by = self.request.user
        obj.save()        
        return http.HttpResponseRedirect(self.get_success_url())
Weatherboarding answered 26/4, 2011 at 4:0 Comment(6)
Thanks! This works, except for the redirect. I get the error: "No URL to redirect to. Either provide a url or define a get_absolute_url method on the Model." However, I do have a get_absolute_url() set on the model, and when I first got this view working (with created_by a select box on form) it redirected properly. Trying to figure this out now. Any ideas?Execrable
Yes, it appears get_absolute_url is pulled from self.object - so in your code, set self.object = obj in form_validRosendorosene
Is it possible to do this without extending the CreateView?Lemaceon
Shouldn't we rather do at the end of form_valid: return super(PlaceFormView, self).form_valid(form) Otherwise the default form_valid is never called...Argentite
@Argentite the "default" form_valid does the same, you can check it easily at ccbv.co.uk, it justs saves the object and redirects.Manama
Awsome answer. Works like a charm !Bilestone
R
26

I know that this is old, but for other people with this problem:

There is an even simpler way - since saving a form multiple times will always use the same model instance, you can also do:

def form_valid(self, form):
    obj = form.save(commit=False)
    obj.created_by = self.request.user
    return super(PlaceFormView, self).form_valid(form)

That way, you get all the benefits of the super call - it's trivial to see that you're really only adding those two lines of code, and you don't have to repeat yourself by replicating the redirect logic.

Rosel answered 12/12, 2015 at 11:3 Comment(0)
A
6

An alternate way to do this is to pass the user through overwriting the get_initial() method in the CreateView, and modify save method in the PlaceForm class to save the user:

class PlaceForm(forms.ModelForm):
    ...
    ...
    ...

    def __init__(self, *args, **kwargs):
        self.created_by = kwargs['initial']['created_by']
        super(PlaceForm, self).__init__(*args, **kwargs)

    def save(self, commit=True):
        obj = super(PlaceForm, self).save(False)
        obj.created_by = self.created_by
        commit and obj.save()
        return obj

class PlaceFormView(CreateView):
    ...
    ...
    form_class = PlaceForm

    def get_initial(self):
        self.initial.update({ 'created_by': self.request.user })
        return self.initial

This way the saving logic is still encapsulated within the form class.

Actinism answered 11/9, 2011 at 3:33 Comment(1)
This worked for me and seems like the best and simplest way to solve this problemWas

© 2022 - 2024 — McMap. All rights reserved.