summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsage <laymonage@gmail.com>2021-07-24 07:09:03 +0700
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-09-01 12:08:02 +0200
commit84c7c4a477eae5de394d036d7ba1e58a37e18b69 (patch)
tree226181586fa7134a6674517105c0699e0314615f
parent3686077d463685d383e14b4695eaaa2658919a42 (diff)
Fixed #32309 -- Added --exclude option to startapp/startproject management commands.
-rw-r--r--django/core/management/templates.py18
-rw-r--r--docs/ref/django-admin.txt16
-rw-r--r--docs/releases/4.0.txt3
-rw-r--r--tests/admin_scripts/tests.py64
4 files changed, 100 insertions, 1 deletions
diff --git a/django/core/management/templates.py b/django/core/management/templates.py
index 8607ca8542..976442021e 100644
--- a/django/core/management/templates.py
+++ b/django/core/management/templates.py
@@ -1,3 +1,4 @@
+import argparse
import cgi
import mimetypes
import os
@@ -54,6 +55,14 @@ class TemplateCommand(BaseCommand):
help='The file name(s) to render. Separate multiple file names '
'with commas, or use -n multiple times.'
)
+ parser.add_argument(
+ '--exclude', '-x',
+ action='append', default=argparse.SUPPRESS, nargs='?', const='',
+ help=(
+ 'The directory name(s) to exclude, in addition to .git and '
+ '__pycache__. Can be used multiple times.'
+ ),
+ )
def handle(self, app_or_project, name, target=None, **options):
self.app_or_project = app_or_project
@@ -82,8 +91,12 @@ class TemplateCommand(BaseCommand):
extensions = tuple(handle_extensions(options['extensions']))
extra_files = []
+ excluded_directories = ['.git', '__pycache__']
for file in options['files']:
extra_files.extend(map(lambda x: x.strip(), file.split(',')))
+ if exclude := options.get('exclude'):
+ for directory in exclude:
+ excluded_directories.append(directory.strip())
if self.verbosity >= 2:
self.stdout.write(
'Rendering %s template files with extensions: %s'
@@ -126,7 +139,10 @@ class TemplateCommand(BaseCommand):
os.makedirs(target_dir, exist_ok=True)
for dirname in dirs[:]:
- if dirname.startswith('.') or dirname == '__pycache__':
+ if 'exclude' not in options:
+ if dirname.startswith('.') or dirname == '__pycache__':
+ dirs.remove(dirname)
+ elif dirname in excluded_directories:
dirs.remove(dirname)
for filename in files:
diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt
index 1c8268e192..4d66a3b3aa 100644
--- a/docs/ref/django-admin.txt
+++ b/docs/ref/django-admin.txt
@@ -1304,6 +1304,14 @@ Specifies which files in the app template (in addition to those matching
``--extension``) should be rendered with the template engine. Defaults to an
empty list.
+.. django-admin-option:: --exclude DIRECTORIES, -x DIRECTORIES
+
+.. versionadded:: 4.0
+
+Specifies which directories in the app template should be excluded, in addition
+to ``.git`` and ``__pycache__``. If this option is not provided, directories
+named ``__pycache__`` or starting with ``.`` will be excluded.
+
The :class:`template context <django.template.Context>` used for all matching
files is:
@@ -1373,6 +1381,14 @@ Specifies which files in the project template (in addition to those matching
``--extension``) should be rendered with the template engine. Defaults to an
empty list.
+.. django-admin-option:: --exclude DIRECTORIES, -x DIRECTORIES
+
+.. versionadded:: 4.0
+
+Specifies which directories in the project template should be excluded, in
+addition to ``.git`` and ``__pycache__``. If this option is not provided,
+directories named ``__pycache__`` or starting with ``.`` will be excluded.
+
The :class:`template context <django.template.Context>` used is:
- Any option passed to the ``startproject`` command (among the command's
diff --git a/docs/releases/4.0.txt b/docs/releases/4.0.txt
index 5849b08cd4..e64f6633ef 100644
--- a/docs/releases/4.0.txt
+++ b/docs/releases/4.0.txt
@@ -278,6 +278,9 @@ Management Commands
<django.core.management.BaseCommand.suppressed_base_arguments>` attribute
allows suppressing unsupported default command options in the help output.
+* The new :option:`startapp --exclude` and :option:`startproject --exclude`
+ options allow excluding directories from the template.
+
Migrations
~~~~~~~~~~
diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py
index bed9c61b31..9467bf32c3 100644
--- a/tests/admin_scripts/tests.py
+++ b/tests/admin_scripts/tests.py
@@ -2232,6 +2232,70 @@ class StartProject(LiveServerTestCase, AdminScriptTestCase):
hidden_dir = os.path.join(testproject_dir, '.hidden')
self.assertIs(os.path.exists(hidden_dir), False)
+ def test_custom_project_template_hidden_directory_included(self):
+ """
+ Template context variables in hidden directories are rendered, if not
+ excluded.
+ """
+ template_path = os.path.join(custom_templates_dir, 'project_template')
+ project_name = 'custom_project_template_hidden_directories_included'
+ args = [
+ 'startproject',
+ '--template',
+ template_path,
+ project_name,
+ 'project_dir',
+ '--exclude',
+ ]
+ testproject_dir = os.path.join(self.test_dir, 'project_dir')
+ os.mkdir(testproject_dir)
+
+ _, err = self.run_django_admin(args)
+ self.assertNoOutput(err)
+ render_py_path = os.path.join(testproject_dir, '.hidden', 'render.py')
+ with open(render_py_path) as fp:
+ self.assertIn(
+ f'# The {project_name} should be rendered.',
+ fp.read(),
+ )
+
+ def test_custom_project_template_exclude_directory(self):
+ """
+ Excluded directories (in addition to .git and __pycache__) are not
+ included in the project.
+ """
+ template_path = os.path.join(custom_templates_dir, 'project_template')
+ project_name = 'custom_project_with_excluded_directories'
+ args = [
+ 'startproject',
+ '--template',
+ template_path,
+ project_name,
+ 'project_dir',
+ '--exclude',
+ 'additional_dir',
+ '-x',
+ '.hidden',
+ ]
+ testproject_dir = os.path.join(self.test_dir, 'project_dir')
+ os.mkdir(testproject_dir)
+
+ _, err = self.run_django_admin(args)
+ self.assertNoOutput(err)
+ excluded_directories = [
+ '.hidden',
+ 'additional_dir',
+ '.git',
+ '__pycache__',
+ ]
+ for directory in excluded_directories:
+ self.assertIs(
+ os.path.exists(os.path.join(testproject_dir, directory)),
+ False,
+ )
+ not_excluded = os.path.join(testproject_dir, project_name)
+ self.assertIs(os.path.exists(not_excluded), True)
+
class StartApp(AdminScriptTestCase):