summaryrefslogtreecommitdiff
path: root/django/core/management/commands/shell.py
diff options
context:
space:
mode:
Diffstat (limited to 'django/core/management/commands/shell.py')
-rw-r--r--django/core/management/commands/shell.py84
1 files changed, 79 insertions, 5 deletions
diff --git a/django/core/management/commands/shell.py b/django/core/management/commands/shell.py
index f55b346406..1619561fea 100644
--- a/django/core/management/commands/shell.py
+++ b/django/core/management/commands/shell.py
@@ -2,7 +2,9 @@ import os
import select
import sys
import traceback
+from collections import defaultdict
+from django.apps import apps
from django.core.management import BaseCommand, CommandError
from django.utils.datastructures import OrderedSet
@@ -27,6 +29,11 @@ class Command(BaseCommand):
),
)
parser.add_argument(
+ "--no-imports",
+ action="store_true",
+ help="Disable automatic imports of models.",
+ )
+ parser.add_argument(
"-i",
"--interface",
choices=self.shells,
@@ -47,18 +54,27 @@ class Command(BaseCommand):
def ipython(self, options):
from IPython import start_ipython
- start_ipython(argv=[])
+ start_ipython(
+ argv=[],
+ user_ns=self.get_and_report_namespace(
+ options["verbosity"], options["no_imports"]
+ ),
+ )
def bpython(self, options):
import bpython
- bpython.embed()
+ bpython.embed(
+ self.get_and_report_namespace(options["verbosity"], options["no_imports"])
+ )
def python(self, options):
import code
# Set up a dictionary to serve as the environment for the shell.
- imported_objects = {}
+ imported_objects = self.get_and_report_namespace(
+ options["verbosity"], options["no_imports"]
+ )
# We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system
# conventions and get $PYTHONSTARTUP first then .pythonrc.py.
@@ -111,10 +127,68 @@ class Command(BaseCommand):
# Start the interactive interpreter.
code.interact(local=imported_objects)
+ def get_and_report_namespace(self, verbosity, no_imports=False):
+ if no_imports:
+ return {}
+
+ namespace = self.get_namespace()
+
+ if verbosity < 1:
+ return namespace
+
+ amount = len(namespace)
+ msg = f"{amount} objects imported automatically"
+
+ if verbosity < 2:
+ self.stdout.write(f"{msg} (use -v 2 for details).", self.style.SUCCESS)
+ return namespace
+
+ imports_by_module = defaultdict(list)
+ for obj_name, obj in namespace.items():
+ if hasattr(obj, "__module__") and (
+ (hasattr(obj, "__qualname__") and obj.__qualname__.find(".") == -1)
+ or not hasattr(obj, "__qualname__")
+ ):
+ imports_by_module[obj.__module__].append(obj_name)
+ if not hasattr(obj, "__module__") and hasattr(obj, "__name__"):
+ tokens = obj.__name__.split(".")
+ if obj_name in tokens:
+ module = ".".join(t for t in tokens if t != obj_name)
+ imports_by_module[module].append(obj_name)
+
+ import_string = "\n".join(
+ [
+ f" from {module} import {objects}"
+ for module, imported_objects in imports_by_module.items()
+ if (objects := ", ".join(imported_objects))
+ ]
+ )
+
+ try:
+ import isort
+ except ImportError:
+ pass
+ else:
+ import_string = isort.code(import_string)
+
+ self.stdout.write(
+ f"{msg}, including:\n\n{import_string}", self.style.SUCCESS, ending="\n\n"
+ )
+
+ return namespace
+
+ def get_namespace(self):
+ apps_models = apps.get_models()
+ namespace = {}
+ for model in reversed(apps_models):
+ if model.__module__:
+ namespace[model.__name__] = model
+ return namespace
+
def handle(self, **options):
# Execute the command and exit.
if options["command"]:
- exec(options["command"], globals())
+ exec(options["command"], {**globals(), **self.get_namespace()})
return
# Execute stdin if it has anything to read and exit.
@@ -124,7 +198,7 @@ class Command(BaseCommand):
and not sys.stdin.isatty()
and select.select([sys.stdin], [], [], 0)[0]
):
- exec(sys.stdin.read(), globals())
+ exec(sys.stdin.read(), {**globals(), **self.get_namespace()})
return
available_shells = (