开发者

How to reload modules in django shell?

开发者 https://www.devze.com 2023-01-17 08:43 出处:网络
I am working with Django and use Django shell all the time. The annoying part is that while the Django server reloads on code changes, the shell does not, so every time I make a change to a method I a

I am working with Django and use Django shell all the time. The annoying part is that while the Django server reloads on code changes, the shell does not, so every time I make a change to a method I am testing, I need to quit the shell and restart it, re-import all the modules I need, reinitialize all the variables I need etc. While iPython history saves a lot of typing on this, this is still a pain. Is there a way to make django shell auto-reload, the same way django development server does?

I know about reload(), but I import a lot of models and generally use from app.models import * syntax,开发者_运维技巧 so reload() is not much help.


I'd suggest use IPython autoreload extension.

./manage.py shell

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

And from now all imported modules would be refreshed before evaluate.

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'

Works also if something was imported before %load_ext autoreload command.

./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'

There is possible also prevent some imports from refreshing with %aimport command and 3 autoreload strategies:

%autoreload

  • Reload all modules (except those excluded by %aimport) automatically now.

%autoreload 0

  • Disable automatic reloading.

%autoreload 1

  • Reload all modules imported with %aimport every time before executing the Python code typed.

%autoreload 2

  • Reload all modules (except those excluded by %aimport) every time before executing the Python code typed.

%aimport

  • List modules which are to be automatically imported or not to be imported.

%aimport foo

  • Import module ‘foo’ and mark it to be autoreloaded for %autoreload 1

%aimport -foo

  • Mark module ‘foo’ to not be autoreloaded.

This generally works good for my use, but there are some cavetas:

  • Replacing code objects does not always succeed: changing a @property in a class to an ordinary method or a method to a member variable can cause problems (but in old objects only).
  • Functions that are removed (eg. via monkey-patching) from a module before it is reloaded are not upgraded.
  • C extension modules cannot be reloaded, and so cannot be autoreloaded.


My solution to it is I write the code and save to a file and then use:

python manage.py shell < test.py

So I can make the change, save and run that command again till I fix whatever I'm trying to fix.


I recommend using the django-extensions project like stated above by dongweiming. But instead of just 'shell_plus' management command, use:

manage.py shell_plus --notebook

This will open a IPython notebook on your web browser. Write your code there in a cell, your imports etc. and run it.

When you change your modules, just click the notebook menu item 'Kernel->Restart'

There you go, your code is now using your modified modules.


look at the manage.py shell_plus command provided by the django-extensions project. It will load all your model files on shell startup. and autoreload your any modify but do not need exit, you can direct call there


It seems that the general consensus on this topic, is that python reload() sucks and there is no good way to do this.


Use shell_plus with an ipython config. This will enable autoreload before shell_plus automatically imports anything.

pip install django-extensions
pip install ipython
ipython profile create

Edit your ipython profile (~/.ipython/profile_default/ipython_config.py):

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

Open a shell - note that you do not need to include --ipython:

python manage.py shell_plus

Now anything defined in SHELL_PLUS_PRE_IMPORTS or SHELL_PLUS_POST_IMPORTS (docs) will autoreload!

Note that if your shell is at a debugger (ex pdb.set_trace()) when you save a file it can interfere with the reload.


My solution for this inconvenient follows. I am using 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)

For Python 3.x, 'reload' must be imported using:

from importlib import reload

Hope it helps. Of course it is for debug purposes.

Cheers.


Reload() doesn't work in Django shell without some tricks. You can check this thread na and my answer specifically:

How do you reload a Django model module using the interactive interpreter via "manage.py shell"?


Using a combination of 2 answers for this I came up with a simple one line approach.

You can run the django shell with -c which will run the commands you pass however it quits immediately after the code is run.

The trick is to setup what you need, run code.interact(local=locals()) and then re-start the shell from within the code you pass. Like this:

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

For me I just wanted the rich library's inspect method. Only a few lines:

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

Finally the cherry on top is an alias

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

Now if I startup my shell and say, want to inspect the form class I get this beautiful output:

How to reload modules in django shell?


Instead of running commands from the Django shell, you can set up a management command like so and rerun that each time.


Not exactly what you want, but I now tend to build myself management commands for testing and fiddling with things.

In the command you can set up a bunch of locals the way you want and afterwards drop into an interactive shell.

import code

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

No reload, but an easy and less annoying way to interactively test django functionality.


import test  // test only has x defined
test.x       // prints 3, now add y = 4 in test.py
test.y       // error, test does not have attribute y

solution Use reload from importlib as follows

from importlib import reload
import test // test only has x defined
test.x // prints 3, now add y = 4 in test.py
test.y // error
reload(test)
test.y // prints 4
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号