无法尝试动态添加方法到Python类(即django-tables2'Table')

时间:2013-01-03 07:08:29

标签: python django metaprogramming django-tables2

因此,对于Django项目,我真的希望能够动态生成和显示表(,基于查询集),而无需事先知道内容或架构。

看起来django-tables2应用程序为呈现表提供了很好的功能,但它要求您通过在自定义的Table子类上声明属性来显式声明列名,或者为它提供一个模型来推断列

即,要使用名为“name”的列,您需要:

class NameTable(tables.Table):
   name = tables.Column()

Tables类没有提供事后添加列的方法,因为从读取源代码开始,它似乎使用了一个元类来清除__new__上的类属性并锁定它们。

似乎非常简单的元编程将是一个优雅的解决方案。我定义了一个基本的类工厂,接受列名是参数:

def define_table(columns):
    class klass(tables.Table): pass              
    for col in columns:
        setattr(klass, col, tables.Column())
    return klass

可悲的是,这不起作用。如果我跑'

x = define_table(["foo", "bar"])(data)
x.foo
x.bar

我回来了:

<django_tables2.columns.base.Column object at 0x7f34755af5d0>
<django_tables2.columns.base.Column object at 0x7f347577f750>

但是,如果我列出列:

print x.base_columns

我没有回来,只有{}

我意识到可能有更简单的解决方案(例如只是咬紧牙关并在代码中定义每个可能的数据配置,或者不使用django-tables2并自己动手),但我现在将此视为一个机会了解有关元编程的更多信息,所以我真的希望以这种方式完成这项工作。

知道我做错了什么吗?我的理论是__new__方法(在元类表使用中重新定义)在定义klass时被调用,而不是在实例化时调用,所以当我对属性进行处理时,为时已晚。但这违反了我对__new__何时应该发生的理解。否则,我很难理解元类__new__如何区分定义代码属性与动态定义属性之间的区别。

谢谢!

2 个答案:

答案 0 :(得分:4)

您在这里处于正确的轨道,但是您应该使用type()内置函数,而不是创建一个准系统类并向其添加属性。它不按照您的方式运行的原因是因为元类已经完成了它的工作。

使用type()允许您在设置基类时使用自己的属性构造新类。含义 - 您可以将您想要的字段描述为您的类的蓝图,允许Table元类在您定义后接管。

type()与django一起使用的{p> Here's an example。我已经将自己用于我自己的项目(有一些细微的变化)但它应该给你一个很好的起点,考虑到你已经差不多了。

def define_table(columns):
    attrs = dict((c, tables.Column()) for c in columns)
    klass = type('DynamicTable', (tables.Table,), attrs)
    return klass

答案 1 :(得分:1)

您将“常规”类的__new__与元类的__new__混淆。如您所知,Table依赖于元类上的__new__方法。确定类时确实调用了元类。该类本身是元类的实例,因此定义类是实例化元类。 (在这种情况下,TableDeclarativeColumnMetaClass的一个实例。)因此,在定义类时,为时已晚。

一种可能的解决方案是编写具有某种方法Table等的refreshColumns子类。你可以调整DeclarativeColumnMetaclass.__new__的代码,使refreshColumns再次做同样的魔术。然后,您可以在新课程上致电refreshColumns()