【问题标题】:How do you reload a Django model module using the interactive interpreter via "manage.py shell"?如何通过“manage.py shell”使用交互式解释器重新加载 Django 模型模块?
【发布时间】:2010-10-27 19:35:20
【问题描述】:

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

How do I unload (reload) a Python module?

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

Writing your first Django app, part 1

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

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'

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

在常规解释器会话中对任意 Python 模块使用相同的技术非常有效。我似乎无法让它在 Django 的“shell”会话中工作。

顺便说一句,如果有什么不同的话,我是在 Ubuntu 9.04 机器上执行此操作的。

【问题讨论】:

  • 刚刚在 Windows 机器上运行相同的测试并得到相同的结果
  • 是的,我都试过了,也在等待答案

标签: python django


【解决方案1】:

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

【讨论】:

  • 我会在脚本的开头添加以下两行:import os;os.system('del *.pyc /s >NUL') 或 linux 的等效项
  • 添加这个会处理 models.pyc 的移除:\nimport os\ncwd = os.getcwd()\n\n然后在循环中使用这个测试:\nif cwd in app.__file__:os.remove(app.__file__)
  • 请记住,这并不能修复 ModelForms,它们有一些额外的缓存内容以 ModelFormMetaclass 的形式进行,当您重新加载模型时不会重做这些内容,因此例如 blank=True/对于您重新加载的任何模型,可能无法在 ModelForms 中正确处理 False。
  • 这似乎不适用于使用 Django 1.6 的我。似乎至少应该将cache.handled 设置为set()(否则你甚至不能运行 reloadmodels.py 两次)。
  • 我确认@blueyed 所说的:)
【解决方案2】:

我在 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'

在外壳中

In [1]: Notification.get_something()
Out[1]: 'I am programmer'

对模型进行了更改

@classmethod
    def get_something(self):

        return 'I am Python programmer'

在外壳中

# shell does not display changes
In [2]: Notification.get_something()
Out[2]: 'I am programmer'

在外壳中。这是一个魔法

# configure extension of ipython
In [3]: %autoreload 2

在外壳中

# 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'

在外壳中

# 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() 的代码,则可能会产生错误。

【讨论】:

【解决方案3】:

您也可以通过以下命令使用 django-extensions 项目:

manage.py shell_plus --notebook

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

当你改变你的模块时,只需点击网页菜单项'Kernel->Restart'

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

【讨论】:

    【解决方案4】:

    假设你的项目是这样设置的

    • 项目名称:书店
    • 应用名称:货架
    • 型号名称:书籍

    第一次加载

    from bookstore.shelf.models import Books
    

    后续重载

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

    【讨论】:

      【解决方案5】:

      就我而言,上述解决方案都没有单独工作,this 线程本身也没有太大帮助,但在结合这些方法后,我设法在shell_plus 中重新加载我的模型:

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

         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
        

      【讨论】:

        【解决方案6】:

        ipython console 对每个 reload() 表达式执行 deep reload;当然还添加了很多其他有用的东西。

        【讨论】:

        • 尝试后,会弹出一个单独的问题。我得到一个 ImportError: No module named django.models
        • Ubuntu 9.04 有 Python2.6 作为最新版本。你需要输入命令python2.5 manage.py runserver。 (如果你通过easy_install安装了django,请确保在python2.5安装上安装它)
        • dreload() 使用 python2.5 manage.py runserver 给出相同的 ImportError
        【解决方案7】:

        来自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']

        您现在可以运行python manage.py shell,编辑您的模型而无需编写%autoreload 2

        【讨论】:

          【解决方案8】:

          在导入任何代码之前启用 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
          

          【讨论】:

            【解决方案9】:

            我无法让上述任何解决方案发挥作用,但我确实想出了一个解决方法来重新加载我的 django 项目中的任何其他非模型模块(例如 functions.pyviews.py 模块) .

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

              # 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) 
              
            2. 启动 shell plus(使用嵌入式 IPython shell):

              python manage.py shell_plus

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

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

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

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

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

              注意:此命令已在步骤 3 中使用,因此您可以输入%run,然后输入向上箭头以自动完成。

            【讨论】:

              猜你喜欢
              • 2013-07-31
              • 2011-04-15
              • 2011-04-14
              • 2012-05-20
              • 2012-09-17
              • 2018-01-28
              • 2012-08-28
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多