feat: enhance predefined project structure with API and models READMEs and updated URLs

This commit is contained in:
sankalp committed 2025-11-26 22:59:47 +05:45
1 parent f22c4613f5
commit bea8965bbc
7 files changed
+321 -263

No files matched your search

+54 -245
View File
@@ -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)
+24 -11
View File
@@ -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:
+76
View File
@@ -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
└── <model_name>/ # 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
@@ -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()
```
+2 -1
View File
@@ -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:
+1 -1
View File
@@ -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
+45 -5
View File
@@ -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 %}
```