如何在Django shell中重新加载模块?

123

我正在使用Django并经常使用Django shell。令人烦恼的是,当Django服务器在代码更改时重新加载时,shell不会重新加载,因此每次我更改正在测试的方法时,都需要退出shell并重新启动它,重新导入所有所需的模块,重新初始化所有所需的变量等等。即使iPython历史记录可以减少大量此类打字,但这仍然很麻烦。是否有一种方法使django shell自动重新加载,就像django开发服务器一样?

我知道reload()函数,但我导入了很多模型,并通常使用from app.models import *语法,因此reload()函数并不能解决我的问题。


2
你应该更新这个问题,将“django-extensions”答案标记为正确的。 - woodardj
1
只有当它真正对我起作用时,我才会使用它。我已经安装了扩展程序,但我的代码没有自动重新加载,而且在shell_plus文档中也没有提到自动重新加载。似乎在runserver_plus命令中有一个重载器,但那不是我要找的。 - Mad Wombat
12个回答

125

我建议使用IPython的autoreload扩展功能

./manage.py shell

In [1]: %load_ext autoreload
In [2]: %autoreload 2

从现在开始,在评估之前,所有导入的模块都将被刷新。

In [3]: from x import print_something
In [4]: print_something()
Out[4]: 'Something'

 # Do changes in print_something method in x.py file.

In [5]: print_something()
Out[5]: 'Something else'

在执行%load_ext autoreload命令之前导入其他内容也是有效的。

./manage.py shell
In [1]: from x import print_something
In [2]: print_something()
Out[2]: 'Something'

 # Do changes in print_something method in x.py file.

In [3]: %load_ext autoreload
In [4]: %autoreload 2
In [5]: print_something()
Out[5]: 'Something else'

使用%aimport命令和3个自动重新加载策略也可以防止一些导入的刷新:

%autoreload

  • 现在自动重新加载所有模块(除了由% aimport排除的模块)。

%autoreload 0

  • 禁用自动重新加载。

%autoreload 1

  • 每次执行输入的Python代码之前重新加载使用%aimport导入的所有模块。

%autoreload 2

  • 每次执行输入的Python代码之前重新加载所有模块(除了由% aimport排除的模块)。

%aimport

  • 列出要自动导入或不导入的模块。

%aimport foo

  • 导入模块'foo'并将其标记为%autoreload 1要自动重新加载的模块

%aimport -foo

  • 标记模块'foo'不会自动重新加载。

这通常对我的使用效果很好,但有一些限制:

  • 替换代码对象并不总是成功的:将类中的@property更改为普通方法或方法更改为成员变量可能会导致问题(但仅在旧对象中)。
  • 在重新加载模块之前从模块中删除的功能(例如通过猴子补丁)不会升级。
  • C扩展模块无法重新加载,因此也无法进行自动重新加载。

4
如果您正在使用 Django 的 /manage.py shell_plus,请注意输入 %load_ext autoreload 然后输入 %autoreload 2,这样可以自动重新加载模型。 - Fusion
太棒了,你让我的一天都变得美好了。 - Shamsul Arefin
请注意,您需要安装ipython才能运行此程序。例如,请参考https://dev59.com/v1YN5IYBdhLWcg3wm5Oi#47170195。 - Rabarberski
在哪里放置这个%autoreload?从答案中不清楚。 - IceFire
有没有办法使用/manage.py shell_plus自动化这个过程,如果我想将其设置为默认行为? - David Dahan

54

我的解决方案是编写代码并保存到文件,然后使用:

python manage.py shell < test.py

这样我就可以进行更改、保存并再次运行该命令,直到我修复了我要解决的问题。


4
简洁明了。需要注意的是,在py文件底部添加"exit()"以更清晰地退出Django shell。谢谢。 - Marc
简单而优雅的解决方案。干得好。 - nikoskip

46

我建议像dongweiming上述所说的那样使用django-extensions项目。但是不仅使用'shell_plus'管理命令,还要使用:

manage.py shell_plus --notebook

这将在你的网页浏览器中打开一个IPython笔记本。在单元格中编写您的代码,导入等并运行它。

当您更改模块时,只需单击笔记本菜单项“核心->重新启动”

完成了,您的代码现在正在使用您修改的模块。


43
“只需使用Python笔记本”不是对问题的回答。 - J__
1
这种方法虽然不像%autoreload那样自动化,但比autoreload更可靠。内核重启可以保证笔记本中的所有单元格都能完全重新加载模块。此外,如果您选择,还可以重新启动并运行所有单元格。 - Anton I. Sipos
我刚刚遇到了ModuleNotFoundError: No module named 'notebook.notebookapp'的错误。 - Akaisteph7

33

看看由django-extensions项目提供的manage.py shell_plus命令。它会在shell启动时加载所有模型文件,并自动重新加载您所做的任何修改,但不需要退出,您可以直接调用。


