Django:从内联模型管理员访问父实例

Posted

技术标签:

【中文标题】Django:从内联模型管理员访问父实例【英文标题】:Django: access the parent instance from the Inline model admin 【发布时间】:2015-11-15 23:24:17 【问题描述】:

如何从内联模型管理员访问父实例?

我的目标是根据父实例的状态覆盖has_add_permission 函数。如果父母的状态不是 1,我不想允许添加孩子。

class ChildInline(admin.TabularInline):
    model = Child
    form = ChildForm

    fields = (
        ...
    )
    extra = 0

    def has_add_permission(self, request):
        # Return True only if the parent has status == 1
        # How to get to the parent instance?
        #return True

class ParentAdmin(admin.ModelAdmin):
    inlines = [ChildInline,]

【问题讨论】:

你查看 request.parent.status 了吗? 'WSGIRequest' object has no attribute 'parent' 你找到解决办法了吗? 一种解决方法:我最终在检查 Parent 的 Child 模型的 clean 方法中添加了一些验证规则。 【参考方案1】:

Django

使用 Django 的 Request 对象(您有权访问)检索 request.path_info,然后从 resolve 匹配中的 args 检索 PK。示例:

from django.contrib import admin
from django.core.urlresolvers import resolve
from app.models import YourParentModel, YourInlineModel


class YourInlineModelInline(admin.StackedInline):
    model = YourInlineModel

    def get_parent_object_from_request(self, request):
        """
        Returns the parent object from the request or None.

        Note that this only works for Inlines, because the `parent_model`
        is not available in the regular admin.ModelAdmin as an attribute.
        """
        resolved = resolve(request.path_info)
        if resolved.args:
            return self.parent_model.objects.get(pk=resolved.args[0])
        return None

    def has_add_permission(self, request):
        parent = self.get_parent_object_from_request(request)

        # Validate that the parent status is active (1)
        if parent:
            return parent.status == 1

        # No parent - return original has_add_permission() check
        return super(YourInlineModelInline, self).has_add_permission(request)


@admin.register(YourParentModel)
class YourParentModelAdmin(admin.ModelAdmin):
    inlines = [YourInlineModelInline]

Django >= 2.0 答案:

感谢Mark Chackerian 进行以下更新:

使用 Django 的 Request 对象(您有权访问)检索 request.path_info,然后从 resolve 匹配中的 args 检索 PK。示例:

from django.contrib import admin
from django.urls import resolve
from app.models import YourParentModel, YourInlineModel


class YourInlineModelInline(admin.StackedInline):
    model = YourInlineModel

    def get_parent_object_from_request(self, request):
        """
        Returns the parent object from the request or None.

        Note that this only works for Inlines, because the `parent_model`
        is not available in the regular admin.ModelAdmin as an attribute.
        """
        resolved = resolve(request.path_info)
        if resolved.args:
            return self.parent_model.objects.get(pk=resolved.args[0])
        return None

    def has_add_permission(self, request):
        parent = self.get_parent_object_from_request(request)

        # Validate that the parent status is active (1)
        if parent:
            return parent.status == 1

        # No parent - return original has_add_permission() check
        return super(YourInlineModelInline, self).has_add_permission(request)


@admin.register(YourParentModel)
class YourParentModelAdmin(admin.ModelAdmin):
    inlines = [YourInlineModelInline]

【讨论】:

这是它唯一的工作方式。奇怪的是很难访问父对象。我花了 1 个小时才找到这个解决方案。 我对 python 3 做了一些更改。1) 解析导入是 from django.urls import resolve 2) “已解决”逻辑是 if resolved.kwargs.get('object_id'): return self.parent_model.objects.get(pk=resolved.kwargs['object_id']) 我在 2.2.1 中找不到self.parent_model 属性,但__init__ 对我来说有kwargs['instance'],它似乎包含父模型实例。 无需import resolverequest.resolver_match 属性已经可用,因此您可以使用request.resolver_match.kwargs.get('object_id')。这适用于 Django 2.2,也适用于 3.1 (source)。【参考方案2】:

我认为这是在内联模型中获取父实例的一种更简洁的方法。

class ChildInline(admin.TabularInline):
    model = Child
    form = ChildForm

    fields = (...)
    extra = 0

    def get_formset(self, request, obj=None, **kwargs):
        self.parent_obj = obj
        return super(ChildInline, self).get_formset(request, obj, **kwargs)

    def has_add_permission(self, request):
        # Return True only if the parent has status == 1
        return self.parent_obj.status == 1


class ParentAdmin(admin.ModelAdmin):
    inlines = [ChildInline, ]

【讨论】:

我无法让它按原样工作。我得到“Parent_obj”不是 ChildInline 的属性。 这很奇怪,它对我有用...你使用的是哪个版本的 Django? Python 2.7下的Django 1.10 不。我转储了对象的 DIR,正如 Python 所说,没有这样的成员。所以我实现了上面更混乱的解决方案。这是几个项目之前的事,甚至不确定这是在哪个项目中。新项目在 Django 2 和 Python 3 中。时间会证明这是否会再次出现。我会先尝试你的更清洁的解决方案! 不管怎样,has_add_permissionget_formset 之前被调用。这个解决方案的问题(我在 Django 1.8 上测试过)是代码的执行顺序。在生成表单之前,它会检查您是否可以添加它们。【参考方案3】:

我尝试了 Michael B 的解决方案,但对我不起作用,我不得不改用它(使用 kwargs 的小修改):

def get_parent_object_from_request(self, request):
        """
        Returns the parent object from the request or None.

        Note that this only works for Inlines, because the `parent_model`
        is not available in the regular admin.ModelAdmin as an attribute.
        """
        resolved = resolve(request.path_info)
        if resolved.kwargs:
            return self.parent_model.objects.get(pk=resolved.kwargs["object_id"])
        return None

【讨论】:

【参考方案4】:

您只需要添加obj参数并检查父模型状态

class ChildInline(admin.TabularInline):
   model = Child
   form = ChildForm

   fields = (
    ...
    )
   extra = 0
   #You only need to add obj parameter 
   #obj is parent object now you can easily check parent object status
   def has_add_permission(self, request, obj=None):
        if obj.status == 1:
           return True
        else:
           return False


   class ParentAdmin(admin.ModelAdmin):
         inlines = [ChildInline,]

【讨论】:

以上是关于Django:从内联模型管理员访问父实例的主要内容,如果未能解决你的问题,请参考以下文章

has_delete_permission 在 django admin inline 中获取父实例

如何在 django 管理页面上创建锚点以自动从 url 向下滚动到内联或字段

在 Django 管理站点中,如何通过内联访问模型属性?

Django 管理员自定义验证 - 至少需要一个内联外键模型

用户的 Django 管理内联模型

如何在字段集中显示Django管理员内联模型?