From 101bb9bb53fc1f0f63d2677632268302f471dc08 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Wed, 31 Jul 2024 14:40:12 +0100 Subject: [PATCH] Refs #18296 -- Created custom target directory if missing in startproject and startapp --- django/core/management/templates.py | 8 ++++---- docs/ref/django-admin.txt | 13 +++++++------ docs/releases/5.2.txt | 3 +++ tests/admin_scripts/tests.py | 25 ++++++++++++++++++------- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/django/core/management/templates.py b/django/core/management/templates.py index 633eed781d..036008dab4 100644 --- a/django/core/management/templates.py +++ b/django/core/management/templates.py @@ -105,10 +105,10 @@ class TemplateCommand(BaseCommand): if app_or_project == "app": self.validate_name(os.path.basename(top_dir), "directory") if not os.path.exists(top_dir): - raise CommandError( - "Destination directory '%s' does not " - "exist, please create it first." % top_dir - ) + try: + os.makedirs(top_dir) + except OSError as e: + raise CommandError(e) # Find formatters, which are external executables, before input # from the templates can sneak into the path. diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt index 128abe5587..c7cd8daec0 100644 --- a/docs/ref/django-admin.txt +++ b/docs/ref/django-admin.txt @@ -1247,9 +1247,9 @@ By default, :source:`the new directory ` contains a ``models.py`` file and other app template files. If only the app name is given, the app directory will be created in the current working directory. -If the optional destination is provided, Django will use that existing -directory rather than creating a new one. You can use '.' to denote the current -working directory. +If the optional destination is provided, Django will use that name instead. If +the directory with the given name doesn't exist, it will be created. You can +use '.' to denote the current working directory. For example: @@ -1359,9 +1359,10 @@ If only the project name is given, both the project directory and project package will be named ```` and the project directory will be created in the current working directory. -If the optional destination is provided, Django will use that existing -directory as the project directory, and create ``manage.py`` and the project -package within it. Use '.' to denote the current working directory. +If the optional destination is provided, Django will use that name as the +project directory, and create ``manage.py`` and the project package within it. +If the directory with the given name doesn't exist, it will be created. Use '.' +to denote the current working directory. For example: diff --git a/docs/releases/5.2.txt b/docs/releases/5.2.txt index 0caeef01cf..d9c6b05271 100644 --- a/docs/releases/5.2.txt +++ b/docs/releases/5.2.txt @@ -199,6 +199,9 @@ Management Commands setting the :envvar:`HIDE_PRODUCTION_WARNING` environment variable to ``"true"``. +* The :djadmin:`startproject` and :djadmin:`startapp` commands now create the + custom target directory if it doesn't exist. + Migrations ~~~~~~~~~~ diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py index 29023b74c3..e341610696 100644 --- a/tests/admin_scripts/tests.py +++ b/tests/admin_scripts/tests.py @@ -2793,7 +2793,7 @@ class StartProject(LiveServerTestCase, AdminScriptTestCase): def test_custom_project_destination_missing(self): """ - Make sure an exception is raised when the provided + Create the directory when the provided destination directory doesn't exist """ template_path = os.path.join(custom_templates_dir, "project_template") @@ -2807,12 +2807,8 @@ class StartProject(LiveServerTestCase, AdminScriptTestCase): testproject_dir = os.path.join(self.test_dir, "project_dir2") out, err = self.run_django_admin(args) self.assertNoOutput(out) - self.assertOutput( - err, - "Destination directory '%s' does not exist, please create it first." - % testproject_dir, - ) - self.assertFalse(os.path.exists(testproject_dir)) + self.assertNoOutput(err) + self.assertTrue(os.path.exists(testproject_dir)) def test_custom_project_template_with_non_ascii_templates(self): """ @@ -3039,6 +3035,21 @@ class StartApp(AdminScriptTestCase): content, ) + def test_custom_app_destination_missing(self): + """ + Create the directory when the provided destination directory doesn't exist + """ + args = [ + "startapp", + "my_app", + "my_app", + ] + testapp_dir = os.path.join(self.test_dir, "my_app") + out, err = self.run_django_admin(args) + self.assertNoOutput(out) + self.assertNoOutput(err) + self.assertTrue(os.path.exists(testapp_dir)) + class DiffSettings(AdminScriptTestCase): """Tests for diffsettings management command."""