Table of Contents
I’ve been thinking a lot about workflow this week. Looking at my daily tasks, I realized that setting up Django projects was taking more time than necessary. Installing Django is quick, especially using Astral-UV, which gets it up and running in under a minute. But that’s just the beginning. After installation, I’d spend time on the same tedious steps: creating directory structures, setting up templates, adding essential files like urls.py and forms.py, and building out placeholder HTML files for pages like the homepage and contact page. These repetitive tasks added up, so I decided to automate as much of the Django setup process as possible and save myself a significant amount of time.
Python’s flexibility makes it ideal for automation. Its tools for file handling, subprocess management, and command-line parsing were perfect for creating a tool that could handle all the setup I needed. I structured the tool with modular classes for each part of the setup process, so it was easy to adapt if I wanted to make tweaks in the future. I also wanted the tool to be easy to use, so I designed it to work with a single command. Running python3 main.py <project_name> <optional_app_name> from the top-level directory where I want to create Django projects is all I need. This command takes care of everything: it installs Django, creates an optional app, sets up the right directory structure, and generates essential files with placeholder content.
This script only works on Debian-based systems (Debian, Ubuntu, etc).
The first step in the script is checking for necessary software, like snapd and uv. I used Python’s subprocess module to run these checks. Here’s how I handled it for snapd:
import subprocess
def check_snapd_installed():
"""Check if 'snapd' is installed by trying to call the snap version command."""
try:
subprocess.run(['snap', '--version'], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print("'snapd' is already installed.")
return True
except FileNotFoundError:
print("'snapd' is not found; it needs to be installed.")
return False
This function checks for snapd by calling snap –version. If the command returns an error, the script lets me know that snapd is missing and prompts to install it. I used similar functions for other dependencies, making sure I could run the tool on any system and still have the necessary software ready.
Setting up the project directory structure was a bit more challenging. I had to consider cases where a user might enter a relative path or a path that already exists. To avoid directory conflicts and prevent using Django’s reserved names like static or media, the script validates the directory name, and if there’s an issue, it prompts for a new name. Once the directory is validated and created, UV handles the backend setup, including initializing the virtual environment, adding Django, and creating a .gitignore file. Here’s a snippet that handles directory setup:
def prompt_for_directory():
"""Prompt user for the directory where the Django project should be installed."""
disallowed_names = {'test', 'django', 'site', 'admin', 'main', 'manage', 'static', 'templates', 'media'}
while True:
directory = input("Enter the directory where you want to install the Django project: ")
directory_name = os.path.basename(os.path.normpath(directory))
if directory_name.lower() in disallowed_names:
print(f"The directory name '{directory_name}' is reserved and cannot be used.")
else:
os.makedirs(directory, exist_ok=True)
os.chdir(directory)
print(f"Changed directory to {directory}")
break
This setup ensures the chosen directory name doesn’t conflict with reserved names, which prevents potential issues down the line.
One tedious part of setting up a Django project is configuring environment variables, so I automated this by having the tool auto-generate .env and .env-template files in the project root. Each file comes pre-populated with default values like DEBUG=True, placeholders for SECRET_KEY, and DATABASE_URL. This saves time and ensures consistent setup across projects. Here’s the snippet that creates these files:
env_content = (
"# Environment variables\n"
"DEBUG=True\n"
"SECRET_KEY=your-secret-key\n"
"DATABASE_URL=your-database-url\n"
)
with open(".env", 'w') as f:
f.write(env_content)
print("Created .env in the installation directory.")
with open(".env-template", 'w') as f:
f.write(env_content)
print("Created .env-template in the installation directory.")
With these files set up automatically, each new Django project is ready for environment variables right from the start, which saves me from having to add these configurations manually.
The tool also automatically creates essential project files like base.html and directories for templates and static assets. The script sets up subdirectories for CSS, JavaScript, and images within the static folder, along with a partials folder in the main template directory. For the app’s template directory, it creates basic HTML files like index.html, about.html, and contact.html with placeholder content. Here’s how that section looks in code:
html_files = {
"index.html": "",
"about.html": "",
"contact.html": "",
}
for filename, content in html_files.items():
with open(f"{app_template_path}/{filename}", 'w') as f:
f.write(content)
print(f"Created {filename} in {app_template_path}")
This simple setup provides consistency and saves me from repeatedly creating these files by hand.
To use the tool, I simply navigate to a top-level directory where I want my Django projects and run python3 main.py <project_name> <optional_app_name>. This one command does everything. It checks dependencies, sets up the directory, installs Django with UV, creates templates and static files, initializes Git, and generates configuration files—all in a few seconds. I can start coding immediately with a fully structured project, skipping all the repetitive setup tasks.
This tool has become a huge time-saver for me. By automating the setup, I’ve eliminated the tedious work that used to come with every new Django project. Now, instead of spending time on directory structure and file creation, I can jump straight into development with a clean, consistent setup every time. Python’s flexibility made this tool possible, and I’ve already started thinking about additional features to add in the future. For anyone working regularly with Django, this tool is a game-changer—allowing more time to focus on building and less on setting up.
You can see the complete script in the Github Repository I setup for the project.