如何对Django进行猴补丁?

20 投票
3 回答
17075 浏览
提问于 2025-04-16 21:41

我看到了一篇关于在Django中使用“猴子补丁”的文章:

from django.contrib.auth.models import User

User.add_to_class('openid', models.CharField(max_length=250,blank=True))

def get_user_name(self):
    if self.first_name or self.last_name:
        return self.first_name + " " + self.last_name
    return self.username

User.add_to_class("get_user_name",get_user_name)

我明白这样做并不是最好的方法,通常更推荐通过一个单独的模型Profile来给User添加字段和功能。

不过,我还是想搞清楚这具体是怎么运作的:

  1. 我应该把猴子补丁的代码放在哪里呢?

  2. 这段代码是何时运行的——只运行一次?每次启动Python解释器时运行一次?还是每次请求时运行一次?

  3. 我想我还是需要修改数据库的结构。那么如果我删除了User表,然后运行./manage.py syncdbsyncdb会“知道”User里新增了字段吗?如果不知道,那我该怎么修改数据库结构呢?

3 个回答

2

根据@suhailvs和@Paul McMillan的建议,我在我的应用程序根目录下添加了一个patch.py文件,并在应用程序的apps.py文件中调用了我修改过的内容,放在ready信号里:

from django.apps import AppConfig

class MyAppConfig(AppConfig):
    name = "MyApp"

    def ready(self):
        """
        Called when the app is ready.
        """
        from .patch import patch_func

        patch_func()

出现AppRegistryNotReady: Apps aren't loaded yet.这个错误是因为导入的原因,而不是因为函数调用,所以这个导入必须放在函数内部。

20

把文件 monkey_patching.py 放到你的任意一个 apps 文件夹里,然后在这个应用的 __init__.py 文件中导入它。比如:

app/monkey_patching.py

#app/monkey_patching.py
from django.contrib.auth.models import User

User.add_to_class('openid', models.CharField(max_length=250,blank=True))

def get_user_name(self):
    if self.first_name or self.last_name:
        return self.first_name + " " + self.last_name
    return self.username

User.add_to_class("get_user_name",get_user_name)

app/__init__.py

#app/__init__.py
import monkey_patching
9

你可以把它放在任何地方,但通常我们会在设置文件里(或者网址配置文件)看到这种东西。只要是可以放信号的地方,放这里也合适。这段代码其实应该更聪明一点,因为文件可能会被导入多次,这种情况下你就没办法避免问题了,所以如果你试图多次运行这样的代码,可能会出错。

这段代码需要在每个Python进程中至少执行一次。

是的,你需要手动修改数据库。syncdb 可能不会捕捉到这个变化(我没有仔细看代码),但可能有一些地方可以放这段代码,让它有效。

你似乎已经知道这样做是个糟糕透顶的主意,真实代码中绝对不应该这样做,所以我就不再强调了。这样做会导致代码中产生很难发现的bug,而且可能在未来的Django版本中不再有效。

另外,这样做和South不太兼容,而你应该使用South。

撰写回答