feat: scaffold a basic Django project structure

This commit is contained in:
sankalp committed 2025-10-29 10:59:58 +05:45
1 parent 1a2e50900d
commit b78ba6d308
33 files changed
+856

No files matched your search

+2
View File
@@ -42,6 +42,8 @@ class Cli:
("Configuring production settings", self.settings_manager.update_production_settings),
("Creating utility files", self._create_utility_files),
("Setting up app URLs", self.file_manager.create_app_urls),
("Creating app serializers", self.file_manager.create_app_serializers),
("Creating app routes", self.file_manager.create_app_routes),
("Configuring comprehensive URLs", self.file_manager.update_project_urls),
("Updating WSGI configuration", self.file_manager.update_wsgi_file),
("Updating ASGI configuration", self.file_manager.update_asgi_file),
+32
View File
@@ -8,6 +8,8 @@ import subprocess
from typing import Optional
from src.scripts.console_ui import UIFormatter
from src.scripts.template_engine import template_engine
from src.utils import create_file_with_content
class AppManager:
@@ -33,6 +35,9 @@ class AppManager:
if not self._add_to_installed_apps():
return False
if not self._create_additional_files():
return False
UIFormatter.print_success(f"Django app '{self.app_name}' created and configured successfully!")
return True
@@ -116,3 +121,30 @@ class AppManager:
return settings_path
return None
def _create_additional_files(self) -> bool:
"""Create additional files for the app (serializers.py, routes.py)."""
app_path = os.path.join(self.current_dir, self.app_name)
# Create serializers.py
context = {"app_name": self.app_name}
serializers_content = template_engine.render_template("serializers.j2", context)
if not create_file_with_content(
os.path.join(app_path, "serializers.py"),
serializers_content,
f"Created {self.app_name}/serializers.py",
should_format=True,
):
return False
# Create routes.py
routes_content = template_engine.render_template("routes.j2", context)
if not create_file_with_content(
os.path.join(app_path, "routes.py"),
routes_content,
f"Created {self.app_name}/routes.py",
should_format=True,
):
return False
return True
+38
View File
@@ -90,6 +90,44 @@ class FileManager:
)
return True
def create_app_serializers(self) -> bool:
"""Create serializers.py file for each app."""
apps_base_dir = self.project_root
if self.metadata.get("nested_apps") and self.metadata.get("nested_dir"):
apps_base_dir = os.path.join(self.project_root, self.metadata.get("nested_dir"))
for app_name in self.app_names:
app_path = os.path.join(apps_base_dir, app_name)
with change_cwd(app_path):
context = {"app_name": app_name}
serializers_content = template_engine.render_template("serializers.j2", context)
create_file_with_content(
"serializers.py",
serializers_content,
f"Created {app_name}/serializers.py",
should_format=True,
)
return True
def create_app_routes(self) -> bool:
"""Create routes.py file for each app."""
apps_base_dir = self.project_root
if self.metadata.get("nested_apps") and self.metadata.get("nested_dir"):
apps_base_dir = os.path.join(self.project_root, self.metadata.get("nested_dir"))
for app_name in self.app_names:
app_path = os.path.join(apps_base_dir, app_name)
with change_cwd(app_path):
context = {"app_name": app_name}
routes_content = template_engine.render_template("routes.j2", context)
create_file_with_content(
"routes.py",
routes_content,
f"Created {app_name}/routes.py",
should_format=True,
)
return True
def update_project_urls(self) -> bool:
with change_cwd(self.project_configs):
nested = bool(self.metadata.get("nested_apps"))
+16
View File
@@ -0,0 +1,16 @@
"""
API routes for {{ app_name }} app.
"""
from rest_framework.routers import DefaultRouter
from . import views
# Create router instance
router = DefaultRouter()
# Register viewsets with router
# Example:
# router.register(r'{{ app_name }}', views.{{ app_name|title }}ViewSet)
# Get URL patterns
urlpatterns = router.urls
+13
View File
@@ -0,0 +1,13 @@
"""
Serializers for {{ app_name }} app.
"""
from rest_framework import serializers
# Add your serializers here
# Example:
# class {{ app_name|title }}Serializer(serializers.ModelSerializer):
# class Meta:
# model = YourModel
# fields = '__all__'
+18
View File
@@ -0,0 +1,18 @@
# Django settings
DJANGO_SETTINGS_MODULE=core.settings.development
SECRET_KEY= your-secret-key
ALLOWED_HOSTS=api.your-domain.com,www.your-domain.com
# Database (individual parameters)
DB_NAME=
DB_USER=
DB_PASSWORD=
DB_HOST=
DB_PORT=
# Email settings
EMAIL_HOST=
EMAIL_PORT=
EMAIL_USE_TLS=
EMAIL_HOST_USER=
EMAIL_HOST_PASSWORD=
+89
View File
@@ -0,0 +1,89 @@
# Python
*.py[cod]
*$py.class
*.so
__pycache__/
*.egg-info/
dist/
build/
.eggs/
*.egg
# Django
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
*.sqlite3
# Environment
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# IDEs
.vscode/
.idea/
*.swp
*.swo
*~
.project
.pydevproject
.settings/
.ropeproject/
# OS
.DS_Store
*.DS_Store
Thumbs.db
desktop.ini
# Django specific
media/
staticfiles/
staticfiles/
static_root/
/static/
# Testing
.coverage
htmlcov/
.pytest_cache/
.tox/
coverage.xml
*.cover
.hypothesis/
.cache
# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Node modules (if using frontend tools)
node_modules/
# Secret keys
secret.txt
secrets.json
# Jupyter Notebook
.ipynb_checkpoints
# Backup files
*.bak
*.tmp
*~
# Database
*.db
*.sql
# Cloud
.cloud
+20
View File
@@ -0,0 +1,20 @@
# Procfile for core
# Used for deploying to PaaS platforms like Heroku, Railway, Render, etc.
# Web server process (using Gunicorn)
# Adjust workers based on your needs: (2 x CPU cores) + 1
web: gunicorn core.wsgi:application --bind 0.0.0.0:$PORT --workers 2 --timeout 120 --access-logfile - --error-logfile - --log-level info
# Alternative web server configurations (uncomment to use):
# web: gunicorn core.wsgi:application --bind 0.0.0.0:$PORT --workers 4 --threads 2 --timeout 120
# web: gunicorn core.wsgi:application --bind 0.0.0.0:$PORT --workers 2 --worker-class gevent --worker-connections 1000 --timeout 120
# Release task (runs migrations before deployment)
release: python manage.py migrate --noinput && python manage.py collectstatic --noinput
# Uncomment below to run scheduled tasks (if using Celery)
# worker: celery -A core worker --loglevel=info
# beat: celery -A core beat --loglevel=info
# Development server (for local testing with Foreman/Overmind)
# web: python manage.py runserver 0.0.0.0:$PORT
+60
View File
@@ -0,0 +1,60 @@
# core
A Django project created with django-setup.
## Features
- Modern project structure with environment-specific settings
- Pre-configured REST API with JWT authentication
- Essential dependencies and utilities
- Production-ready configuration
## Setup
1. Install dependencies:
```bash
pip install -r requirements.txt
```
2. Set environment variables in `.env` file
3. Run migrations:
```bash
python manage.py migrate
```
4. Create superuser:
```bash
python manage.py createsuperuser
```
5. Start development server:
```bash
python manage.py runserver
```
## Project Structure
```
core/
├── core/
│ ├── settings/
│ │ ├── base.py
│ │ ├── development.py
│ │ └── production.py
│ ├── urls.py
│ ├── wsgi.py
│ └── asgi.py
├── store/
├── manage.py
├── requirements.txt
├── pyproject.toml
├── .env.sample
└── README.md
```
## API Documentation
When running in development mode, API documentation is available at:
- Swagger UI: http://localhost:8000/docs/
- Schema: http://localhost:8000/schema/
View File
Whitespace-only changes.
View File
Whitespace-only changes.
+3
View File
@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.
+6
View File
@@ -0,0 +1,6 @@
from django.apps import AppConfig
class StoreConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "store"
Whitespace-only changes.
+3
View File
@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.
+16
View File
@@ -0,0 +1,16 @@
"""
API routes for store app.
"""
from rest_framework.routers import DefaultRouter
from . import views
# Create router instance
router = DefaultRouter()
# Register viewsets with router
# Example:
# router.register(r'store', views.StoreViewSet)
# Get URL patterns
urlpatterns = router.urls
+13
View File
@@ -0,0 +1,13 @@
"""
Serializers for store app.
"""
from rest_framework import serializers
# Add your serializers here
# Example:
# class StoreSerializer(serializers.ModelSerializer):
# class Meta:
# model = YourModel
# fields = '__all__'
+3
View File
@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.
+9
View File
@@ -0,0 +1,9 @@
from django.urls import path
from . import views
app_name = "store"
urlpatterns = [
# Add your URL patterns here
# path('', views.index, name='index'),
]
+3
View File
@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.
View File
Whitespace-only changes.
+9
View File
@@ -0,0 +1,9 @@
import os
from dotenv import load_dotenv
from django.core.asgi import get_asgi_application
load_dotenv()
os.environ.setdefault("DJANGO_SETTINGS_MODULE", os.getenv("DJANGO_SETTINGS_MODULE"))
application = get_asgi_application()
View File
Whitespace-only changes.
+163
View File
@@ -0,0 +1,163 @@
"""
Common settings shared between development and production environment
"""
import os
from datetime import timedelta
from pathlib import Path
from dotenv import load_dotenv
load_dotenv()
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# Application definition
THIRD_PARTY_APPS = [
"rest_framework",
"rest_framework_simplejwt",
"rest_framework_simplejwt.token_blacklist",
"corsheaders",
"drf_spectacular",
]
USER_DEFINED_APPS = [
"apps.store",
"account",
]
BUILT_IN_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
INSTALLED_APPS = BUILT_IN_APPS + THIRD_PARTY_APPS + USER_DEFINED_APPS
INSTALLED_APPS.insert(0, "jazzmin")
MIDDLEWARE = [
"corsheaders.middleware.CorsMiddleware",
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"account",
]
ROOT_URLCONF = "core.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "core.wsgi.application"
# Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.2/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True
# Static files and media files
STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
MEDIA_URL = "media/"
MEDIA_ROOT = BASE_DIR / "media"
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
# REST Framework Settings
REST_FRAMEWORK = {
"DEFAULT_RENDERER_CLASSES": [
"rest_framework.renderers.JSONRenderer",
],
"DEFAULT_PARSER_CLASSES": [
"rest_framework.parsers.JSONParser",
],
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
"rest_framework.authentication.SessionAuthentication",
),
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"PAGE_SIZE": 20,
}
# DRF Spectacular settings
SPECTACULAR_SETTINGS = {
"TITLE": "core API",
"DESCRIPTION": "API documentation for core",
"VERSION": "1.0.0",
"SERVE_INCLUDE_SCHEMA": False,
"COMPONENT_SPLIT_REQUEST": True,
"SCHEMA_PATH_PREFIX": "/api/",
}
# JWT Settings
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(days=1),
"REFRESH_TOKEN_LIFETIME": timedelta(days=3),
"ROTATE_REFRESH_TOKENS": True,
"BLACKLIST_AFTER_ROTATION": True,
}
# CORS Settings
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000",
"http://127.0.0.1:3000",
]
CORS_ALLOW_CREDENTIALS = True
# Security Settings
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = "DENY"
+27
View File
@@ -0,0 +1,27 @@
from .base import *
SECRET_KEY = "+#JJBTL^S&N7FOYi8_WM$*(BvxD6VqB7JnLCvgO_4f9yN$FJP8"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ["*"]
# Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# Email backend for development
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
# CORS settings for development
CORS_ALLOW_ALL_ORIGINS = True
# Disable HTTPS redirect in development
SECURE_SSL_REDIRECT = False
+43
View File
@@ -0,0 +1,43 @@
from .base import *
import os
import dj_database_url
DEBUG = False
SECRET_KEY = os.getenv("SECRET_KEY")
ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "").split(",")
# Database
# Using individual database parameters
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.getenv("DB_NAME"),
"USER": os.getenv("DB_USER"),
"PASSWORD": os.getenv("DB_PASSWORD"),
"HOST": os.getenv("DB_HOST"),
"PORT": os.getenv("DB_PORT"),
}
}
# Email settings
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = os.getenv("EMAIL_HOST")
EMAIL_PORT = int(os.getenv("EMAIL_PORT", 587))
EMAIL_USE_TLS = os.getenv("EMAIL_USE_TLS", "True").lower() == "true"
EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER")
EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD")
# Security settings for production
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
# Session settings
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
# Static files
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
+29
View File
@@ -0,0 +1,29 @@
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from drf_spectacular.views import (
SpectacularAPIView,
SpectacularSwaggerView,
)
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
TokenBlacklistView,
)
urlpatterns = [
path("admin/", admin.site.urls),
# JWT tokens
path("token/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),
path("token/blacklist/", TokenBlacklistView.as_view(), name="token_blacklist"),
# App URLs
path("", include("apps.store.urls")),
]
# Don't show schema in production
if settings.DEBUG:
urlpatterns += [
path("schema/", SpectacularAPIView.as_view(), name="schema"),
path("docs/", SpectacularSwaggerView.as_view(url_name="schema")),
]
+9
View File
@@ -0,0 +1,9 @@
import os
from dotenv import load_dotenv
from django.core.wsgi import get_wsgi_application
load_dotenv()
os.environ.setdefault("DJANGO_SETTINGS_MODULE", os.getenv("DJANGO_SETTINGS_MODULE"))
application = get_wsgi_application()
+154
View File
@@ -0,0 +1,154 @@
# Justfile for core
# Task runner for Django development
# Install just: https://github.com/casey/just
# Default task: show available commands
default:
@just --list
# Run development server
dev:
uv run python manage.py runserver
# Run development server with custom host and port
dev-server HOST="0.0.0.0" PORT="8000":
uv run python manage.py runserver :
# Create migrations for all apps
makemigrations:
uv run python manage.py makemigrations
# Create migrations for specific app
makemigrations-app APP:
uv run python manage.py makemigrations
# Run all migrations
migrate:
uv run python manage.py migrate
# Reset database (delete all migrations)
reset-db:
uv run python manage.py flush --noinput
uv run python manage.py migrate
# Create superuser
createsuperuser:
uv run python manage.py createsuperuser
# Collect static files
collectstatic:
uv run python manage.py collectstatic --noinput
# Run Django shell
shell:
uv run python manage.py shell
# Run Django shell plus (IPython)
shell-plus:
uv run python manage.py shell_plus
# Run tests
test:
uv run python manage.py test
# Run tests with coverage
test-coverage:
uv run pytest --cov=. --cov-report=html --cov-report=term
# Lint code with ruff
lint:
uv run ruff check .
# Format code with ruff
format:
uv run ruff format .
# Lint and format code
style:
uv run ruff check .
uv run ruff format .
# Check security issues
check:
uv run python manage.py check
# Check security issues (production settings)
check-deploy:
uv run python manage.py check --deploy
# Show all database migrations status
showmigrations:
uv run python manage.py showmigrations
# Sync dependencies (uv equivalent of pip freeze)
requirements:
uv pip compile
# Run all linting and checks
ci:
uv run ruff check .
uv run ruff format --check .
uv run python manage.py check
# Start interactive shell
shell-ipython:
uv run python manage.py shell_plus
# Clear all cache
clear-cache:
uv run python manage.py clear_cache
# Show Django version
version:
uv run python manage.py version
# Run database shell
dbshell:
uv run python manage.py dbshell
# Start production server with gunicorn
server:
uv run gunicorn core.wsgi:application --bind 0.0.0.0:8000
# Start production server with gunicorn workers
server-prod WORKERS="4":
uv run gunicorn core.wsgi:application --bind 0.0.0.0:8000 --workers
# Start production server with gunicorn and reload on file changes
server-watch:
uv run gunicorn core.wsgi:application --bind 0.0.0.0:8000 --reload
# Clean Python cache files
clean:
find . -type d -name "__pycache__" -exec rm -rf {} +
find . -type f -name "*.pyc" -delete
find . -type f -name "*.pyo" -delete
find . -type f -name ".coverage" -delete
find . -type d -name "*.egg-info" -exec rm -rf {} +
rm -rf htmlcov/
rm -rf .pytest_cache/
# Setup project for development (using uv)
setup:
uv sync
uv run python manage.py migrate
uv run python manage.py createsuperuser
# Complete setup including static files
setup-complete:
uv sync
uv run python manage.py migrate
uv run python manage.py collectstatic --noinput
uv run python manage.py createsuperuser
# Install a new package
install PACKAGE:
uv add
# Install a dev package
install-dev PACKAGE:
uv add --dev
# Remove a package
remove PACKAGE:
uv remove
Executable
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
from dotenv import load_dotenv
def main():
"""Run administrative tasks."""
os.environ.setdefault("DJANGO_SETTINGS_MODULE", os.getenv("DJANGO_SETTINGS_MODULE"))
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == "__main__":
load_dotenv()
main()
+41
View File
@@ -0,0 +1,41 @@
[project]
name = "yoo"
version = "0.1.0"
description = ""
readme = "README.md"
requires-python = ">=3.13"
license = { text = "MIT" }
dependencies = [
"django>=5.2.7",
"python-dotenv>=1.1.1",
"django-jazzmin>=3.0.1",
"djangorestframework>=3.16.1",
"djangorestframework-simplejwt>=5.5.1",
"drf-spectacular>=0.28.0",
"django-cors-headers>=4.9.0",
"whitenoise>=6.8.2",
"psycopg2-binary>=2.9.9",
"gunicorn>=21.2.0",
]
[tool.ruff]
line-length = 88
target-version = "py38"
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"UP", # pyupgrade
]
ignore = [
"E501", # line too long, handled by formatter
"B008", # do not perform function calls in argument defaults
]
[tool.ruff.lint.isort]
known-first-party = ["core"]
+10
View File
@@ -0,0 +1,10 @@
Django
python-dotenv
django-jazzmin>
djangorestframework
djangorestframework_simplejwt
drf-spectacular
django-cors-headers
whitenoise
psycopg2-binary
gunicorn
+1
View File
@@ -0,0 +1 @@
python-3.13