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

时间:2009-06-04 08:32:38

标签: django django-admin modeladmin

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

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类。对于详细信息视图,我在parent_order属性中包含了fieldsets。当然,默认情况下,它会列出选择框中的所有订单,但这不是所需的行为。相反,对于没有父订单的订单(即未从其他订单拆分; 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中运行时返回,返回包含#11234的#11208订单的单项查询集以及可能已从中拆分的所有其他订单。

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

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

我发现没有办法替换?????参考“当前”订单实例,我挖得很深。 self里面的formfield_for_foreignkey指的是ModelAdmin实例,虽然它确实有model属性,但它不是订单模型实例(它是ModelBase引用; self.model()返回一个实例,但其订单号为无)。

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

3 个答案:

答案 0 :(得分:57)

我认为您可能需要以稍微不同的方式处理此问题 - 通过修改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

答案 1 :(得分:7)

我用这种方式建模了我的内联类。 它如何获得父表单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)

答案 2 :(得分:3)

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)

它告诉我不要“回应”别人的回答,但是我还不允许“回复”,我一直在寻找这个,所以希望这会对其他人有所帮助。我也不被允许赞成或者我完全愿意!