42
我使用shell_plus,但我的模型没有自动重新加载,我是否漏掉了什么? - Diego Ponciano
38
shell_plus无法重新加载模型,因此这并没有回答这个问题。 - allanberry
5
如所说,shell_plus 不会重新加载模型。 - zenperttu
我现在正在使用shell_plus,只要我更改代码,我的模型就会重新加载。我甚至刚刚更改了一个模型的定义,我的shell中的一个实例就捕捉到了这个变化。这应该被接受为正确答案。 - Joseph Sheedy
2
也许我漏掉了什么,但是 shell_plus 对我来说没有重新加载任何内容。它在启动时加载所有模型,这很方便,但就是这样。 - Mad Wombat
16
shell_plus可以使用autoreload模块重新加载模块。输入'%load_ext autoreload',然后'%autoreload 2' - 参见https://ipython.org/ipython-doc/3/config/extensions/autoreload.html - bbrame

26

看起来在这个话题上,普遍的共识是 Python 的 reload() 方法很糟糕,而且没有好的解决方法。


不正确。@dongweiming上面的答案是解决方案,应该被接受为最佳答案。 - Joseph Sheedy
3
他的回答收到了多条评论,指出shell_plus无法重新加载模型。 - Mad Wombat
1
我刚刚亲自测试了一下,它没有重新加载。另外,那些我手动导入的非模型代码怎么办? - Mad Wombat
我现在使用的应用程序中的类(Python 3.4.3,Django 1.9b1,django-extensions 1.5.9)重新加载非常好,包括一个普通的非Django模型模块和其中的类。这个答案已经有5年了,发生了很多开发。 - Joseph Sheedy
1
我在40分钟前的设置上尝试了一下,但shell_plus并没有重新加载任何内容。Django 1.7.10,Python 3.4.3,django-extensions 1.5.9。 - Mad Wombat
显示剩余6条评论

15

使用带有ipython配置的shell_plus。这将在shell_plus自动导入任何内容之前启用autoreload

pip install django-extensions
pip install ipython
ipython profile create

编辑您的IPython配置文件(~/.ipython/profile_default/ipython_config.py):

c.InteractiveShellApp.exec_lines = ['%autoreload 2']
c.InteractiveShellApp.extensions = ['autoreload']

打开一个 shell - 注意不需要包含 --ipython

python manage.py shell_plus

现在任何定义在 SHELL_PLUS_PRE_IMPORTSSHELL_PLUS_POST_IMPORTS文档)中的内容都会自动重新加载!请注意,如果你的shell在调试器中(例如pdb.set_trace()),当你保存一个文件时它可能会干扰重新加载。

3
谢谢你的回答。不过你可能注意到,我是在2010年提出这个问题的。很高兴知道经过八年的时间,自动重新加载终于在shell_plus中起作用了 :) - Mad Wombat
我尝试了这个,但它没有起作用,我还添加到了 SHELL_PLUS_PRE_IMPORTSSHELL_PLUS_POST_IMPORTS - Santhosh
这非常整洁。 - zrbecker
仍然会出现警告:RuntimeWarning: 模型 'xxx.xxxxxx' 已经注册。不建议重新加载模型,因为这可能导致不一致性,尤其是与相关模型之间的不一致性。请注意。 - Akaisteph7

8

我对这个不便的解决方案如下。我正在使用IPython。

$ ./manage.py shell
> import myapp.models as mdls   # 'mdls' or whatever you want, but short...
> mdls.SomeModel.objects.get(pk=100)
> # At this point save some changes in the model
> reload(mdls)
> mdls.SomeModel.objects.get(pk=100)

对于Python 3.x,必须使用以下方式导入'reload':

from importlib import reload

希望能够帮助。当然,这是用于调试目的的。 祝好。

4

3

结合两个答案的方法,我得出了一种简单的一行代码解决方案。

您可以使用 -c 运行django shell,它将运行您传递的命令,但是在代码执行后立即退出。

诀窍是设置所需内容,运行code.interact(local=locals()),然后从您传递的代码中重新启动shell。像这样:

python manage.py shell -c 'import uuid;test="mytestvar";import code;code.interact(local=locals())'

对我来说,我只需要使用丰富库的检查方法。仅需几行代码:

python manage.py shell -c 'import code;from rich import pretty;pretty.install();from rich import inspect;code.interact(local=locals())'

最后点睛之笔是一个别名(alias)

alias djshell='python manage.py shell -c "import code;from rich import pretty;pretty.install();from rich import inspect;code.interact(local=locals())"'

现在,如果我启动我的shell并想要检查表单类,我会得到这样一个美丽的输出:

enter image description here

我喜欢注入Rich的想法。然而,code.interact(local=locals()) 部分似乎无法与ipython一起使用。当我使用 shell_plus 而不是 shell 运行与您相同的命令时,我仍然得到一个普通的shell而不是ipython shell。 - David Dahan

1

虽然不完全符合您的需求,但我现在倾向于构建自己的管理命令来进行测试和调试。

在命令中,您可以按照自己的方式设置一堆本地变量,然后进入交互式 shell。

import code

class Command(BaseCommand):
  def handle(self, *args, **kwargs):
     foo = 'bar'
     code.interact(local=locals())

没有重新加载页面,但有一种简单且不那么烦人的交互式测试Django功能的方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接