feat: add file and project templates for djinit
This commit is contained in:
4 files changed
+827
-1
No files matched your search
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
App management for Django project setup.
|
||||
App management for djinit.
|
||||
Handles creation of Django apps and updating settings.
|
||||
"""
|
||||
|
||||
|
||||
@@ -0,0 +1,412 @@
|
||||
"""
|
||||
File management utilities for djinit.
|
||||
Handles creation and modification of project files.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from src.utils import change_cwd, create_file_with_content
|
||||
|
||||
|
||||
class FileManager:
|
||||
def __init__(self, project_root: str, project_name: str, app_names: list):
|
||||
self.project_root = project_root
|
||||
self.project_name = project_name
|
||||
self.app_names = app_names
|
||||
self.project_configs = os.path.join(project_root, project_name)
|
||||
self.settings_folder = os.path.join(self.project_configs, "settings")
|
||||
self.settings_file = os.path.join(self.project_configs, "settings.py")
|
||||
|
||||
def create_gitignore(self) -> bool:
|
||||
with change_cwd(self.project_root):
|
||||
gitignore_content = [
|
||||
"# 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",
|
||||
]
|
||||
|
||||
return create_file_with_content(
|
||||
".gitignore",
|
||||
"\n".join(gitignore_content) + "\n",
|
||||
"Created .gitignore file",
|
||||
)
|
||||
|
||||
def create_requirements(self) -> bool:
|
||||
with change_cwd(self.project_root):
|
||||
requirements = [
|
||||
"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", # For PostgreSQL support
|
||||
"gunicorn>=21.2.0", # For production deployment
|
||||
]
|
||||
|
||||
return create_file_with_content(
|
||||
"requirements.txt",
|
||||
"\n".join(requirements) + "\n",
|
||||
"Created requirements.txt with Django dependencies",
|
||||
)
|
||||
|
||||
def create_readme(self) -> bool:
|
||||
with change_cwd(self.project_root):
|
||||
readme_content = f"""# {self.project_name}
|
||||
|
||||
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
|
||||
|
||||
```
|
||||
{self.project_name}/
|
||||
├── {self.project_name}/
|
||||
│ ├── settings/
|
||||
│ │ ├── base.py
|
||||
│ │ ├── development.py
|
||||
│ │ └── production.py
|
||||
│ ├── urls.py
|
||||
│ ├── wsgi.py
|
||||
│ └── asgi.py
|
||||
{"".join([f"├── {app}/\n" for app in self.app_names])}├── 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/
|
||||
"""
|
||||
|
||||
return create_file_with_content(
|
||||
"README.md",
|
||||
readme_content,
|
||||
"Created README.md file",
|
||||
)
|
||||
|
||||
def create_env_file(self) -> bool:
|
||||
"""Create .env.sample file with environment variables."""
|
||||
with change_cwd(self.project_root):
|
||||
env_content = f"""# Django settings
|
||||
DJANGO_SETTINGS_MODULE={self.project_name}.settings.development
|
||||
SECRET_KEY= your-secret-key
|
||||
ALLOWED_HOSTS=api.your-domain.com,www.your-domain.com
|
||||
|
||||
# Database
|
||||
DB_NAME=
|
||||
DB_USER=
|
||||
DB_PASSWORD=
|
||||
DB_HOST=
|
||||
DB_PORT=
|
||||
"""
|
||||
|
||||
return create_file_with_content(
|
||||
".env.sample",
|
||||
env_content,
|
||||
"Created .env.sample file with generated secret key",
|
||||
)
|
||||
|
||||
def create_pyproject_toml(self, metadata: dict) -> bool:
|
||||
with change_cwd(self.project_root):
|
||||
pyproject_content = f"""[project]
|
||||
name = "{metadata["package_name"]}"
|
||||
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 = ["{self.project_name}"]
|
||||
"""
|
||||
|
||||
return create_file_with_content(
|
||||
"pyproject.toml",
|
||||
pyproject_content,
|
||||
"Created pyproject.toml file for uv",
|
||||
)
|
||||
|
||||
def create_app_urls(self) -> bool:
|
||||
for app_name in self.app_names:
|
||||
app_path = os.path.join(self.project_root, app_name)
|
||||
with change_cwd(app_path):
|
||||
urls_content = f"""from django.urls import path
|
||||
from . import views
|
||||
|
||||
app_name = '{app_name}'
|
||||
|
||||
urlpatterns = [
|
||||
# Add your URL patterns here
|
||||
# path('', views.index, name='index'),
|
||||
]
|
||||
"""
|
||||
|
||||
create_file_with_content(
|
||||
"urls.py",
|
||||
urls_content,
|
||||
f"Created {app_name}/urls.py",
|
||||
should_format=True,
|
||||
)
|
||||
return True
|
||||
|
||||
def update_project_urls(self) -> bool:
|
||||
with change_cwd(self.project_configs):
|
||||
# Generate URL includes for all apps
|
||||
app_urls = "\n ".join([f'path("", include("{app_name}.urls")),' for app_name in self.app_names])
|
||||
|
||||
urls_content = f"""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
|
||||
{app_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")),
|
||||
]
|
||||
"""
|
||||
|
||||
result = create_file_with_content(
|
||||
"urls.py",
|
||||
urls_content,
|
||||
"Updated project urls.py with comprehensive URL configuration",
|
||||
should_format=True,
|
||||
)
|
||||
return result
|
||||
|
||||
def update_wsgi_file(self) -> bool:
|
||||
with change_cwd(self.project_configs):
|
||||
wsgi_content = """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()
|
||||
"""
|
||||
|
||||
return create_file_with_content(
|
||||
"wsgi.py",
|
||||
wsgi_content,
|
||||
"Updated wsgi.py with custom configuration",
|
||||
should_format=True,
|
||||
)
|
||||
|
||||
def update_asgi_file(self) -> bool:
|
||||
with change_cwd(self.project_configs):
|
||||
asgi_content = """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()
|
||||
"""
|
||||
|
||||
return create_file_with_content(
|
||||
"asgi.py",
|
||||
asgi_content,
|
||||
"Updated asgi.py with custom configuration",
|
||||
should_format=True,
|
||||
)
|
||||
|
||||
def update_manage_py(self) -> bool:
|
||||
"""Update manage.py with custom configuration."""
|
||||
with change_cwd(self.project_root):
|
||||
manage_content = """#!/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()
|
||||
"""
|
||||
|
||||
return create_file_with_content(
|
||||
"manage.py",
|
||||
manage_content,
|
||||
"Updated manage.py with custom configuration",
|
||||
should_format=True,
|
||||
)
|
||||
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
Project management for djinit.
|
||||
Handles creation of Django projects and apps.
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from src.scripts.console_ui import UIFormatter
|
||||
from src.utils import change_cwd
|
||||
|
||||
|
||||
class ProjectManager:
|
||||
def __init__(self, project_dir: str, project_name: str, app_names: list):
|
||||
self.project_dir = project_dir
|
||||
self.project_name = project_name
|
||||
self.app_names = app_names
|
||||
self.project_root = os.path.join(os.getcwd(), project_dir)
|
||||
|
||||
def create_project(self) -> bool:
|
||||
# Check if project already exists
|
||||
if os.path.exists(self.project_root):
|
||||
UIFormatter.print_error(f"Directory '{self.project_dir}' already exists.")
|
||||
UIFormatter.print_info("Please choose a different project directory name or remove the existing directory.")
|
||||
return False
|
||||
|
||||
# Ensure Django is installed
|
||||
self._ensure_django_installed()
|
||||
|
||||
# Create the project directory first
|
||||
os.makedirs(self.project_root, exist_ok=True)
|
||||
|
||||
# Create Django project inside the directory
|
||||
subprocess.run(
|
||||
["django-admin", "startproject", self.project_name, self.project_root],
|
||||
check=True,
|
||||
)
|
||||
|
||||
UIFormatter.print_success(f"Django project '{self.project_name}' created successfully!")
|
||||
return True
|
||||
|
||||
def create_apps(self) -> bool:
|
||||
with change_cwd(self.project_root):
|
||||
# Create each app
|
||||
for app_name in self.app_names:
|
||||
try:
|
||||
subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
os.path.join(self.project_root, "manage.py"),
|
||||
"startapp",
|
||||
app_name,
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
UIFormatter.print_success(f"Django app '{app_name}' created successfully!")
|
||||
except subprocess.CalledProcessError as e:
|
||||
UIFormatter.print_error(f"Failed to create app '{app_name}': {e}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _ensure_django_installed(self) -> None:
|
||||
try:
|
||||
import django
|
||||
except ImportError:
|
||||
UIFormatter.print_info("Django not found, installing...")
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "pip", "install", "--upgrade", "django"],
|
||||
check=True,
|
||||
)
|
||||
UIFormatter.print_success("Django installed successfully!")
|
||||
|
||||
def validate_project_structure(self) -> bool:
|
||||
required_files = [
|
||||
os.path.join(self.project_root, "manage.py"),
|
||||
os.path.join(self.project_root, self.project_name, "__init__.py"),
|
||||
os.path.join(self.project_root, self.project_name, "settings.py"),
|
||||
os.path.join(self.project_root, self.project_name, "urls.py"),
|
||||
os.path.join(self.project_root, self.project_name, "wsgi.py"),
|
||||
os.path.join(self.project_root, self.project_name, "asgi.py"),
|
||||
]
|
||||
|
||||
# Add required files for each app
|
||||
for app_name in self.app_names:
|
||||
app_files = [
|
||||
os.path.join(self.project_root, app_name, "__init__.py"),
|
||||
os.path.join(self.project_root, app_name, "apps.py"),
|
||||
os.path.join(self.project_root, app_name, "models.py"),
|
||||
os.path.join(self.project_root, app_name, "views.py"),
|
||||
os.path.join(self.project_root, app_name, "admin.py"),
|
||||
]
|
||||
required_files.extend(app_files)
|
||||
|
||||
missing_files = [file_path for file_path in required_files if not os.path.exists(file_path)]
|
||||
|
||||
if missing_files:
|
||||
UIFormatter.print_error("Project structure validation failed:")
|
||||
for file_path in missing_files:
|
||||
UIFormatter.print_error(f" Missing: {file_path}")
|
||||
return False
|
||||
|
||||
UIFormatter.print_success("Project structure validation passed")
|
||||
return True
|
||||
@@ -0,0 +1,309 @@
|
||||
"""
|
||||
Settings management for djinit.
|
||||
Handles creation and configuration of Django settings files.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from src.scripts.console_ui import UIFormatter
|
||||
from src.scripts.secretkey_generator import generate_secret_key
|
||||
from src.utils import change_cwd, create_file_with_content
|
||||
|
||||
|
||||
class SettingsManager:
|
||||
def __init__(self, project_root: str, project_name: str, app_names: list):
|
||||
self.project_root = project_root
|
||||
self.project_name = project_name
|
||||
self.app_names = app_names
|
||||
self.project_configs = os.path.join(project_root, project_name)
|
||||
self.settings_folder = os.path.join(self.project_configs, "settings")
|
||||
self.settings_file = os.path.join(self.project_configs, "settings.py")
|
||||
self.secret_key = generate_secret_key(50)
|
||||
|
||||
def create_settings_structure(self) -> bool:
|
||||
# Change to project configs directory
|
||||
with change_cwd(self.project_configs):
|
||||
# Create settings folder
|
||||
os.makedirs("settings", exist_ok=True)
|
||||
|
||||
# Move settings.py to settings/base.py
|
||||
if os.path.exists(self.settings_file):
|
||||
os.rename(self.settings_file, os.path.join(self.settings_folder, "base.py"))
|
||||
|
||||
# Create __init__.py files
|
||||
with change_cwd(self.settings_folder):
|
||||
open("__init__.py", "a").close()
|
||||
open("development.py", "a").close()
|
||||
open("production.py", "a").close()
|
||||
|
||||
UIFormatter.print_success("Created Django settings folder structure")
|
||||
return True
|
||||
|
||||
def update_base_settings(self) -> bool:
|
||||
with change_cwd(self.settings_folder):
|
||||
# Generate app list from app_names
|
||||
app_list_lines = [f' "{app_name}",' for app_name in self.app_names]
|
||||
app_list = "\n".join(app_list_lines)
|
||||
|
||||
base_content = f'''"""
|
||||
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 = [
|
||||
{app_list}
|
||||
]
|
||||
|
||||
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",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = "{self.project_name}.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 = "{self.project_name}.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": "{self.project_name} API",
|
||||
"DESCRIPTION": "API documentation for {self.project_name}",
|
||||
"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"
|
||||
|
||||
'''
|
||||
|
||||
return create_file_with_content(
|
||||
"base.py",
|
||||
base_content,
|
||||
"Updated settings/base.py with comprehensive configuration",
|
||||
should_format=True,
|
||||
)
|
||||
|
||||
def update_development_settings(self) -> bool:
|
||||
"""Update development.py with development-specific settings."""
|
||||
with change_cwd(self.settings_folder):
|
||||
dev_content = f"""from .base import *
|
||||
|
||||
SECRET_KEY = "{self.secret_key}"
|
||||
|
||||
# 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
|
||||
"""
|
||||
|
||||
return create_file_with_content(
|
||||
"development.py",
|
||||
dev_content,
|
||||
"Updated settings/development.py",
|
||||
should_format=True,
|
||||
)
|
||||
|
||||
def update_production_settings(self) -> bool:
|
||||
"""Update production.py with production-specific settings."""
|
||||
with change_cwd(self.settings_folder):
|
||||
prod_content = """from .base import *
|
||||
import os
|
||||
|
||||
DEBUG = False
|
||||
SECRET_KEY = os.getenv('SECRET_KEY')
|
||||
ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', '').split(',')
|
||||
|
||||
# Database
|
||||
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'
|
||||
|
||||
"""
|
||||
|
||||
return create_file_with_content(
|
||||
"production.py",
|
||||
prod_content,
|
||||
"Updated settings/production.py",
|
||||
should_format=True,
|
||||
)
|
||||
Reference in new issue
Block a user