How To Access The Request Object in Django's GenericStackedInline Admin
Question: Using **GenericStackedInline** in Django 1.9 _(Python 3.4)_ I want to access
the **request** object before **saving my model** in the Django **Admin**.

When using `MediaItemAdmin` I can intercept the save function before
`obj.save()` is run, as in this example:

**admin.py**

    
    
    class StuffAdmin(admin.ModelAdmin):
        def save_model(self, request, obj, form, change):
            # Do some stuff here like obj.user = request.user before saving.
            obj.save()
    

However, the same behaviour or 'hook' isn't available using a
`GenericStackedInline`. It appears to call the model save method directly:

**admin.py**

    
    
    class StuffAdmin(GenericStackedInline):
        model = StuffModel
    
        def save_model(self, request, obj, form, change):
            print("I'm never run :(")
            obj.save()
    

As I understand `GenericStackedInline` inherits from a `form` so I have also
tried using a form and overriding that as in this example:

**admin.py**

    
    
    class StuffAdmin(GenericStackedInline):
        model = StuffModel
        form = StuffForm
    
    class StuffForm(forms.ModelForm):
        def __init__(self, *args, **kwargs):
            super(StuffForm, self).__init__(*args, **kwargs)
    
        def save_model(self, request, obj, form, change):
            print("Still not run!(")
            obj.save()
    
        def save_form(self, request, obj, form, change):
            print("Work already!")
            obj.save()
    

I have searched stackoverflow, but most are unanswered, as seen here
[accessing request object within a django admin inline
model](http://stackoverflow.com/questions/7107498/accessing-request-object-
within-a-django-admin-inline-model) or say use `init` to do something like
`self.request = kwargs.pop('request')` however, `request` is never passed
here, right?

Anyhow, any idea how I can call the request object and update my instance
before the model save() is called?


Answer: The method that saves the "inlines" is part of `ModelAdmin`, not
`InlineModelAdmin`.

    
    
    class BarInline(GenericStackedInline):
        model = Bar
    
    class FooModelAdmin(ModelAdmin):
        model = Foo
        inlines = [BarInline]
    
        def save_formset(self, request, form, formset, change):
            """
            `form` is the base Foo form
            `formset` is the ("Bar") formset to save
            `change` is True if you are editing an existing Foo,
                        False if you are creating a new Foo
            """
            if formset_matches_your_inline_or_some_requirement(formset):
                do_something_with(request)
            super().save_formset(request, form, formset, change)
    

If you want to check whether the formset is the `BarInline`'s formset, you can
do something like this:

    
    
    class BarInline(GenericStackedInline):
        model = Bar
    
        def get_formset(self, *args, **kwargs):
            formset = super().get_formset(*args, **kwargs)
            formset.i_come_from_bar_inline = True
            return formset
    
    
    class FooModelAdmin(ModelAdmin):
        model = Foo
        inlines = [BarInline]
    
        def save_formset(self, request, form, formset, change):
            if getattr(formset, 'i_come_from_bar_inline', False):
                do_something_with(request)
            super().save_formset(request, form, formset, change)
    

Or even better, make it generic:

    
    
    class BarInline(GenericStackedInline):
        model = Bar
    
        def pre_save_formset(self, request, form, model_admin, change):
           """Do something here with `request`."""
    
    class FooModelAdmin(ModelAdmin):
        model = Foo
        inlines = [BarInline]
    
        def save_formset(self, request, form, formset, change):
            if hasattr(formset, 'pre_save_formset'):
                formset.pre_save_formset(request, form, self, change)
            super().save_formset(request, form, formset, change)
            if hasattr(formset, 'post_save_formset'):
                formset.post_save_formset(request, form, self, change)
    

* * *

If you need to do something with the request before each form save rather than
before each formset, you will have to use your own Form and FormSet propagate
the request through the formset to the form:

    
    
    from django.forms import ModelForm
    from django.forms.models import BaseInlineFormSet
    
    class BarForm(ModelForm):
        model = Bar
    
        def __init__(self, *args, **kwargs):
            request = kwargs.pop('request', None)
            super().__init__(*args, **kwargs)
            self.request = request
    
        def save(self, commit=True):
            print(self.request)
            print(self.instance)
            obj = super().save(False)  # Get object but don't save it
            do_something_with(self.request, obj)
            if commit:
                obj.save()
                self.save_m2m()
            return obj
    
    class BarFormSet(BaseInlineFormSet):
    
        @property
        def request(self):
            return self._request
    
        @request.setter
        def request(self, request):
            self._request = request
            for form in self.forms:
                form.request = request
    
    class BarInline(GenericStackedInline):
        codel = Bar
        form = BarForm
        formset = BarFormSet
    
    
    class FooModelAdmin(ModelAdmin):
        inlines = [BarInline]
    
        def _create_formsets(self, request, obj, change):
            formsets, inline_instances = super()._create_formsets(request, obj, change)
            for formset in formsets:
                formset.request = request
            return formsets, inline_instances
    

According to you usecase, the save method might also simply look like
something like this:

    
    
    class BarForm(ModelForm):
        model = Bar
    
        def save(self, commit=True):
            do_something_with(self.request, self.instance)
            return super().save(commit)  # Get object but don't save it
    

