如何通过“manage.py shell”在交互式解释器中重新加载Django模型模块?

53 投票
9 回答
25568 浏览
提问于 2025-04-15 11:44

我知道如何在普通的Python解释器中重新加载一个常规的Python模块。这个问题的解决方法在这里讲得很清楚:

如何卸载(重新加载)一个Python模块?

但不知为什么,我在Django的“manage.py shell”解释器中遇到了麻烦。要重现我的问题,可以从这里开始基本的Django教程:

编写你的第一个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 个回答

6

你还可以使用django-extensions这个项目,方法是输入以下命令:

manage.py shell_plus --notebook

这样会在你的网页浏览器中打开一个IPython笔记本,而不是在IPython命令行中。你可以在这里写代码并运行它。

当你修改了模块后,只需点击网页菜单中的'Kernel->Restart'选项。

重新运行代码时,就会使用你修改过的模块。

7

我在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不支持运行任意代码。未来的版本可能会有这个功能。

注意事项:

  1. Django 1.10
  2. Python 3.4
  3. django_extension 1.7.4
  4. 主要基于 https://django-extensions.readthedocs.io/en/latest/shell_plus.htmlhttp://ipython.readthedocs.io/en/stable/config/extensions/autoreload.html
  5. 注意。如果你尝试在使用super()的地方更改代码,可能会产生错误。
39

好吧,我觉得我得回答这个问题。问题在于,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

撰写回答