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

时间:2009-05-21 00:38:17

标签: python django

我知道如何在常规Python解释器会话中重新加载常规Python模块。这个问题记录了如何做得很好:

How do I unload (reload) a Python module?

出于某种原因,我在Django的“manage.py shell”解释器会话中遇到了麻烦。要重新创建我的问题,请启动此处的基本Django教程:

Writing your first Django app, part 1

创建“民意调查”应用程序和“民意调查”类后,通过“manage.py shell”启动解释器并将“民意调查”应用程序导入其中。

import polls.models as pm

创建一个新的“民意调查”对象:

p = pm.Poll()
到目前为止一切顺利。现在回到您的源代码并添加任意方法或属性。例如,我添加了:

def x(self):
    return 2+2

现在回到解释器并“重新加载”模块:

reload(pm)

现在尝试使用您的新方法或属性:

p1 = pm.Poll()
p1.x()

你会收到这条消息:

'Poll' object has no attribute 'x'

是什么给出的?我还尝试重新运行import命令,使用不同的语法导入模块,删除对任何“Poll”对象或“Poll”类的所有引用。我也尝试过使用IPython解释器和普通的Python(v2.6)解释器。似乎没什么用。

在常规解释器会话中使用与任意Python模块相同的技术可以很好地工作。我似乎无法让它在Django的“shell”会话中工作。

顺便说一句,如果它有任何区别,我在Ubuntu 9.04机器上这样做。

9 个答案:

答案 0 :(得分:38)

嗯,我想我必须回答这个问题。问题是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

我已将所有这些放在我的Django站点的根目录中名为reloadmodels.py的单独文件中。使用IPython我可以通过运行来重新加载所有内容:

%run ~/mysite/reloadmodels.py

答案 1 :(得分:6)

您还可以使用以下命令使用django-extensions项目:

manage.py shell_plus --notebook

这将在您的Web浏览器上打开一个IPython笔记本,而不是IPython shell解释器。在那里编写代码并运行它。

更改模块时,只需单击网页菜单项'Kernel-> Restart'

现在重新运行代码会使用您修改过的模块。

答案 2 :(得分:5)

就我而言,上述解决方案都没有自行解决,this线程本身也没有多大帮助,但在结合这些方法后,我设法重新加载我的模型{ {3}}:

  1. 更改模型(MyModel)
  2. 删除models.pyc
  3. 清理Django模型缓存(如shell_plus):

     from django.db.models.loading import AppCache
     cache = AppCache()
     from django.utils.datastructures import SortedDict
     cache.app_store = SortedDict()
     cache.app_models = SortedDict()
     cache.app_errors = {}
     cache.handled = {}
     cache.loaded = False
    
  4. 重新加载here

    之类的模型
    reload(project.app.models)
    from project.app.models import MyModel
    

答案 3 :(得分:5)

2016年我的解决方案(将来可能会更改)

1.安装django_extension

2.添加下一个设置:

SHELL_PLUS = 'ipython'

IPYTHON_ARGUMENTS = [
    '--ext', 'autoreload',
]

3.Run 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. 基于({3}}和https://django-extensions.readthedocs.io/en/latest/shell_plus.html
  5. 的(主要)
  6. 注意。如果您尝试更改使用super()的代码,则可能会产生错误。

答案 4 :(得分:5)

假设您的项目以这种方式设置

  • 项目名称:书店
  • app name:shelf
  • 型号名称:书籍

首次加载

from bookstore.shelf.models import Books

后续重新加载

import bookstore;reload(bookstore.shelf.models);from bookstore.shelf.models import Books

答案 5 :(得分:3)

ipython console每个reload()表达式都会deep reload;当然还增加了许多其他有用的东西。

答案 6 :(得分:2)

来自Seti Volkylanypv

的答案
  1. 安装IPython:pip install ipython
  2. 运行python manage.py shell:行首的符号现在应为In [1]:(cmd为>>>
  3. 运行ipython profile create
  4. 进入~/.ipython/profile_default/ipython_config.py并在文本编辑器中打开它,并在最后添加这两行:

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

  5. 您现在可以运行python manage.py shell,无需编写%autoreload 2

    即可修改模型

答案 7 :(得分:1)

在导入任何代码之前启用IPython自动加载扩展:

%load_ext autoreload
%autoreload 2

我将它与常规的django shell一起使用,虽然它确实有一些局限性,但它可以很好地工作:

<强> *注意事项:

以可靠的方式重新加载Python模块通常很困难,并且可能会发生意外情况。 %autoreload尝试通过用新版本替换模块中先前的函数代码对象和类的部分来解决常见的陷阱。这使得以下工作成为可能:

  • 重新加载'xxx'时,通过'from xxx import foo'导入的函数和类将升级为新版本。
  • 类的方法和属性在重新加载时升级,因此在重新加载之前在对象'c'上调用'c.foo()'会导致执行'foo'的新代码。

一些已知的警告是:

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

来源:https://ipython.org/ipython-doc/3/config/extensions/autoreload.html#caveats

另一个很好的选择是在单独的脚本中编写代码并将其发送到django shell,如下所示:

manage.py shell < my_script.py

答案 8 :(得分:0)

我无法获得上述任何解决方案,但我确实想出了一个解决方法,用于在我的django项目中重新加载任何其他非模型模块(例如functions.py或{{1模块)。

  1. 创建名为views.py的文件。我将它存储在我的开发机器上的django项目的reimport_module.py文件夹中。

    local/
  2. 启动shell plus(使用嵌入式IPython shell):

    # Desc: Imports the module with the name passed in, or imports it for first # time if it hasn't already been imported. # # Purpose of this script is to speed up development of functions that # are written in an external editor then tested in IPython. # # Without this script you have to exit & reenter IPython then redo # import statements, definitions of local variables, etc. # # Note: doesn't work for Django models files, because Django caches # them in a structure called AppCache. # # Args: module to reload (string) import sys module_to_reload = sys.argv[1] # Attempt to pop module try: sys.modules.pop(module_to_reload) print 'reimporting...' except KeyError: print 'importing for first time...' # (re)import module import_str = 'from {0} import *'.format(module_to_reload) exec(import_str)

  3. 使用以下命令导入您正在开发的模块:

    python manage.py shell_plus

  4. 使用IPython测试模块中的函数。

  5. 在外部编辑器中更改模块。
  6. 使用以下命令重新导入模块,而无需退出&amp;重新进入IPython:

    %run local/reimport_module.py 'your.module'

    注意:此命令已在步骤3中使用,因此您可以键入%run local/reimport_module.py 'your.module',然后点击向上箭头以自动填充它。