-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Code of Conduct
- I agree to follow Django's Code of Conduct
Feature Description
The current workflow for starting a new Django project requires multiple commands:
django-admin startproject myproject
cd myproject
python manage.py startapp myapp
Then manually adding the app to INSTALLED_APPS
.
This patch adds a new create
management command that combines these steps into a single command:
django-admin create myproject myapp
Features:
- Creates both project and app in one command
- Automatically adds app to
INSTALLED_APPS
- Maintains existing settings.py formatting
- Includes comprehensive test coverage
- Backwards compatible (existing commands remain unchanged)
Usage:
Basic usage
django-admin create myproject myapp
With optional directory
django-admin create myproject myapp --directory /path/to/directory
The command:
- Creates a new project using
startproject
- Creates a new app using
startapp
- Automatically adds the app to
INSTALLED_APPS
in settings.py - Maintains proper indentation and quote style in settings.py
Test coverage includes:
- Basic project and app creation
- Directory handling
- Error cases
- Output messages
- INSTALLED_APPS formatting
Patch includes:
- New management command:
django/core/management/commands/create.py
- Test suite:
django/tests/user_commands/tests.py
π¦ Patch Includes
New command module: django/core/management/commands/create.py
Associated test suite: django/tests/user_commands/tests.py
Problem
π Problem Statement
The current multi-step project setup process can be repetitive and error-prone. Combining the commands and automating configuration of INSTALLED_APPS improves developer experience, especially for beginners or during rapid prototyping.
π Additional Details
This patch maintains Djangoβs commitment to sensible defaults and developer ergonomics while offering a non-disruptive enhancement to project bootstrapping.
Let me know if you'd like a version of this in reStructuredText (reST) format for a Django ticket submission or if you need help writing the actual create.py command or tests.
import os
from pathlib import Path
from django.core.management import CommandError, call_command
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = (
"Creates a Django project and app in one command, automatically configuring "
"INSTALLED_APPS. Usage: django-admin create myproject myapp"
)
def add_arguments(self, parser):
parser.add_argument("project_name", help="Name of the Django project")
parser.add_argument("app_name", help="Name of the Django app")
parser.add_argument(
"--directory",
help="Optional directory for project creation",
default=None,
)
def handle(self, *args, **options):
project_name = options["project_name"]
app_name = options["app_name"]
target_dir = options["directory"]
if target_dir:
if not os.path.exists(target_dir):
os.makedirs(target_dir)
os.chdir(target_dir)
self.stdout.write(f"Creating project '{project_name}'...")
try:
call_command("startproject", project_name, ".")
except Exception as e:
raise CommandError(f"Failed to create project: {e}")
self.stdout.write(f"Creating app '{app_name}'...")
os.chdir(project_name)
try:
call_command("startapp", app_name)
except Exception as e:
raise CommandError(f"Failed to create app: {e}")
settings_path = Path(project_name) / "settings.py"
if not settings_path.exists():
raise CommandError("Could not find settings.py")
self.stdout.write("Updating INSTALLED_APPS...")
with open(settings_path) as f:
content = f.read()
apps_start = content.find("INSTALLED_APPS")
if apps_start == -1:
raise CommandError("Could not find INSTALLED_APPS in settings.py")
bracket_start = content.find("[", apps_start)
if bracket_start == -1:
raise CommandError("Could not parse INSTALLED_APPS format")
quote_style = "'" if content[bracket_start:].find("'") < content[bracket_start:].find('"') else '"'
bracket_end = content.find("]", bracket_start)
if bracket_end == -1:
raise CommandError("Could not parse INSTALLED_APPS format")
last_newline = content.rfind("\n", 0, bracket_start)
indentation = " " * (bracket_start - last_newline - 1)
new_app = f"{quote_style}{project_name}.{app_name}{quote_style},"
apps_list = content[bracket_start + 1:bracket_end].strip()
if apps_list:
new_content = (
content[:bracket_end].rstrip()
+ "\n"
+ indentation
+ new_app
+ "\n"
+ indentation[:-4]
+ "]"
+ content[bracket_end + 1:]
)
else:
new_content = (
content[:bracket_start + 1]
+ "\n"
+ indentation
+ new_app
+ "\n"
+ indentation[:-4]
+ "]"
+ content[bracket_end + 1:]
)
with open(settings_path, "w") as f:
f.write(new_content)
self.stdout.write(
self.style.SUCCESS(
f"Successfully created project '{project_name}' and app '{app_name}'"
)
)
Metadata
Metadata
Assignees
Labels
Type
Projects
Status