如何对Django进行猴补丁?
我看到了一篇关于在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
添加字段和功能。
不过,我还是想搞清楚这具体是怎么运作的:
我应该把猴子补丁的代码放在哪里呢?
这段代码是何时运行的——只运行一次?每次启动Python解释器时运行一次?还是每次请求时运行一次?
我想我还是需要修改数据库的结构。那么如果我删除了
User
表,然后运行./manage.py syncdb
,syncdb
会“知道”User
里新增了字段吗?如果不知道,那我该怎么修改数据库结构呢?
3 个回答
根据@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.
这个错误是因为导入的原因,而不是因为函数调用,所以这个导入必须放在函数内部。
把文件 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
你可以把它放在任何地方,但通常我们会在设置文件里(或者网址配置文件)看到这种东西。只要是可以放信号的地方,放这里也合适。这段代码其实应该更聪明一点,因为文件可能会被导入多次,这种情况下你就没办法避免问题了,所以如果你试图多次运行这样的代码,可能会出错。
这段代码需要在每个Python进程中至少执行一次。
是的,你需要手动修改数据库。syncdb 可能不会捕捉到这个变化(我没有仔细看代码),但可能有一些地方可以放这段代码,让它有效。
你似乎已经知道这样做是个糟糕透顶的主意,真实代码中绝对不应该这样做,所以我就不再强调了。这样做会导致代码中产生很难发现的bug,而且可能在未来的Django版本中不再有效。
另外,这样做和South不太兼容,而你应该使用South。