diff --git a/djinit/scripts/console_ui.py b/djinit/scripts/console_ui.py index 06aac3b..c8c2213 100644 --- a/djinit/scripts/console_ui.py +++ b/djinit/scripts/console_ui.py @@ -1,6 +1,6 @@ """ Console UI utilities for djinit. -Provides enhanced styling, formatting, and user interface components. +Provides clean, minimal styling and user interface components. """ from contextlib import contextmanager @@ -9,15 +9,7 @@ from typing import Any, Dict, List, Optional from rich import box from rich.console import Console from rich.panel import Panel -from rich.progress import ( - BarColumn, - Progress, - SpinnerColumn, - TaskProgressColumn, - TextColumn, - TimeRemainingColumn, -) -from rich.syntax import Syntax +from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TextColumn, TimeRemainingColumn from rich.table import Table from rich.text import Text @@ -25,6 +17,8 @@ console = Console() class UIColors: + """Color scheme for consistent UI styling""" + SUCCESS = "bold green" ERROR = "bold red" WARNING = "bold yellow" @@ -32,40 +26,32 @@ class UIColors: HIGHLIGHT = "bold cyan" MUTED = "dim white" ACCENT = "bold magenta" - PRIMARY = "bold white" - SECONDARY = "bright_white" - CODE = "dim cyan" - GRADIENT_START = "rgb(139,92,246)" - GRADIENT_END = "rgb(236,72,153)" class Icons: + """Minimal icon set""" + SUCCESS = "✓" ERROR = "✗" WARNING = "⚠" INFO = "ℹ" ROCKET = "🚀" - PACKAGE = "📦" - FOLDER = "📁" - FILE = "📄" - SETTINGS = "⚙" - DATABASE = "🗄" - LINK = "🔗" - STAR = "⭐" - FIRE = "🔥" - SPARKLES = "✨" - PARTY = "🎉" TARGET = "🎯" - MAGNIFIER = "🔍" - WRENCH = "🔧" + LINK = "🔗" CLOCK = "⏱" + PARTY = "🎉" class UIFormatter: """Utility class for consistent UI formatting""" + # ============================================================================ + # Basic Output Methods + # ============================================================================ + @staticmethod def print_success(message: str, icon: str = Icons.SUCCESS): + """Print success message""" console.print(f"[{UIColors.SUCCESS}]{icon}[/{UIColors.SUCCESS}] [bold]{message}[/bold]") @staticmethod @@ -77,36 +63,24 @@ class UIFormatter: @staticmethod def print_warning(message: str, icon: str = Icons.WARNING): + """Print warning message""" console.print(f"[{UIColors.WARNING}]{icon}[/{UIColors.WARNING}] [bold]{message}[/bold]") @staticmethod - def print_info(message: str, icon: str = Icons.INFO): - console.print(f"[{UIColors.INFO}]{icon}[/{UIColors.INFO}] [bold]{message}[/bold]") + def print_info(message: str, icon: str = ""): + """Print info message""" + if icon: + console.print(f"[{UIColors.INFO}]{icon}[/{UIColors.INFO}] [bold]{message}[/bold]") + else: + console.print(f"[{UIColors.INFO}]{message}[/{UIColors.INFO}]") - @staticmethod - def print_step(step_number: int, total_steps: int, description: str, icon: str = ""): - """Print step information with enhanced progress indicator""" - icon_str = f"{icon} " if icon else "" - percentage = int((step_number / total_steps) * 100) - console.print( - f"[{UIColors.HIGHLIGHT}]Step {step_number}/{total_steps}[/{UIColors.HIGHLIGHT}] " - f"[dim]({percentage}%)[/dim] {icon_str}{description}" - ) - - @staticmethod - def print_progress_bar(step_number: int, total_steps: int, width: int = 40): - """Print a single progress bar that shows overall progress""" - progress_percentage = int((step_number / total_steps) * 100) - filled = int((step_number / total_steps) * width) - bar = "█" * filled + "░" * (width - filled) - console.print( - f"\r[{UIColors.ACCENT}][{bar}][/{UIColors.ACCENT}] {progress_percentage}%", - end="", - ) + # ============================================================================ + # Progress & Headers + # ============================================================================ @staticmethod def create_live_progress(description: str = "Setup Progress", total_steps: int = 100): - """Create a live progress display that updates in place""" + """Create a live progress display""" progress = Progress( SpinnerColumn(), TextColumn("[bold blue]{task.description}"), @@ -119,66 +93,25 @@ class UIFormatter: return progress, task @staticmethod - def print_header(text: str, style: str = UIColors.PRIMARY, width: int = 70): - """Print a styled header with customizable width""" - console.print(f"\n[{style}]{'═' * width}[/{style}]") - console.print(f"[{style}]{text.center(width)}[/{style}]") - console.print(f"[{style}]{'═' * width}[/{style}]\n") - - @staticmethod - def print_subheader(text: str, style: str = UIColors.HIGHLIGHT): - """Print a styled subheader""" - console.print(f"\n[{style}]{text}[/{style}]") - console.print(f"[{UIColors.MUTED}]{'─' * len(text)}[/{UIColors.MUTED}]") + def print_header(text: str, style: str = UIColors.ACCENT): + """Print a styled header""" + console.print(f"\n[{style}]{'═' * 70}[/{style}]") + console.print(f"[{style}]{text.center(70)}[/{style}]") + console.print(f"[{style}]{'═' * 70}[/{style}]\n") @staticmethod def print_separator(char: str = "─", width: int = 70, style: str = UIColors.MUTED): - """Print a visual separator with customization""" + """Print a visual separator""" console.print(f"[{style}]{char * width}[/{style}]") - @staticmethod - def print_feature_list(features: List[str], icon: str = Icons.STAR): - """Print a styled feature list with custom icons""" - for _i, feature in enumerate(features, 1): - console.print(f"[{UIColors.ACCENT}] {icon}[/{UIColors.ACCENT}] [bold]{feature}[/bold]") + # ============================================================================ + # Panels & Tables + # ============================================================================ @staticmethod - def print_numbered_list(items: List[str], start: int = 1): - """Print a numbered list with tree-style formatting""" - total = len(items) - for i, item in enumerate(items, start): - prefix = "└─" if i == start + total - 1 else "├─" - console.print(f" [dim]{prefix}[/dim] [dim]{i}.[/dim] {item}") - - @staticmethod - def print_tree_item(text: str, is_last: bool = False, indent: int = 0, icon: str = ""): - """Print a tree-style item""" - prefix = "└─" if is_last else "├─" - icon_str = f"{icon} " if icon else "" - spacing = " " * indent - console.print(f"{spacing}[dim]{prefix}[/dim] {icon_str}{text}") - - @staticmethod - def print_code_block( - code: str, - language: str = "python", - theme: str = "monokai", - line_numbers: bool = True, - ): - """Print code with syntax highlighting and customization""" - syntax = Syntax(code, language, theme=theme, line_numbers=line_numbers, word_wrap=True) - console.print(syntax) - - @staticmethod - def print_panel(content: str, title: str = "", style: str = "cyan", border_style: str = "blue"): + def print_panel(content: str, title: str = "", border_style: str = "blue"): """Print content in a styled panel""" - panel = Panel( - content, - title=title, - border_style=border_style, - box=box.ROUNDED, - padding=(1, 2), - ) + panel = Panel(content, title=title, border_style=border_style, box=box.ROUNDED, padding=(1, 2)) console.print(panel) @staticmethod @@ -189,41 +122,29 @@ class UIFormatter: table = Table(title=title, box=box.ROUNDED, show_header=True, header_style="bold cyan") - # Add columns from first row for key in data[0].keys(): table.add_column(key.replace("_", " ").title(), style="white") - # Add rows for row in data: table.add_row(*[str(v) for v in row.values()]) console.print(table) + # ============================================================================ + # Welcome & Summary + # ============================================================================ + @staticmethod def create_welcome_panel(): - """Create an enhanced welcome panel with ASCII art header""" + """Create a minimal welcome panel""" welcome_text = Text() - ascii_art = r""" - _ _ _ _ _ - | (_) (_) (_) | - __| |_ __ _ _ __ __ _ ___ _ _ __ _| |_ - / _` | |/ _` | '_ \ / _` |/ _ \ | | '_ \| | __| - | (_| | | (_| | | | | (_| | (_) | | | | | | | |_ - \__,_| |\__,_|_| |_|\__, |\___/ |_|_| |_|_|\__| - _/ | __/ | - |__/ |___/ -""" - - welcome_text.append(ascii_art, style=UIColors.ACCENT) - welcome_text.append("\n Django Project Setup Tool\n", style="bold white") - welcome_text.append( - " Create production-ready Django projects with modern architecture\n\n", - style=UIColors.MUTED, - ) + welcome_text.append("\n ╔═══════════════════════════════════════╗\n", style=UIColors.ACCENT) + welcome_text.append(" ║ Django Init (djinit) ║\n", style=UIColors.ACCENT) + welcome_text.append(" ╚═══════════════════════════════════════╝\n\n", style=UIColors.ACCENT) + welcome_text.append(" Create production-ready Django projects\n\n", style=UIColors.MUTED) welcome_text.append(" Repository: ", style=UIColors.MUTED) welcome_text.append("https://github.com/S4NKALP/djinit\n", style="blue underline") - welcome_text.append(" License: MIT\n", style=UIColors.MUTED) return welcome_text @@ -236,94 +157,28 @@ class UIFormatter: duration: Optional[float] = None, metadata: Optional[Dict[str, Any]] = None, ): - """Create a clean completion summary with optional duration""" - console.print("\n") - + """Create a minimal completion summary""" if success: - # Success header + console.print("\n") console.print(f"[bold green]{'═' * 70}[/bold green]") - console.print(f"[bold green]{f'{Icons.PARTY} SETUP COMPLETE! {Icons.PARTY}'.center(70)}[/bold green]") + console.print(f"[bold green]{f'{Icons.PARTY} SETUP COMPLETE'.center(70)}[/bold green]") console.print(f"[bold green]{'═' * 70}[/bold green]\n") - - # Duration if provided - if duration: - console.print(f"[dim]{Icons.CLOCK} Completed in {duration:.2f} seconds[/dim]\n") - - # Project details - console.print(f"[bold cyan]{Icons.TARGET} Project Details:[/bold cyan]") - console.print(" [dim]│[/dim]") - console.print(f" [dim]├─[/dim] [bold]Directory:[/bold] [white]{project_dir}[/white]") - console.print(f" [dim]├─[/dim] [bold]Project:[/bold] [white]{project_name}[/white]") - # Predefined structure adjustments - if metadata and metadata.get("predefined_structure"): - module_name = metadata.get("project_module_name") or project_name - console.print(f" [dim]├─[/dim] [bold]Module:[/bold] [white]{module_name}[/white]") - console.print(" [dim]├─[/dim] [bold]Structure:[/bold] [white]Predefined[/white]") - # Hide empty apps list in predefined quick flow - if app_names: - console.print(f" [dim]└─[/dim] [bold]Apps:[/bold] [white]{', '.join(app_names)}[/white]") - else: - console.print(" [dim]└─[/dim] [bold]Apps:[/bold] [white]users, core[/white]") - else: - console.print(f" [dim]└─[/dim] [bold]Apps:[/bold] [white]{', '.join(app_names)}[/white]") - console.print() - - # Next steps - console.print(f"[bold cyan]{Icons.ROCKET} Next Steps:[/bold cyan]") - console.print(" [dim]│[/dim]") - console.print(" [dim]├─[/dim] [dim]1.[/dim] Navigate to your project directory") - console.print(" [dim]├─[/dim] [dim]2.[/dim] Set environment variables in .env file") - console.print(" [dim]├─[/dim] [dim]3.[/dim] Run migrations: [cyan]just migrate[/cyan]") - console.print(" [dim]├─[/dim] [dim]4.[/dim] Create superuser: [cyan]just createsuperuser[/cyan]") - console.print(" [dim]└─[/dim] [dim]5.[/dim] Start server: [cyan]just dev[/cyan]") - console.print() - - # Useful URLs - console.print(f"[bold cyan]{Icons.LINK} Useful URLs:[/bold cyan]") - console.print(" [dim]│[/dim]") - console.print(" [dim]├─[/dim] Admin: [blue]http://localhost:8000/admin/[/blue]") - console.print(" [dim]├─[/dim] API Docs: [blue]http://localhost:8000/docs/[/blue]") - console.print(" [dim]└─[/dim] API Schema: [blue]http://localhost:8000/schema/[/blue]") - console.print() - - console.print(f"[bold green]{'═' * 70}[/bold green]\n") - else: - # Error header + console.print("\n") console.print(f"[bold red]{'═' * 70}[/bold red]") console.print(f"[bold red]{'❌ SETUP FAILED'.center(70)}[/bold red]") console.print(f"[bold red]{'═' * 70}[/bold red]\n") - - console.print("[red]The setup process encountered an error.[/red]\n") - - # Troubleshooting - console.print(f"[bold yellow]{Icons.MAGNIFIER} Troubleshooting Tips:[/bold yellow]") - console.print(" [dim]│[/dim]") - console.print(" [dim]├─[/dim] Check the error messages above") - console.print(" [dim]├─[/dim] Ensure you have write permissions") - console.print(" [dim]├─[/dim] Verify Django is installed correctly") - console.print(" [dim]└─[/dim] Try running with elevated permissions") - console.print() - + console.print("[red]Setup encountered an error. Check messages above.[/red]\n") console.print(f"[bold red]{'═' * 70}[/bold red]\n") - @staticmethod - def create_progress_display(description: str = "Processing", total_steps: int = 100): - """Create a comprehensive live progress display""" - progress = Progress( - SpinnerColumn(), - TextColumn("[progress.description]{task.description}"), - BarColumn(bar_width=40), - TaskProgressColumn(), - TimeRemainingColumn(), - console=console, - ) - return progress + # ============================================================================ + # User Input + # ============================================================================ @staticmethod @contextmanager def status(message: str, spinner: str = "dots"): - """Context manager for status display with spinner""" + """Context manager for status display""" with console.status(f"[bold cyan]{message}...", spinner=spinner): yield @@ -332,60 +187,14 @@ class UIFormatter: """Display a confirmation prompt""" default_str = "Y/n" if default else "y/N" response = console.input(f"[bold cyan]?[/bold cyan] {prompt} [{default_str}]: ").strip().lower() - if not response: return default return response in ("y", "yes") @staticmethod def prompt(message: str, default: Optional[str] = None) -> str: - """Display an input prompt with optional default""" + """Display an input prompt""" if default: message = f"{message} [{default}]" response = console.input(f"[bold cyan]?[/bold cyan] {message}: ").strip() return response or default or "" - - @staticmethod - def print_command(command: str, description: Optional[str] = None): - """Print a command with optional description""" - console.print(f"[{UIColors.CODE}]$ {command}[/{UIColors.CODE}]") - if description: - console.print(f"[dim] {description}[/dim]") - - -class ProgressTracker: - """Enhanced progress tracking with context""" - - def __init__(self, total_steps: int, description: str = "Setup Progress"): - self.progress = Progress( - SpinnerColumn(), - TextColumn("[bold blue]{task.description}"), - BarColumn(bar_width=40), - TaskProgressColumn(), - TimeRemainingColumn(), - console=console, - ) - self.task = None - self.total_steps = total_steps - self.description = description - self.current_step = 0 - - def __enter__(self): - self.progress.start() - self.task = self.progress.add_task(self.description, total=self.total_steps) - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.progress.stop() - - def update(self, advance: int = 1, description: Optional[str] = None): - """Update progress with optional description""" - if description: - self.progress.update(self.task, advance=advance, description=description) - else: - self.progress.update(self.task, advance=advance) - self.current_step += advance - - def set_description(self, description: str): - """Update the progress description""" - self.progress.update(self.task, description=description) diff --git a/djinit/scripts/files.py b/djinit/scripts/files.py index 8c16467..f9b37fe 100644 --- a/djinit/scripts/files.py +++ b/djinit/scripts/files.py @@ -94,7 +94,7 @@ class FileManager: def create_env_file(self) -> bool: """Create .env.sample file with environment variables.""" context = { - "project_name": self.project_name, + "project_name": self.module_name, "use_database_url": self.metadata.get("use_database_url", True), } return self._render_and_create_file( @@ -341,7 +341,11 @@ class FileManager: # Core project files project_files = [ - ("urls.py", "config/urls/with_api.j2", {"api_module": f"{self.module_name}.api"}), + ( + "urls.py", + "config/urls/with_api.j2", + {"api_module": f"{self.module_name}.api", "comment_out_api_url": True}, + ), ("wsgi.py", "config/wsgi.j2", {}), ("asgi.py", "config/asgi.j2", {}), ] @@ -352,7 +356,9 @@ class FileManager: ) # Create app component directories - components = ["admin", "api", "models", "routes", "serializers", "tests", "views"] + # Note: routes, serializers, views are NOT created here + # They should be organized under api/ by model name (e.g., api/user/views.py) + components = ["admin", "api", "models", "tests"] self._create_subdirectories_with_init(project_dir, components, f"{self.module_name}/") # Create basic files for components to ensure valid python packages/modules @@ -365,14 +371,6 @@ class FileManager: f"Created {self.module_name}/admin/__init__.py", ) - # api - create_file_from_template( - os.path.join(project_dir, "api", "urls.py"), - "presets/predefined/api/urls.j2", - {}, - f"Created {self.module_name}/api/urls.py", - ) - # models create_file_from_template( os.path.join(project_dir, "models", "__init__.py"), @@ -381,6 +379,21 @@ class FileManager: f"Created {self.module_name}/models/__init__.py", ) + # Create README files for api and models directories + create_file_from_template( + os.path.join(project_dir, "api", "README.md"), + "components/api_readme.j2", + {"module_name": self.module_name}, + f"Created {self.module_name}/api/README.md", + ) + + create_file_from_template( + os.path.join(project_dir, "models", "README.md"), + "components/models_readme.j2", + {"module_name": self.module_name}, + f"Created {self.module_name}/models/README.md", + ) + return True def create_procfile(self) -> bool: diff --git a/djinit/templates/components/api_readme.j2 b/djinit/templates/components/api_readme.j2 new file mode 100644 index 0000000..112cbe5 --- /dev/null +++ b/djinit/templates/components/api_readme.j2 @@ -0,0 +1,76 @@ +# API Directory + +This directory contains your API endpoints organized by model/resource. + +## Structure + +For each model in your `models/` directory, create a corresponding subdirectory here with the following files: + +``` +api/ +├── __init__.py +├── urls.py # Main API URL router +└── / # e.g., user, product, order + ├── __init__.py + ├── views.py + ├── serializers.py + └── urls.py +``` + +## Example + +If you have a `User` model in `models/user.py`, create: + +### 1. `api/user/serializers.py` +```python +from rest_framework import serializers +from {{ module_name }}.models.user import User + +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = '__all__' +``` + +### 2. `api/user/views.py` +```python +from rest_framework import viewsets +from {{ module_name }}.models.user import User +from .serializers import UserSerializer + +class UserViewSet(viewsets.ModelViewSet): + queryset = User.objects.all() + serializer_class = UserSerializer +``` + +### 3. `api/user/urls.py` +```python +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from .views import UserViewSet + +router = DefaultRouter() +router.register(r'', UserViewSet, basename='user') + +urlpatterns = [ + path('', include(router.urls)), +] +``` + +### 4. Update `api/urls.py` +```python +from django.urls import path, include + +urlpatterns = [ + path('user/', include('{{ module_name }}.api.user.urls')), + # Add more API endpoints here +] +``` + +## Best Practices + +1. **One model per subdirectory**: Keep API endpoints for each model in its own subdirectory +2. **Consistent naming**: Use lowercase, plural names for API endpoints (e.g., `users/`, `products/`) +3. **Version your API**: Consider adding versioning (e.g., `api/v1/user/`) +4. **Use ViewSets**: Leverage DRF's ViewSets for standard CRUD operations +5. **Custom actions**: Add custom actions using `@action` decorator for non-CRUD operations diff --git a/djinit/templates/components/models_readme.j2 b/djinit/templates/components/models_readme.j2 new file mode 100644 index 0000000..867eebb --- /dev/null +++ b/djinit/templates/components/models_readme.j2 @@ -0,0 +1,119 @@ +# Models Directory + +This directory contains your Django models. + +## Structure + +Organize your models as individual Python files: + +``` +models/ +├── __init__.py +├── user.py +├── product.py +└── order.py +``` + +## Creating a Model + +### 1. Create the model file (e.g., `models/user.py`) + +```python +from django.db import models +from django.contrib.auth.models import AbstractUser + +class User(AbstractUser): + """Custom user model.""" + bio = models.TextField(blank=True) + avatar = models.ImageField(upload_to='avatars/', blank=True, null=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + db_table = 'users' + verbose_name = 'User' + verbose_name_plural = 'Users' + + def __str__(self): + return self.username +``` + +### 2. Import in `models/__init__.py` + +```python +from .user import User +from .product import Product +from .order import Order + +__all__ = ['User', 'Product', 'Order'] +``` + +### 3. Create corresponding API endpoints + +After creating a model, create the corresponding API structure in the `api/` directory: + +``` +api/ +└── user/ + ├── __init__.py + ├── views.py + ├── serializers.py + └── urls.py +``` + +See the `api/README.md` for detailed instructions on creating API endpoints. + +### 4. Register in admin (optional) + +Create or update `admin/__init__.py`: + +```python +from django.contrib import admin +from {{ module_name }}.models import User + +@admin.register(User) +class UserAdmin(admin.ModelAdmin): + list_display = ['username', 'email', 'created_at'] + search_fields = ['username', 'email'] + list_filter = ['is_active', 'created_at'] +``` + +## Best Practices + +1. **One model per file**: Keep each model in its own file for better organization +2. **Use meaningful names**: Choose clear, descriptive names for models and fields +3. **Add Meta class**: Define `db_table`, `verbose_name`, and other metadata +4. **Implement __str__**: Always define a meaningful `__str__` method +5. **Use mixins**: Create reusable mixins for common fields (e.g., timestamps) +6. **Add docstrings**: Document your models with clear docstrings + +## Common Model Patterns + +### Timestamped Model Mixin + +```python +class TimestampedModel(models.Model): + """Abstract base class with timestamp fields.""" + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + abstract = True +``` + +### Soft Delete Mixin + +```python +class SoftDeleteModel(models.Model): + """Abstract base class with soft delete functionality.""" + is_deleted = models.BooleanField(default=False) + deleted_at = models.DateTimeField(null=True, blank=True) + + class Meta: + abstract = True + + def delete(self, *args, **kwargs): + self.is_deleted = True + self.deleted_at = timezone.now() + self.save() +``` diff --git a/djinit/templates/config/urls/with_api.j2 b/djinit/templates/config/urls/with_api.j2 index ee6843f..d0f0202 100644 --- a/djinit/templates/config/urls/with_api.j2 +++ b/djinit/templates/config/urls/with_api.j2 @@ -19,7 +19,8 @@ urlpatterns = [ 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"), - path("api/", include("{{ api_module }}.urls")),] + {% if comment_out_api_url %}# {% endif %}path("api/", include("{{ api_module }}.urls")), +] # Don't show schema in production if settings.DEBUG: diff --git a/djinit/templates/config/urls/with_apps.j2 b/djinit/templates/config/urls/with_apps.j2 index 87da515..a299544 100644 --- a/djinit/templates/config/urls/with_apps.j2 +++ b/djinit/templates/config/urls/with_apps.j2 @@ -20,7 +20,7 @@ urlpatterns = [ path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"), path("token/blacklist/", TokenBlacklistView.as_view(), name="token_blacklist"), # App URLs -{% for app in app_names %} path("", include("{{ app }}.urls")), +{% if comment_out_api_url %}# {% endif %} {% for app in app_names %} path("", include("{{ app }}.urls")), {% endfor %}] # Don't show schema in production diff --git a/djinit/templates/project/readme.j2 b/djinit/templates/project/readme.j2 index 8533b12..93ac266 100644 --- a/djinit/templates/project/readme.j2 +++ b/djinit/templates/project/readme.j2 @@ -11,6 +11,22 @@ A Django project created with [djinitx/djinit/dj](https://github.com/S4NKALP/dji ## Setup +### Using Just (Recommended) + +1. Set environment variables in `.env` file + +2. Run setup (installs dependencies, runs migrations, creates superuser): + ```bash + just setup + ``` + +3. Start development server: + ```bash + just dev + ``` + +### Traditional Method + 1. Install dependencies: ```bash pip install -r requirements.txt @@ -33,6 +49,20 @@ A Django project created with [djinitx/djinit/dj](https://github.com/S4NKALP/dji python manage.py runserver ``` +## Available Commands + +Run `just` to see all available commands, including: + +- `just dev` - Run development server +- `just migrate` - Run migrations +- `just makemigrations` - Create migrations +- `just createsuperuser` - Create superuser +- `just test` - Run tests +- `just lint` - Lint code +- `just format` - Format code +- `just shell` - Django shell +- `just clean` - Clean cache files + ## Project Structure ``` @@ -130,12 +160,14 @@ A Django project created with [djinitx/djinit/dj](https://github.com/S4NKALP/dji │ │ ├── development.py │ │ └── production.py │ ├── admin/ -│ ├── api/ -│ ├── models/ -│ ├── routes/ -│ ├── serializers/ +│ ├── api/ # API endpoints organized by model +│ │ ├── user/ # Example: if you have a User model +│ │ │ ├── views.py +│ │ │ ├── serializers.py +│ │ │ └── urls.py +│ │ └── urls.py +│ ├── models/ # Django models │ ├── tests/ -│ ├── views/ │ ├── urls.py │ ├── wsgi.py │ └── asgi.py @@ -144,6 +176,14 @@ A Django project created with [djinitx/djinit/dj](https://github.com/S4NKALP/dji ├── pyproject.toml ├── .env.sample └── README.md + +**Note:** For the single folder layout, organize your API endpoints by model name under the `api/` directory. +For example, if you have a `User` model in `models/user.py`, create: +- `api/user/views.py` - API views for User +- `api/user/serializers.py` - Serializers for User +- `api/user/urls.py` - URL patterns for User endpoints + +See the README files in `api/` and `models/` directories for more details. {% endif %} ```