如何通过“manage.py shell”在交互式解释器中重新加载Django模型模块?
我知道如何在普通的Python解释器中重新加载一个常规的Python模块。这个问题的解决方法在这里讲得很清楚:
但不知为什么,我在Django的“manage.py shell”解释器中遇到了麻烦。要重现我的问题,可以从这里开始基本的Django教程:
在创建了“polls”应用和“Poll”类后,通过“manage.py shell”启动解释器,并将“polls”应用导入其中。
import polls.models as pm
创建一个新的“Poll”对象:
p = pm.Poll()
到目前为止一切都很好。现在回到你的代码,添加任意一个方法或属性。例如,我添加了:
def x(self):
return 2+2
然后回到解释器中“重新加载”这个模块:
reload(pm)
现在尝试使用你新添加的方法或属性:
p1 = pm.Poll()
p1.x()
你会看到这个消息:
'Poll' object has no attribute 'x'
这是怎么回事?我还尝试过重新运行导入命令,用不同的语法导入模块,删除所有与“Poll”对象或“Poll”类相关的引用。我还尝试过在IPython解释器和普通的Python(v2.6)解释器中这样做,但都没有成功。
在普通的解释器会话中使用相同的方法对任意Python模块是完全有效的。就是在Django的“shell”会话中我就是搞不定。
顺便说一下,如果有影响的话,我是在一台Ubuntu 9.04的机器上进行的。
9 个回答
你还可以使用django-extensions这个项目,方法是输入以下命令:
manage.py shell_plus --notebook
这样会在你的网页浏览器中打开一个IPython笔记本,而不是在IPython命令行中。你可以在这里写代码并运行它。
当你修改了模块后,只需点击网页菜单中的'Kernel->Restart'选项。
重新运行代码时,就会使用你修改过的模块。
我在2016年的解决方案(未来可能会有所变化)
1. 安装django_extension
2. 添加以下设置:
SHELL_PLUS = 'ipython'
IPYTHON_ARGUMENTS = [
'--ext', 'autoreload',
]
3. 运行shell
./manage.py shell_plus
查看结果:
模型示例
class Notification(models.Model):
........
@classmethod
def get_something(self):
return 'I am programmer'
在shell中
In [1]: Notification.get_something()
Out[1]: 'I am programmer'
对模型进行了更改
@classmethod
def get_something(self):
return 'I am Python programmer'
在shell中
# shell does not display changes
In [2]: Notification.get_something()
Out[2]: 'I am programmer'
在shell中。这是个魔法
# configure extension of ipython
In [3]: %autoreload 2
在shell中
# try again - all worked
In [4]: Notification.get_something()
Out[4]: 'I am Python programmer'
再次进行了更改
@classmethod
def get_something(self):
return 'I am full-stack Python programmer'
在shell中
# all worked again
In [5]: Notification.get_something()
Out[5]: 'I am full-stack Python programmer'
缺点: 1. 需要手动运行代码
%autoreload 2
因为django_extension 1.7不支持运行任意代码。未来的版本可能会有这个功能。
注意事项:
- Django 1.10
- Python 3.4
- django_extension 1.7.4
- 主要基于 https://django-extensions.readthedocs.io/en/latest/shell_plus.html 和 http://ipython.readthedocs.io/en/stable/config/extensions/autoreload.html
- 注意。如果你尝试在使用super()的地方更改代码,可能会产生错误。
好吧,我觉得我得回答这个问题。问题在于,Django会把它的模型缓存到一个叫做AppCache的地方,这个地方有点像单例模式。简单来说,要重新加载Django的模型,你首先需要重新加载并重新导入所有存储在AppCache里的模型模块。然后,你还得清空这个AppCache。下面是实现这个功能的代码:
import os
from django.db.models.loading import AppCache
cache = AppCache()
curdir = os.getcwd()
for app in cache.get_apps():
f = app.__file__
if f.startswith(curdir) and f.endswith('.pyc'):
os.remove(f)
__import__(app.__name__)
reload(app)
from django.utils.datastructures import SortedDict
cache.app_store = SortedDict()
cache.app_models = SortedDict()
cache.app_errors = {}
cache.handled = {}
cache.loaded = False
我把这些代码放在一个叫做reloadmodels.py的文件里,这个文件在我Django网站的根目录下。通过使用IPython,我可以通过运行以下命令来重新加载所有内容:
%run ~/mysite/reloadmodels.py