【问题标题】:How To Access The Request Object in Django's GenericStackedInline Admin如何在 Django 的 GenericStackedInline Admin 中访问请求对象
【发布时间】:2016-11-08 07:53:30
【问题描述】:

在 Django 1.9 中使用 GenericStackedInline (Python 3.4) 我想在 保存我的模型之前访问 request 对象> 在 Django Admin 中。

当使用MediaItemAdmin 时,我可以在obj.save() 运行之前拦截保存功能,如下例所示:

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()

但是,使用GenericStackedInline 无法实现相同的行为或“挂钩”。看来是直接调用模型保存方法了:

admin.py

class StuffAdmin(GenericStackedInline):
    model = StuffModel

    def save_model(self, request, obj, form, change):
        print("I'm never run :(")
        obj.save()

据我所知,GenericStackedInline 继承自 form,所以我也尝试使用表单并覆盖它,如本例所示:

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()

我已经搜索了 stackoverflow,但大多数都没有得到答复,如这里所见 accessing request object within a django admin inline model 或说使用 init 执行类似 self.request = kwargs.pop('request') 的操作,但是,request 永远不会在这里传递,对吗?

无论如何,知道如何在调用模型 save() 之前调用请求对象并更新我的实例吗?

【问题讨论】:

    标签: python django python-3.x django-forms django-admin


    【解决方案1】:

    保存“内联”的方法是ModelAdmin 的一部分,而不是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)
    

    如果你想检查formset是否是BarInline的formset,你可以这样做:

    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)
    

    或者更好,让它通用:

    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)
    

    如果您需要在每个表单保存之前而不是在每个表单集之前对请求执行某些操作,则必须使用自己的 Form 和 FormSet 将请求通过表单集传播到表单:

    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
    

    根据您的用例,保存方法也可能看起来像这样:

    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
    

    【讨论】:

    • 谢谢,我可以看到我现在使用了 def save() 但这并没有得到请求对象
    • 我在这一行收到错误set_request() missing 1 required positional argument: 'self' formset.set_request(request) 那是因为在这个阶段没有“self”对吧?
    • 这是因为get_formset() 返回的是 FormSet 类,而不是实例。我的错。我修正了我的答案。
    【解决方案2】:

    管理类不继承自表单;它们包括形式。而且 ModelForms 没有 save_modelsave_form 方法,它们只有 save 方法。完全可以覆盖该方法,但它不接受request;您还需要覆盖 __init__ 以接受该参数并将其从模型管理员的 get_form_kwargs 方法传入。

    【讨论】:

    • 好的,有道理。你知道__init__中的request参数隐藏在哪里吗,是post.DATA等还是self?
    • 它没有隐藏在任何地方。您必须在方法签名中明确接受它,或者从**kwargs 获取它,并将其存储在实例属性中。
    猜你喜欢
    • 2017-05-28
    • 1970-01-01
    • 2020-11-04
    • 1970-01-01
    • 1970-01-01
    • 2012-01-28
    • 1970-01-01
    • 1970-01-01
    • 2011-11-15
    相关资源
    最近更新 更多