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

Django: accessing the model instance from within ModelAdmin?

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

1
2
3
4
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为空/无),不应显示任何订单。对于已拆分的订单,应仅显示单个父订单。

有一个非常新的modeladmin方法,formfield_for_foreignkey,它似乎非常适合这样做,因为查询集可以在其中进行过滤。假设我们正在查看订单11234的详细视图,它与订单11208分开。代码如下

1
2
3
4
5
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中运行时,注释行起作用,返回包含order 11208 for 11234和所有其他可能已从中拆分的订单的单个项查询集。

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

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

我找不到替代的工作方式???????关于"当前"订单实例,我已经挖得很深了。formfield_for_foreignkey中的self表示modeladmin实例,虽然它有model属性,但它不是order模型实例(它是modelbase引用;self.model()返回一个实例,但它的orderNumber为none)。

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


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

1
2
3
4
5
6
7
8
9
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


我已经用这种方式模拟了我的内联类。它在如何获取父表单ID来过滤内联数据方面有点难看,但是它可以工作。它从父窗体中按公司筛选单位。

原始概念解释如下:http://www.stereoplex.com/blog/filtering下拉列表in the django admin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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)


上面来自欧文·朱利叶斯的回答对我很有效,但我发现"get-uobject"这个名称与django函数冲突,所以将函数命名为"my-get-uobject"。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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)

它告诉我不要"回应"别人的回答,但我还不能"回复",我已经找了一段时间了,所以希望这对别人有帮助。我也不被允许上票,否则我会的!