覆盖python中的类方法

时间:2010-11-19 16:07:34

标签: python django

我想覆盖一个类方法,而不是从类创建子类/扩展。

一个例子:

from django.contrib import admin

class NewModelAdmin(admin.ModelAdmin):
    def formfield_for_dbfield(self, db_field, **kwargs):
        # some custom stuff

现在我不想更改从admin.ModelAdminNewModelAdmin的所有类(又称模型)。但我也不想修改原来的django代码。

有没有办法实现这个目标?

7 个答案:

答案 0 :(得分:4)

我不是100%清楚你想做什么,为什么你不想创建一个新的子类或者有一个不同名称的方法。

但是通常在python中你可以做类似的事情:

class MyClass(object):
    def print_hello(self):
        print "not hello"

def real_print_hello():
    print "hello"

x = MyClass()
x.print_hello() # "not hello"
setattr(x, "print_hello", real_print_hello)
x.print_hello() # "hello"

答案 1 :(得分:3)

答案 2 :(得分:3)

为了保持代码的可维护性,最好继续使用NewModelAdmin继承您的各个ModelAdmin类。这样,查看代码的其他开发人员(以及您,也许一两年后)可以清楚地看到自定义formfield_for_dbfield行为的来源,以便在需要时可以更新。如果你修补admin.ModelAdmin,它将使得追踪问题或在以后需要时改变行为变得更加困难。

答案 3 :(得分:2)

如果没有猴子修补,你的问题是可以解决的,这很有可能,这往往会产生意想不到的后果。

您如何使用django admin注册模型?

如果您使用这种方法:

admin.site.register(FooModel) #uses generic ModelAdmin

您遇到的问题是需要将此更改为NewModelAdmin子类的许多样板实例,如下所示:

class FooModelAdmin(NewModelAdmin):
    pass #does nothing except set up inheritance
admin.site.register(FooModel, FooModelAdmin)

这真的很冗长,如果你有很多模型可能需要花费很多时间来实现,所以通过编写包装函数以编程方式实现它:

def my_admin_register(model):
    class _newmodeladmin(ModelAdmin):
        def your_overridden_method(*args, **kwargs):
            #do whatever here
    admin.site.register(model, _newmodeladmin)

然后,您可以像这样使用:

my_admin_register(FooModel)

答案 4 :(得分:1)

您可以使用类setattr()上的monkey patching更改类方法。

答案 5 :(得分:0)

如果修改类中的方法,则修改以下行为:

  • 将其方法解析为该类的所有实例
  • 所有将其方法解析为该类的派生类

您的要求是互相排斥的。您不能在不影响将其方法解析为类的对象的情况下修改类的行为。

为了修改这些其他对象的行为,您需要创建 实例中的方法,以便它不会解决它在课堂上的方法。

另一种选择是依赖Python的鸭子打字。您不需要将对象与当前使用的对象直接相关。您可以重新实现界面,并在代码中交换旧类的调用。

这些技术在可维护性和设计方面存在权衡。换句话说,除非你没有其他选择,否则不要使用它们。

答案 6 :(得分:0)

我不是100%肯定你想要实现的目标,但我想你想要表现所有继承自models.ModelAdmin的管理员,而不必改变他们的声明。实现这一目标的唯一解决方案是对原始ModelAdmin类进行猴子修补,例如。类似的东西:

setattr(admin.ModelAdmin, 'form_field_for_dbfield', mymethod)

这肯定不是最值得推荐的方式,因为代码很难维护和其他东西。