Django:从 ModelAdmin 中访问模型实例?

Posted

技术标签:

【中文标题】Django:从 ModelAdmin 中访问模型实例?【英文标题】:Django: accessing the model instance from within ModelAdmin? 【发布时间】:2010-10-31 06:14:35 【问题描述】:

我有一个网上商店应用程序中的订单模型,具有自动递增的主键和自身的外键,因为订单可以拆分为多个订单,但必须保持与原始订单的关系。

class Order(models.Model):
    ordernumber = models.AutoField(primary_key=True)
    parent_order = models.ForeignKey('self', null=True, blank=True, related_name='child_orders')
    # .. other fields not relevant here

我已经为管理站点注册了一个 OrderAdmin 类。对于详细视图,我在 fieldsets 属性中包含了 parent_order。当然,默认情况下,这会在选择框中列出所有订单,但这不是所需的行为。相反,对于没有父订单的订单(即没有从另一个订单拆分;parent_order 为 NULL/None),不应显示任何订单。对于已拆分的订单,这应该只显示单父订单。

有一个相当新的 ModelAdmin 方法可用,formfield_for_foreignkey,这似乎非常适合,因为可以在其中过滤查询集。假设我们正在查看订单#11234 的详细视图,该订单已从订单#11208 中拆分出来。代码如下

def formfield_for_foreignkey(self, db_field, request, **kwargs):
    if db_field.name == 'parent_order':
        # kwargs["queryset"] = Order.objects.filter(child_orders__ordernumber__exact=11234)
        return db_field.formfield(**kwargs)
    return super(OrderAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

注释行在 Python shell 中运行时有效,返回一个单项查询集,其中包含订单 #11208 和 #11234 以及所有其他可能已从中拆分的订单。

当然,我们不能在那里对订单号进行硬编码。我们需要引用我们正在查看其详细信息页面的订单实例的ordernumber 字段。像这样:

kwargs["queryset"] = Order.objects.filter(child_orders__ordernumber__exact=?????)

我没有找到替换的有效方法??????参考“当前” Order 实例,我已经深入挖掘了。 selfformfield_for_foreignkey 指的是 ModelAdmin 实例,虽然它确实有一个 model 属性,但它不是订单模型实例(它是一个 ModelBase 引用;self.model() 返回一个实例,但它的 ordernumber 是 None )。

一种解决方案可能是从 request.path (/admin/orders/order/11234/) 中提取订单号,但这真的很难看。我真的希望有更好的方法。

【问题讨论】:

【参考方案1】:

我以这种方式为我的内联类建模。 它如何获取父表单 id 来过滤内联数据有点难看,但它确实有效。它从父表单中按公司过滤单位。

这里解释原概念http://www.stereoplex.com/blog/filtering-dropdown-lists-in-the-django-admin

class CompanyOccupationInline(admin.TabularInline):

    model = Occupation
    # max_num = 1
    extra = 0
    can_delete = False
    formset = RequiredInlineFormSet

    def formfield_for_dbfield(self, field, **kwargs):

        if field.name == 'unit':
            parent_company = self.get_object(kwargs['request'], Company)
            units = Unit.objects.filter(company=parent_company)
            return forms.ModelChoiceField(queryset=units)
        return super(CompanyOccupationInline, self).formfield_for_dbfield(field, **kwargs)

    def get_object(self, request, model):
        object_id = resolve(request.path).args[0]
        try:
            object_id = int(object_id)
        except ValueError:
            return None
        return model.objects.get(pk=object_id)

【讨论】:

一个更简洁的解决方法是利用 URL 解析器:object_id = resolve(request.path).args[0] 谢谢@philipk。 @DanielH。该链接已被所有者删除。我将编辑答案。谢谢。 在 Django 2.2 和 3.1 (source) 中,您可以简单地使用 request.resolver_match.kwargs.get('object_id')【参考方案2】:

Erwin Julius 的上述回答对我有用,但我发现名称“get_object”与 Django 函数冲突,因此将函数命名为“my_get_object”。

class CompanyOccupationInline(admin.TabularInline):

    model = Occupation
    # max_num = 1
    extra = 0
    can_delete = False
    formset = RequiredInlineFormSet

    def formfield_for_dbfield(self, field, **kwargs):

        if field.name == 'unit':
            parent_company = self.my_get_object(kwargs['request'], Company)
            units = Unit.objects.filter(company=parent_company)
            return forms.ModelChoiceField(queryset=units)
        return super(CompanyOccupationInline, self).formfield_for_dbfield(field, **kwargs)

    def my_get_object(self, request, model):
        object_id = request.META['PATH_INFO'].strip('/').split('/')[-1]
        try:
            object_id = int(object_id)
        except ValueError:
            return None
        return model.objects.get(pk=object_id)

它告诉我不要“回复”别人的答案,但我还不允许“回复”,我一直在寻找这个,所以希望这对其他人有帮助。我也不允许投票,否则我完全会!

【讨论】:

虽然最好将此信息作为评论留下,但我知道您对此没有足够的声誉——尽管知道get_object 会导致冲突是很有价值的。【参考方案3】:

我认为您可能需要以稍微不同的方式来解决这个问题 - 通过修改 ModelForm,而不是管理类。像这样的:

class OrderForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super(OrderForm, self).__init__(*args, **kwargs)
        self.fields['parent_order'].queryset = Order.objects.filter(
            child_orders__ordernumber__exact=self.instance.pk)

class OrderAdmin(admin.ModelAdmin):
    form = OrderForm

【讨论】:

有效!太感谢了!我对所有这些 ModelForm/ModelAdmin 业务完全陌生,并且找错地方了。 我意识到这篇文章很旧,但这对我来说也是一个不错的解决方法,因为传递给它的 obj 参数当前是父对象,而不是内联对象实例。出于所有意图和目的,这使我的对象只读,允许我只将一个对象返回到我的外键中。 确保先调用 super.__init__。设置 self.instance。 如果您的管理模型在管理模型中有autocomplete_fields 属性,它将无法工作

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

如何在 django admin 中组合两个或多个 ModelAdmin

Django - 在 render_change_form (ModelAdmin) 中获取对象 ID

django-admin之ModelAdmin最全解释

django-admin之ModelAdmin最全解释

Django的admin后台

Django ModelAdmin.get_urls()没有注册自定义网址