Django - 为什么我不能在来自admin.py的models.py中的类中访问动态生成的属性?

时间:2014-10-28 21:02:22

标签: django django-models django-admin django-1.7

以下是models.py

class Parent(models.Model):
    id        = models.CharField(max_length=14, primary_key=True)
    json_dump = models.TextField(null=False)

    def __init__(self, *args, **kwargs):
        super(Base, self).__init__(*args, **kwargs)
        setattr(self, 'name', json.loads(self.json_dump)['name'])

    class Meta:
        abstract = True


class Child(Parent):
    magnitude  = models.IntegerField()

在我的admin.py中,我想为子管理员配置显示name属性,所以我有以下内容:

class ChildAdmin(admin.ModelAdmin):
    model = Child

    def get_list_display(self, request):
        return ('id', 'name', 'magnitude')


admin.site.register(Child, ChildAdmin)

我必须从list_display方法动态生成get_list_display,否则Django会在启动时抛出错误,抱怨name模型中未定义Child 。但是,在运行时,name应该可用,因为只要从数据库中实例化对象,就会在__init__方法中设置它。

但是,当我尝试加载管理页面时,我收到错误:

Unable to lookup 'name' on Child or ChildAdmin

是什么给出了?

2 个答案:

答案 0 :(得分:6)

<class 'app.admin.ChildAdmin'>: (admin.E108) The value of 'list_display[1]' 
refers to 'name', which is not a callable, an attribute of 'ChildAdmin', 
or an attribute or method on 'app.Child'.

以上内容很可能是您收到的错误消息。抽象类不允许您像这样从抽象类继承实例属性。它正在寻找Child类上的self.name,它不存在。

我们要查看的错误部分是:

  

...这不是可调用

不。它不是可调用的,它是一个属性。

  

...&#39; ChildAdmin&#39; 的属性,

不。它不是ChildAdmin类的属性。

  

...或者&#39; app.Child&#39; 上的属性或方法。

这是绊倒你的部分。 &#34;是什么给#34;它不是Child类的属性或方法,而是Parent类。

您想要做的是:

class Parent(models.Model):
    id = models.CharField(max_length=14, primary_key=True)
    json_dump = models.TextField(null=False)

    class Meta:
        abstract = True

    @property
    def name(self):
        return json.loads(self.json_dump)['name']

class Child(Parent):
    magnitude  = models.IntegerField()

执行此操作将使父属的Child类可以使用该属性。或者,您可以创建名为@property的函数定义,而不是使用get_name装饰器。我发现第一个更简单。

此方法的警告是它不会在运行时保存名称。如果你想这样做,你可能想要考虑Django的信号来做一个post_save钩子来检索名称值并为你的模型添加name = models.CharField(...)

为了澄清,Django不支持这一点。在启动时,以下代码对list_display属性运行检查:

def _check_list_display_item(self, cls, model, item, label):
    """
    cls=<class 'app.admin.ChildAdmin'>
    model=<class 'app.models.Child'>
    item='name'
    """
    # 'name' is not callable
    if callable(item):  
        return []
    # <class 'app.admin.ChildAdmin'> does not have the class attribute 'name'
    elif hasattr(cls, item):
        return []
    # <class 'app.models.Child'> does not have the class attribute 'name'
    elif hasattr(model, item): 
        ...
    else:
        try:
            # <class 'app.models.Child'>.Meta does not have a field called 'name'
            model._meta.get_field(item)
        except models.FieldDoesNotExist:
            # This is where you end up.
            return [
                # This is a deliberate repeat of E108; there's more than one path
                # required to test this condition.
                checks.Error(
                    "The value of '%s' refers to '%s', which is not a callable, an attribute of '%s', or an attribute or method on '%s.%s'." % (
                        label, item, cls.__name__, model._meta.app_label, model._meta.object_name
                    ),
                    hint=None,
                    obj=cls,
                    id='admin.E108',
                )
            ]

正如您所看到的,我在运行的代码中添加了注释,以帮助您了解正在发生的事情。你是{1}}的实例确实有名,你是对的,但这并不是Django所寻求的。它正在寻找一个类属性,而不是实例属性。

所以,你可以解决这个问题的另一种方式(你也不会这样),这样做是:

Child

这很有效。我刚试过它。 Django会在类上找到属性。

答案 1 :(得分:0)

正如doc中所述,更改Parent模型,如下所示:

class Parent(models.Model):
    id        = models.CharField(max_length=14, primary_key=True)
    json_dump = models.TextField(null=False)

    def name(self):
        return json.loads(self.json_dump)['name']

    class Meta:
        abstract = True

将显示name属性。