mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +00:00
Made changes requested following the forum discussion:
- Sorted the imports using isort. - Removed modules from imports. - Changed informative output. - Added no-imports flag.
This commit is contained in:
parent
1f7e972485
commit
aedd8ae6c1
@ -27,6 +27,11 @@ class Command(BaseCommand):
|
||||
"variable and ~/.pythonrc.py script."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-imports",
|
||||
action="store_true",
|
||||
help="Disable automatic imports of models.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--interface",
|
||||
@ -50,19 +55,25 @@ class Command(BaseCommand):
|
||||
|
||||
start_ipython(
|
||||
argv=[],
|
||||
user_ns=self.get_and_report_namespace(options["verbosity"]),
|
||||
user_ns=self.get_and_report_namespace(
|
||||
options["verbosity"], options["no_imports"]
|
||||
),
|
||||
)
|
||||
|
||||
def bpython(self, options):
|
||||
import bpython
|
||||
|
||||
bpython.embed(self.get_and_report_namespace(options["verbosity"]))
|
||||
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 = self.get_and_report_namespace(options["verbosity"])
|
||||
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.
|
||||
@ -115,17 +126,17 @@ class Command(BaseCommand):
|
||||
# Start the interactive interpreter.
|
||||
code.interact(local=imported_objects)
|
||||
|
||||
def get_and_report_namespace(self, verbosity):
|
||||
namespace = self.get_namespace()
|
||||
def get_and_report_namespace(self, verbosity, no_imports=False):
|
||||
namespace = {} if no_imports else self.get_namespace()
|
||||
|
||||
if verbosity >= 1:
|
||||
self.stdout.write(
|
||||
f"{len(namespace)} objects imported automatically",
|
||||
f"Automatic imports: {len(namespace)} objects imported"
|
||||
" (use -v 2 for details)",
|
||||
self.style.SUCCESS,
|
||||
)
|
||||
if verbosity >= 2:
|
||||
imports_by_module = {}
|
||||
imports_by_alias = {}
|
||||
for obj_name, obj in namespace.items():
|
||||
if hasattr(obj, "__module__") and (
|
||||
(hasattr(obj, "__qualname__") and obj.__qualname__.find(".") == -1)
|
||||
@ -141,34 +152,30 @@ class Command(BaseCommand):
|
||||
module = ".".join(tokens)
|
||||
collected_imports = imports_by_module.get(module, [])
|
||||
imports_by_module[module] = collected_imports + [obj_name]
|
||||
else:
|
||||
module = ".".join(tokens)
|
||||
imports_by_alias[module] = obj_name
|
||||
|
||||
import_string = ""
|
||||
for module, imported_objects in imports_by_module.items():
|
||||
self.stdout.write(
|
||||
f"from {module} import {', '.join(imported_objects)}",
|
||||
self.style.SUCCESS,
|
||||
import_string += (
|
||||
f"\tfrom {module} import {', '.join(imported_objects)}\n"
|
||||
)
|
||||
for module, alias in imports_by_alias.items():
|
||||
self.stdout.write(f"import {module} as {alias}", self.style.SUCCESS)
|
||||
|
||||
try:
|
||||
import isort
|
||||
|
||||
import_string = isort.code(import_string)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
self.stdout.write(import_string, self.style.SUCCESS)
|
||||
|
||||
return namespace
|
||||
|
||||
def get_namespace(self):
|
||||
apps_models = apps.get_models()
|
||||
apps_models_modules = [
|
||||
(app.models_module, app.label)
|
||||
for app in apps.get_app_configs()
|
||||
if app.models_module is not None
|
||||
]
|
||||
namespace = {}
|
||||
for model in reversed(apps_models):
|
||||
if model.__module__:
|
||||
namespace[model.__name__] = model
|
||||
for app_models_module, app_label in apps_models_modules:
|
||||
if f"{app_label}_models" not in namespace:
|
||||
namespace[f"{app_label}_models"] = app_models_module
|
||||
return namespace
|
||||
|
||||
def handle(self, **options):
|
||||
|
@ -1070,11 +1070,9 @@ All models from all installed apps are automatically imported. See the guide on
|
||||
remove imports.
|
||||
|
||||
Models from apps listed earlier in :setting:`INSTALLED_APPS` take precedence.
|
||||
You can access overridden models from later, lower-precedence apps through its
|
||||
models module, which is made available as ``<app_label>_models``, for example
|
||||
``biblio_models.Book`` to access a model called ``Book`` in an app called
|
||||
``biblio``. For a ``--verbosity`` of 2 and above, all imported functions and
|
||||
classes are shown.
|
||||
For a ``--verbosity`` of 2 and above, all imported functions and classes
|
||||
are shown. If you don't want to import any model use the flag
|
||||
``--no-imports``.
|
||||
|
||||
.. versionchanged:: 5.2
|
||||
|
||||
|
@ -3,9 +3,7 @@ import unittest
|
||||
from unittest import mock
|
||||
|
||||
from django import __version__
|
||||
from django.contrib.auth import models as auth_model_module
|
||||
from django.contrib.auth.models import Group, Permission, User
|
||||
from django.contrib.contenttypes import models as contenttypes_model_module
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.management import CommandError, call_command
|
||||
from django.core.management.commands import shell
|
||||
@ -89,7 +87,7 @@ class ShellCommandTestCase(SimpleTestCase):
|
||||
mock_ipython = mock.Mock(start_ipython=mock.MagicMock())
|
||||
|
||||
with mock.patch.dict(sys.modules, {"IPython": mock_ipython}):
|
||||
cmd.ipython({"verbosity": 0})
|
||||
cmd.ipython({"verbosity": 0, "no_imports": False})
|
||||
|
||||
self.assertEqual(
|
||||
mock_ipython.start_ipython.mock_calls,
|
||||
@ -110,7 +108,7 @@ class ShellCommandTestCase(SimpleTestCase):
|
||||
mock_bpython = mock.Mock(embed=mock.MagicMock())
|
||||
|
||||
with mock.patch.dict(sys.modules, {"bpython": mock_bpython}):
|
||||
cmd.bpython({"verbosity": 0})
|
||||
cmd.bpython({"verbosity": 0, "no_imports": False})
|
||||
|
||||
self.assertEqual(
|
||||
mock_bpython.embed.mock_calls, [mock.call(cmd.get_and_report_namespace(0))]
|
||||
@ -130,7 +128,7 @@ class ShellCommandTestCase(SimpleTestCase):
|
||||
mock_code = mock.Mock(interact=mock.MagicMock())
|
||||
|
||||
with mock.patch.dict(sys.modules, {"code": mock_code}):
|
||||
cmd.python({"verbosity": 0, "no_startup": True})
|
||||
cmd.python({"verbosity": 0, "no_startup": True, "no_imports": False})
|
||||
|
||||
self.assertEqual(
|
||||
mock_code.interact.mock_calls,
|
||||
@ -160,9 +158,6 @@ class ShellCommandTestCase(SimpleTestCase):
|
||||
"Group": Group,
|
||||
"Permission": Permission,
|
||||
"User": User,
|
||||
"auth_models": auth_model_module,
|
||||
"contenttypes_models": contenttypes_model_module,
|
||||
"shell_models": shell_models,
|
||||
},
|
||||
)
|
||||
|
||||
@ -216,12 +211,29 @@ class ShellCommandTestCase(SimpleTestCase):
|
||||
"Group": Group,
|
||||
"Permission": Permission,
|
||||
"User": User,
|
||||
"auth_models": auth_model_module,
|
||||
"contenttypes_models": contenttypes_model_module,
|
||||
"shell_models": shell_models,
|
||||
},
|
||||
)
|
||||
|
||||
@override_settings(
|
||||
INSTALLED_APPS=["shell", "django.contrib.auth", "django.contrib.contenttypes"]
|
||||
)
|
||||
def test_no_imports_namespace(self):
|
||||
cmd = shell.Command()
|
||||
namespace = cmd.get_and_report_namespace(verbosity=0, no_imports=True)
|
||||
self.assertEqual(namespace, {})
|
||||
|
||||
@override_settings(
|
||||
INSTALLED_APPS=["shell", "django.contrib.auth", "django.contrib.contenttypes"]
|
||||
)
|
||||
def test_message_with_no_imports(self):
|
||||
with captured_stdout() as stdout:
|
||||
cmd = shell.Command()
|
||||
cmd.get_and_report_namespace(verbosity=1, no_imports=True)
|
||||
self.assertEqual(
|
||||
stdout.getvalue().strip(),
|
||||
"Automatic imports: 0 objects imported (use -v 2 for details)",
|
||||
)
|
||||
|
||||
@override_settings(
|
||||
INSTALLED_APPS=["shell", "django.contrib.auth", "django.contrib.contenttypes"]
|
||||
)
|
||||
@ -229,7 +241,10 @@ class ShellCommandTestCase(SimpleTestCase):
|
||||
with captured_stdout() as stdout:
|
||||
cmd = shell.Command()
|
||||
cmd.get_and_report_namespace(verbosity=1)
|
||||
self.assertEqual(stdout.getvalue().strip(), "9 objects imported automatically")
|
||||
self.assertEqual(
|
||||
stdout.getvalue().strip(),
|
||||
"Automatic imports: 6 objects imported (use -v 2 for details)",
|
||||
)
|
||||
|
||||
@override_settings(
|
||||
INSTALLED_APPS=["shell", "django.contrib.auth", "django.contrib.contenttypes"]
|
||||
@ -241,7 +256,8 @@ class ShellCommandTestCase(SimpleTestCase):
|
||||
self.assertEqual(stdout.getvalue().strip(), "")
|
||||
|
||||
@override_settings(INSTALLED_APPS=["shell", "django.contrib.contenttypes"])
|
||||
def test_message_with_stdout_listing_objects(self):
|
||||
@mock.patch.dict(sys.modules, {"isort": None})
|
||||
def test_message_with_stdout_listing_objects_with_isort_not_installed(self):
|
||||
class Command(shell.Command):
|
||||
def get_namespace(self):
|
||||
class MyClass:
|
||||
@ -261,38 +277,40 @@ class ShellCommandTestCase(SimpleTestCase):
|
||||
|
||||
self.assertEqual(
|
||||
stdout.getvalue().strip(),
|
||||
"7 objects imported automatically\n"
|
||||
"from django.contrib.contenttypes.models import ContentType\n"
|
||||
"from shell.models import Phone, Marker\n"
|
||||
"import shell.models as shell_models\n"
|
||||
"import django.contrib.contenttypes.models as contenttypes_models",
|
||||
"Automatic imports: 5 objects imported (use -v 2 for details)\n"
|
||||
"\tfrom django.contrib.contenttypes.models import ContentType\n"
|
||||
"\tfrom shell.models import Phone, Marker",
|
||||
)
|
||||
|
||||
@override_settings(
|
||||
INSTALLED_APPS=[
|
||||
"shell",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
]
|
||||
)
|
||||
def test_shell_has_no_empty_models_objects(self):
|
||||
cmd = shell.Command()
|
||||
namespace = cmd.get_namespace()
|
||||
@override_settings(INSTALLED_APPS=["shell", "django.contrib.contenttypes"])
|
||||
def test_message_with_stdout_listing_objects_with_isort(self):
|
||||
sorted_imports = (
|
||||
"\tfrom shell.models import Marker, Phone\n\n"
|
||||
"\tfrom django.contrib.contenttypes.models import ContentType"
|
||||
)
|
||||
mock_isort_code = mock.Mock(code=mock.MagicMock(return_value=sorted_imports))
|
||||
|
||||
self.assertNotIn("messages_models", namespace)
|
||||
self.assertNotIn("staticfiles_models", namespace)
|
||||
self.assertIsNotNone(namespace["auth_models"])
|
||||
self.assertIsNotNone(namespace["shell_models"])
|
||||
class Command(shell.Command):
|
||||
def get_namespace(self):
|
||||
class MyClass:
|
||||
pass
|
||||
|
||||
@override_settings(INSTALLED_APPS=["shell"])
|
||||
@isolate_apps("shell", kwarg_name="apps")
|
||||
def test_app_has_model_with_name_equal_module_name(self, apps):
|
||||
class shell_models(models.Model):
|
||||
pass
|
||||
constant = "constant"
|
||||
|
||||
cmd = shell.Command()
|
||||
return {
|
||||
**super().get_namespace(),
|
||||
"MyClass": MyClass,
|
||||
"constant": constant,
|
||||
}
|
||||
|
||||
with mock.patch("django.apps.apps.get_models", return_value=apps.get_models()):
|
||||
namespace = cmd.get_namespace()
|
||||
self.assertEqual(namespace["shell_models"], shell_models)
|
||||
with captured_stdout() as stdout:
|
||||
cmd = Command()
|
||||
with mock.patch.dict(sys.modules, {"isort": mock_isort_code}):
|
||||
cmd.get_and_report_namespace(verbosity=2)
|
||||
|
||||
self.assertEqual(
|
||||
stdout.getvalue().strip(),
|
||||
"Automatic imports: 5 objects imported (use -v 2 for details)\n"
|
||||
"\tfrom shell.models import Marker, Phone\n\n"
|
||||
"\tfrom django.contrib.contenttypes.models import ContentType",
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user