从admin inline的modelform访问父模型实例

Cer*_*rin 17 python django django-models django-forms

我在Django的管理员中使用TabularInline,配置为显示一个额外的空白表单.

class MyChildInline(admin.TabularInline):
    model = MyChildModel
    form = MyChildInlineForm
    extra = 1
Run Code Online (Sandbox Code Playgroud)

该模型看起来像MyParentModel-> MyChildModel-> MyInlineForm.

我正在使用自定义表单,因此我可以动态查找值并填充字段中的选项.例如

class MyChildInlineForm(ModelForm):

    my_choice_field = forms.ChoiceField()

    def __init__(self, *args, **kwargs):
        super(MyChildInlineForm, self).__init__(*args, **kwargs)

        # Lookup ID of parent model.
        parent_id = None
        if "parent_id" in kwargs:
            parent_id = kwargs.pop("parent_id")
        elif self.instance.parent_id:
            parent_id = self.instance.parent_id
        elif self.is_bound:
            parent_id = self.data['%s-parent'% self.prefix]

        if parent_id:
            parent = MyParentModel.objects.get(id=parent_id)
            if rev:
                qs = parent.get_choices()
                self.fields['my_choice_field'].choices = [(r.name,r.value) for r in qs]
Run Code Online (Sandbox Code Playgroud)

这适用于绑定到实际记录的内联记录,但对于额外的空白表单,它不会在我的选择字段中显示任何值,因为它没有任何记录ID,并且无法查找关联的MyParentModel记录.

我已经检查了所有可以找到的值(args,kwargs,self.data,self.instance等),但我找不到任何方法来访问表格内联绑定的父对象.有没有办法做到这一点?

vdb*_*oor 25

更新:从Django 1.9开始,课堂上有一个def get_form_kwargs(self, index)方法BaseFormSet.因此,覆盖将数据传递给表单.

这将是Python 3/Django 1.9+版本:

class MyFormSet(BaseInlineFormSet):
    def get_form_kwargs(self, index):
        kwargs = super().get_form_kwargs(index)
        kwargs['parent_object'] = self.instance
        return kwargs


class MyForm(forms.ModelForm):
    def __init__(self, *args, parent_object, **kwargs):
        self.parent_object = parent_object
        super(MyForm, self).__init__(*args, **kwargs)


class MyChildInline(admin.TabularInline):
    formset = MyFormSet
    form = MyForm
Run Code Online (Sandbox Code Playgroud)

对于Django 1.8及以下版本:

要将formset的值传递给各个表单,您必须查看它们是如何构造的.带有"跳转到定义"的编辑器/ IDE真的有助于深入研究ModelAdmin代码,并了解inlineformset_factory它和它的BaseInlineFormSet类.

从那里你会发现表单是构造的_construct_form(),你可以覆盖它以传递额外的参数.它可能看起来像这样:

class MyFormSet(BaseInlineFormSet):
    def _construct_form(self, i, **kwargs):
        kwargs['parent_object'] = self.instance
        return super(MyFormSet, self)._construct_form(i, **kwargs)

    @property
    def empty_form(self):
        form = self.form(
            auto_id=self.auto_id,
            prefix=self.add_prefix('__prefix__'),
            empty_permitted=True,
            parent_object=self.instance,
        )
        self.add_fields(form, None)
        return form

class MyForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        self.parent_object = kwargs.pop('parent_object', None)
        super(MyForm, self).__init__(*args, **kwargs)


class MyChildInline(admin.TabularInline):
    formset = MyFormSet
    form = MyForm
Run Code Online (Sandbox Code Playgroud)

是的,这涉及私人_construct_form功能.

更新注意:这不包括empty_form,因此您的表单代码需要可选地接受参数.

  • 我试过这个,但是我在MyForm的行中遇到'parent_object'的KeyError,你试图弹出parent_object值. (3认同)

Iva*_*kin 5

我正在使用 Django 1.10,它对我
有用:创建一个 FormSet 并将父对象放入 kwargs:

class MyFormSet(BaseInlineFormSet):

    def get_form_kwargs(self, index):
        kwargs = super(MyFormSet, self).get_form_kwargs(index)
        kwargs.update({'parent': self.instance})
        return kwargs
Run Code Online (Sandbox Code Playgroud)

创建一个表单并在super调用 之前弹出一个属性

class MyForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        parent = kwargs.pop('parent')
        super(MyForm, self).__init__(*args, **kwargs)
        # do whatever you need to with parent
Run Code Online (Sandbox Code Playgroud)

把它放在内联管理员中:

class MyModelInline(admin.StackedInline):
    model = MyModel
    fields = ('my_fields', )
    form = MyFrom
    formset = MyFormSet
Run Code Online (Sandbox Code Playgroud)


ilv*_*var 2

AdminModel 有一些方法,例如get_formsets。它接收一个对象并返回一堆表单集。我认为您可以向该表单集类添加一些有关父对象的信息,并稍后在表单集的 __init__ 中使用它