Repository: django-extensions/django-extensions

Issue:
Multiple Settings files
I'm wondering, if my app has more than one settings file, how can I add multiple Django-Shell kernels?

I didn't find this is docs and at the first glance in source code it is  impossible to do as well.

Am I miss something?

Thanks


Discussion:
@oduvan depending on what you're trying to do exactly my change #1509 allows you to generate more kernels. You would need to override the ENV or figure out another way to initialize your kernels. If all you need to do is specify multiple settings files I believe you would be able to use the ENV var `DJANGO_SETTINGS_MODULE` to point to the modules you want to be started as kernels.

Resolving patch:
diff --git a/django_extensions/management/commands/shell_plus.py b/django_extensions/management/commands/shell_plus.py
--- a/django_extensions/management/commands/shell_plus.py
+++ b/django_extensions/management/commands/shell_plus.py
@@ -171,6 +171,46 @@ def run_kernel():
             start_kernel(**kwargs)
         return run_kernel
 
+    def load_base_kernel_spec(self, app):
+        """Finds and returns the base Python kernelspec to extend from."""
+        ksm = app.kernel_spec_manager
+        try_spec_names = getattr(settings, 'NOTEBOOK_KERNEL_SPEC_NAMES', [
+            'python3' if PY3 else 'python2',
+            'python',
+        ])
+
+        if isinstance(try_spec_names, six.string_types):
+            try_spec_names = [try_spec_names]
+
+        ks = None
+        for spec_name in try_spec_names:
+            try:
+                ks = ksm.get_kernel_spec(spec_name)
+                break
+            except Exception:
+                continue
+        if not ks:
+            raise CommandError("No notebook (Python) kernel specs found. Tried %r" % try_spec_names)
+
+        return ks
+
+    def generate_kernel_specs(self, app, ipython_arguments):
+        """Generate an IPython >= 3.0 kernelspec that loads django extensions"""
+        ks = self.load_base_kernel_spec(app)
+        ks.argv.extend(ipython_arguments)
+        ks.display_name = getattr(settings, 'IPYTHON_KERNEL_DISPLAY_NAME', "Django Shell-Plus")
+
+        manage_py_dir, manage_py = os.path.split(os.path.realpath(sys.argv[0]))
+        if manage_py == 'manage.py' and os.path.isdir(manage_py_dir):
+            pythonpath = ks.env.get('PYTHONPATH', os.environ.get('PYTHONPATH', ''))
+            pythonpath = pythonpath.split(os.pathsep)
+            if manage_py_dir not in pythonpath:
+                pythonpath.append(manage_py_dir)
+
+            ks.env['PYTHONPATH'] = os.pathsep.join(filter(None, pythonpath))
+
+        return {'django_extensions': ks}
+
     def get_notebook(self, options):
         try:
             from IPython import release
@@ -194,43 +234,6 @@ def get_notebook(self, options):
 
         no_browser = options['no_browser']
 
-        def install_kernel_spec(app, display_name, ipython_arguments):
-            """Install an IPython >= 3.0 kernelspec that loads django extensions"""
-            ksm = app.kernel_spec_manager
-            try_spec_names = getattr(settings, 'NOTEBOOK_KERNEL_SPEC_NAMES', [
-                'python3' if PY3 else 'python2',
-                'python',
-            ])
-            if isinstance(try_spec_names, six.string_types):
-                try_spec_names = [try_spec_names]
-            ks = None
-            for spec_name in try_spec_names:
-                try:
-                    ks = ksm.get_kernel_spec(spec_name)
-                    break
-                except Exception:
-                    continue
-            if not ks:
-                raise CommandError("No notebook (Python) kernel specs found")
-            ks.argv.extend(ipython_arguments)
-            ks.display_name = display_name
-
-            manage_py_dir, manage_py = os.path.split(os.path.realpath(sys.argv[0]))
-
-            if manage_py == 'manage.py' and os.path.isdir(manage_py_dir):
-                pythonpath = ks.env.get('PYTHONPATH', os.environ.get('PYTHONPATH', ''))
-                pythonpath = pythonpath.split(os.pathsep)
-                if manage_py_dir not in pythonpath:
-                    pythonpath.append(manage_py_dir)
-
-                ks.env['PYTHONPATH'] = os.pathsep.join(filter(None, pythonpath))
-
-            kernel_dir = os.path.join(ksm.user_kernel_dir, 'django_extensions')
-            if not os.path.exists(kernel_dir):
-                os.makedirs(kernel_dir)
-            with open(os.path.join(kernel_dir, 'kernel.json'), 'w') as f:
-                f.write(ks.to_json())
-
         def run_notebook():
             app = NotebookApp.instance()
 
@@ -254,8 +257,26 @@ def run_notebook():
 
             # IPython >= 3 uses kernelspecs to specify kernel CLI args
             if release.version_info[0] >= 3:
-                display_name = getattr(settings, 'IPYTHON_KERNEL_DISPLAY_NAME', "Django Shell-Plus")
-                install_kernel_spec(app, display_name, ipython_arguments)
+                ksm = app.kernel_spec_manager
+                for kid, ks in self.generate_kernel_specs(app, ipython_arguments).items():
+                    roots = [os.path.dirname(ks.resource_dir), ksm.user_kernel_dir]
+                    success = False
+                    for root in roots:
+                        kernel_dir = os.path.join(root, kid)
+                        try:
+                            if not os.path.exists(kernel_dir):
+                                os.makedirs(kernel_dir)
+
+                            with open(os.path.join(kernel_dir, 'kernel.json'), 'w') as f:
+                                f.write(ks.to_json())
+
+                            success = True
+                            break
+                        except OSError:
+                            continue
+
+                    if not success:
+                        raise CommandError("Could not write kernel %r in directories %r" % (kid, roots))
 
             app.start()
         return run_notebook


Test patch:
diff --git a/tests/test_management_command.py b/tests/test_management_command.py
--- a/tests/test_management_command.py
+++ b/tests/test_management_command.py
@@ -4,7 +4,10 @@
 import mock
 import logging
 import importlib
+import pytest
+import sys
 
+from django.conf import settings
 from django.core.management import call_command, find_commands, load_command_class
 from django.test import TestCase
 from six import StringIO
@@ -290,6 +293,7 @@ def test_all(self):
         self.assertIn('__class__()', self.output)
         self.assertIn('validate_unique()', self.output)
 
+    @pytest.mark.skipif(sys.version_info < (3, 3), reason="inspect.Signature requires python3.3 or higher")
     def test_signature(self):
         out = StringIO()
         call_command('list_model_info', '--model', 'django_extensions.MultipleFieldsAndMethods', '--signature', stdout=out)
@@ -301,10 +305,15 @@ def test_signature(self):
         self.assertIn("has_defaults(self, one=1, two='Two', true=True, false=False, none=None)", self.output)
 
     def test_db_type(self):
+        if settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql':
+            id_type = 'serial'
+        else:
+            id_type = 'integer'
+
         out = StringIO()
         call_command('list_model_info', '--model', 'django_extensions.MultipleFieldsAndMethods', '--db-type', stdout=out)
         self.output = out.getvalue()
-        self.assertIn('id - serial', self.output)
+        self.assertIn('id - %s' % id_type, self.output)
         self.assertIn('char_field - varchar(10)', self.output)
         self.assertIn('integer_field - integer', self.output)
         self.assertIn('foreign_key_field - integer', self.output)