django 管理内联:从 formfield_for_foreignkey 获取对象

问题描述 投票:0回答:6

我正在尝试在 django 管理内联中过滤外键字段中显示的选项。因此,我想访问正在编辑的父对象。我一直在研究但找不到任何解决方案。

class ProjectGroupMembershipInline(admin.StackedInline):
    model = ProjectGroupMembership
    extra = 1
    formset = ProjectGroupMembershipInlineFormSet
    form = ProjectGroupMembershipInlineForm

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        if db_field.name == 'group':
            kwargs['queryset'] = Group.objects.filter(some_filtering_here=object_being_edited)
        return super(ProjectGroupMembershipInline, self).formfield_for_foreignkey(db_field, request, **kwargs)

我已经验证在编辑对象时kwargs为空,所以我无法从那里获取对象。

请问有什么帮助吗?谢谢

python django django-admin
6个回答
25
投票

另一种方式,恕我直言,感觉比 @erichonkanen 的答案更干净,但类似于这样:

class ProjectGroupMembershipInline(admin.StackedInline):
    # irrelevant bits....

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "group":
            try:
                parent_id = request.resolver_match.args[0]
                kwargs["queryset"] = Group.objects.filter(some_column=parent_id)
            except IndexError:
                pass
        return super().formfield_for_foreignkey(db_field, request, **kwargs)

19
投票

为了过滤管理内联中外键字段的可用选项,我覆盖了表单,以便可以更新表单字段的

queryset
属性。这样您就可以访问
self.instance
,它是表单中正在编辑的对象。所以像这样:

class ProjectGroupMembershipInlineForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['group'].queryset = Group.objects.filter(
            some_filtering_here=self.instance
        )
        

如果您执行上述操作,则无需使用

formfield_for_foreignkey
,它应该可以完成您所描述的任务。


13
投票

@mkoistinen 提供的答案很棒,但是 django 将父 ID 存储在 kwargs 而不是 args 中,因此像这样提取它是正确的。

parent_id = request.resolver_match.kwargs.get('object_id')

7
投票

我能够通过使用 formfield_for_foreignkey 并从 url 中删除对象 ID 来解决这个问题。这不是获取 ID 的最性感的方式,但 Django 还没有提供对管理对象上的对象 ID 的访问(它应该)。

class ObjectAdmin(admin.ModelAdmin):

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        obj_id = request.META['PATH_INFO'].rstrip('/').split('/')[-1]
        if db_field.name == 'my_field' and obj_id.isdigit():
            obj = self.get_object(request, obj_id)
            if obj:
                kwargs['queryset'] = models.Object.objects.filter(field=obj)
        return super().formfield_for_foreignkey(db_field, request, **kwargs)

4
投票

这是我想出的解决方案,看起来很干净

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "group":
            parent_id = request.resolver_match.kwargs['object_id']
            kwargs["queryset"] = Group.objects.filter(some_column=parent_id)
        return super().formfield_for_foreignkey(db_field, request, **kwargs)

0
投票

如果您使用的是较旧的 Django 版本,它可能尚不支持

resolver_match
属性。

在这种情况下,我找到了以下解决方案:

    def formfield_for_foreignkey(self, db_field, request, *args, **kwargs):
        field = super(ProjectGroupMembershipInline, self).formfield_for_foreignkey(db_field, request, *args, **kwargs)
        if db_field.name == 'group':
            resolved_url = resolve(request.path.replace('/{}/'.format(get_language()), '/'))  # remove localisation of url
            if resolved_url and resolved_url.args:  # check we are not in changelist view
                field.queryset = field.queryset.filter(pk=resolved_url.args[0]))  # obj id first and only arg for view.
        return field
© www.soinside.com 2019 - 2024. All rights reserved.