如何在Django的GenericStackedInline管理员

时间:2016-07-06 09:48:19

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

在Django 1.9 (Python 3.4)中使用 GenericStackedInline 我想在保存我的模型之前访问请求对象在Django 管理员

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

然而,相同的行为或“' hook'使用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()之前调用请求对象并更新我的实例?

2 个答案:

答案 0 :(得分:3)

保存“内联”的方法是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将请求通过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

根据你的用例,save方法也可能看起来像这样:

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

答案 1 :(得分:1)

管理员课程不会从表格中继承;他们包含表单。并且ModelForms没有save_modelsave_form方法,他们只有save方法。完全可以覆盖该方法,但它不接受request;您还需要覆盖__init__以接受该参数并将其从modeladmin的get_form_kwargs方法中传入。