chore(djinit/templates): purge obsolete template components
Remove legacy template placeholders that are no longer used in the project scaffold. - New Features: - Enhancements: Removed unused template files - Bug Fixes:
This commit is contained in:
46 files changed
-1870
No files matched your search
@@ -1,5 +0,0 @@
|
||||
"""generated with djinit"""
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
@@ -1,76 +0,0 @@
|
||||
# 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
|
||||
@@ -1,12 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
|
||||
class [[ app_config_name ]]Config(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
# @IF app_module
|
||||
name = '[[ app_module ]]'
|
||||
# @ENDIF
|
||||
# @IF not app_module
|
||||
name = '[[ app_name ]]'
|
||||
# @ENDIF
|
||||
@@ -1,3 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
@@ -1,119 +0,0 @@
|
||||
# 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()
|
||||
```
|
||||
@@ -1,8 +0,0 @@
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
router = DefaultRouter()
|
||||
|
||||
# Register your viewsets here
|
||||
# router.register(r'endpoint', YourViewSet, basename='your-endpoint')
|
||||
|
||||
urlpatterns = router.urls
|
||||
@@ -1,3 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
# Create your serializers here.
|
||||
@@ -1,3 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -1,3 +0,0 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
@@ -1,11 +0,0 @@
|
||||
"""generated with djinit"""
|
||||
|
||||
import os
|
||||
import environ
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
env = environ.Env()
|
||||
environ.Env.read_env()
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", env("DJANGO_SETTINGS_MODULE", default="[[ project_name ]].settings.production"))
|
||||
application = get_asgi_application()
|
||||
@@ -1,179 +0,0 @@
|
||||
"""
|
||||
generated with djinit
|
||||
Common settings shared between development and production environment
|
||||
"""
|
||||
|
||||
import os
|
||||
from datetime import timedelta
|
||||
from pathlib import Path
|
||||
|
||||
import environ
|
||||
|
||||
env = environ.Env()
|
||||
environ.Env.read_env()
|
||||
|
||||
# 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 = [
|
||||
# @LOOP app in app_names "[[ app ]]",
|
||||
# @ENDLOOP
|
||||
]
|
||||
|
||||
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 = "[[ 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 = "[[ 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": "[[ project_name ]] API",
|
||||
"DESCRIPTION": "API documentation for [[ 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_ALLOW_METHODS = [
|
||||
"DELETE",
|
||||
"GET",
|
||||
"OPTIONS",
|
||||
"PATCH",
|
||||
"POST",
|
||||
"PUT",
|
||||
]
|
||||
|
||||
CORS_ALLOW_HEADERS = [
|
||||
"accept",
|
||||
"accept-encoding",
|
||||
"authorization",
|
||||
"content-type",
|
||||
"dnt",
|
||||
"origin",
|
||||
"user-agent",
|
||||
"x-csrftoken",
|
||||
"x-requested-with",
|
||||
]
|
||||
|
||||
CORS_PREFLIGHT_MAX_AGE = 86400 # 24 hours
|
||||
# Security Settings
|
||||
SECURE_BROWSER_XSS_FILTER = True
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||||
X_FRAME_OPTIONS = "DENY"
|
||||
@@ -1,42 +0,0 @@
|
||||
"""generated with djinit"""
|
||||
|
||||
from .base import *
|
||||
|
||||
SECRET_KEY = "[[ 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",
|
||||
"OPTIONS": {
|
||||
"transaction_mode": "IMMEDIATE",
|
||||
"timeout": 5, # seconds
|
||||
"init_command": """
|
||||
PRAGMA journal_mode=WAL;
|
||||
PRAGMA synchronous=NORMAL;
|
||||
PRAGMA mmap_size = 134217728;
|
||||
PRAGMA journal_size_limit = 27103364;
|
||||
PRAGMA cache_size=2000;
|
||||
""",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
# Email backend for development
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
|
||||
# CORS settings for development
|
||||
# Allow all origins in development (convenient but not secure)
|
||||
CORS_ALLOW_ALL_ORIGINS = True
|
||||
CORS_ALLOW_CREDENTIALS = True
|
||||
|
||||
# Disable HTTPS redirect in development
|
||||
SECURE_SSL_REDIRECT = False
|
||||
@@ -1,65 +0,0 @@
|
||||
"""generated with djinit"""
|
||||
|
||||
from .base import *
|
||||
import dj_database_url
|
||||
|
||||
DEBUG = False
|
||||
SECRET_KEY = env('SECRET_KEY')
|
||||
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=[])
|
||||
|
||||
# Database
|
||||
# @IF use_database_url
|
||||
# Using dj_database_url (recommended for production)
|
||||
DATABASES = {
|
||||
'default': env.db('DATABASE_URL')
|
||||
}
|
||||
# @ELSE
|
||||
# Using individual database parameters
|
||||
DATABASES = {
|
||||
'default': {
|
||||
# @IF database_type == 'mysql'
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'PORT': env('DB_PORT', default='3306'),
|
||||
# @ELSE
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'PORT': env('DB_PORT', default='5432'),
|
||||
# @ENDIF
|
||||
'NAME': env('DB_NAME'),
|
||||
'USER': env('DB_USER'),
|
||||
'PASSWORD': env('DB_PASSWORD'),
|
||||
'HOST': env('DB_HOST'),
|
||||
}
|
||||
}
|
||||
# @ENDIF
|
||||
|
||||
# CORS settings for production
|
||||
# Filter out empty strings from FRONTEND_URL
|
||||
CORS_ALLOWED_ORIGINS = env.list('FRONTEND_URL', default=[])
|
||||
CORS_ALLOW_CREDENTIALS = True
|
||||
|
||||
# Additional CORS security for production
|
||||
CORS_ALLOW_PRIVATE_NETWORK = False
|
||||
|
||||
# Email settings
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
EMAIL_HOST = env('EMAIL_HOST')
|
||||
EMAIL_PORT = env.int('EMAIL_PORT', default=587)
|
||||
EMAIL_USE_TLS = env.bool('EMAIL_USE_TLS', default=True)
|
||||
EMAIL_HOST_USER = env('EMAIL_HOST_USER')
|
||||
EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD')
|
||||
DEFAULT_FROM_EMAIL = env('EMAIL_HOST_USER')
|
||||
SERVER_EMAIL = env('EMAIL_HOST_USER')
|
||||
|
||||
# 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'
|
||||
@@ -1,103 +0,0 @@
|
||||
# @IF url_type == 'project'
|
||||
"""generated with djinit"""
|
||||
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
# @IF include_api or include_apps
|
||||
from django.urls import include
|
||||
# @ENDIF
|
||||
# @IF include_api
|
||||
from django.conf import settings
|
||||
from drf_spectacular.views import (
|
||||
SpectacularAPIView,
|
||||
SpectacularSwaggerView,
|
||||
)
|
||||
from rest_framework_simplejwt.views import (
|
||||
TokenObtainPairView,
|
||||
TokenRefreshView,
|
||||
TokenBlacklistView,
|
||||
)
|
||||
# @ENDIF
|
||||
|
||||
urlpatterns = [
|
||||
path("admin/", admin.site.urls),
|
||||
# @IF include_api
|
||||
# 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"),
|
||||
# @ENDIF
|
||||
# @IF include_api and comment_out_api_url
|
||||
# path("api/", include("[[ api_module ]].urls")),
|
||||
# @ENDIF
|
||||
# @IF include_api and not comment_out_api_url
|
||||
path("api/", include("[[ api_module ]].urls")),
|
||||
# @ENDIF
|
||||
# @IF include_apps
|
||||
# App URLs
|
||||
# @LOOP app in app_names
|
||||
path("", include("[[ app ]].urls")),
|
||||
# @ENDLOOP
|
||||
# @ENDIF
|
||||
]
|
||||
# @IF include_api
|
||||
|
||||
# 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")),
|
||||
]
|
||||
# @ENDIF
|
||||
# @ENDIF
|
||||
# @IF url_type == 'api_root'
|
||||
from django.urls import include, path
|
||||
|
||||
app_name = "[[ app_name ]]"
|
||||
|
||||
urlpatterns = [
|
||||
path("[[ version ]]/", include("[[ api_module ]].[[ version ]].urls")),
|
||||
]
|
||||
# @ENDIF
|
||||
# @IF url_type == 'api_version'
|
||||
from django.urls import include, path
|
||||
|
||||
app_name = "[[ app_name ]]"
|
||||
|
||||
urlpatterns = [
|
||||
# @LOOP app in app_list
|
||||
path("[[ app ]]/", include("[[ app_module ]].[[ app ]].urls")),
|
||||
# @ENDLOOP
|
||||
]
|
||||
# @ENDIF
|
||||
# @IF url_type == 'drf_router'
|
||||
from rest_framework.routers import DefaultRouter
|
||||
# @IF has_viewset
|
||||
from [[ app_module ]].views.[[ view_file ]] import [[ viewset_name ]]
|
||||
# @ENDIF
|
||||
# @IF has_app_name
|
||||
|
||||
app_name = "[[ app_name ]]"
|
||||
# @ENDIF
|
||||
|
||||
router = DefaultRouter()
|
||||
# @IF has_viewset
|
||||
router.register(r"[[ router_prefix ]]", [[ viewset_name ]], basename="[[ basename ]]")
|
||||
# @ENDIF
|
||||
|
||||
urlpatterns = [
|
||||
# @IF has_include
|
||||
path("", include(router.urls)),
|
||||
# @ENDIF
|
||||
]
|
||||
# @IF not has_include
|
||||
urlpatterns = router.urls
|
||||
# @ENDIF
|
||||
# @ENDIF
|
||||
# @IF url_type == 'component'
|
||||
from django.urls import path
|
||||
|
||||
urlpatterns = [
|
||||
# Add your URL patterns here
|
||||
]
|
||||
# @ENDIF
|
||||
@@ -1,12 +0,0 @@
|
||||
"""generated with djinit"""
|
||||
|
||||
import os
|
||||
import environ
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
env = environ.Env()
|
||||
environ.Env.read_env()
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", env("DJANGO_SETTINGS_MODULE", default="[[ project_name ]].settings.production"))
|
||||
|
||||
application = get_wsgi_application()
|
||||
@@ -1,18 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class [[ model_class_name ]](models.Model):
|
||||
"""[[ model_class_name ]] model."""
|
||||
|
||||
# Add your fields here
|
||||
name = models.CharField(max_length=255)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "[[ model_class_name ]]"
|
||||
verbose_name_plural = "[[ model_class_name ]]s"
|
||||
ordering = ["-created_at"]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@@ -1,11 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
from [[ app_module ]].models.[[ app_name ]] import [[ model_class_name ]]
|
||||
|
||||
|
||||
class [[ model_class_name ]]Serializer(serializers.ModelSerializer):
|
||||
"""Serializer for [[ model_class_name ]] model."""
|
||||
|
||||
class Meta:
|
||||
model = [[ model_class_name ]]
|
||||
fields = "__all__"
|
||||
read_only_fields = ["id", "created_at", "updated_at"]
|
||||
@@ -1,36 +0,0 @@
|
||||
from [[ app_module ]].models.[[ app_name ]] import [[ model_class_name ]]
|
||||
|
||||
|
||||
class [[ model_class_name ]]Service:
|
||||
"""Service class for [[ model_class_name ]] business logic."""
|
||||
|
||||
@staticmethod
|
||||
def get_all():
|
||||
"""Get all [[ model_class_name ]] instances."""
|
||||
return [[ model_class_name ]].objects.all()
|
||||
|
||||
@staticmethod
|
||||
def get_by_id(id):
|
||||
"""Get [[ model_class_name ]] by ID."""
|
||||
try:
|
||||
return [[ model_class_name ]].objects.get(id=id)
|
||||
except [[ model_class_name ]].DoesNotExist:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def create(data):
|
||||
"""Create a new [[ model_class_name ]] instance."""
|
||||
return [[ model_class_name ]].objects.create(**data)
|
||||
|
||||
@staticmethod
|
||||
def update(instance, data):
|
||||
"""Update an existing [[ model_class_name ]] instance."""
|
||||
for key, value in data.items():
|
||||
setattr(instance, key, value)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
@staticmethod
|
||||
def delete(instance):
|
||||
"""Delete a [[ model_class_name ]] instance."""
|
||||
instance.delete()
|
||||
@@ -1,55 +0,0 @@
|
||||
from rest_framework.test import APITestCase
|
||||
from rest_framework import status
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from [[ app_module ]].models.[[ app_name ]] import [[ model_class_name ]]
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class [[ model_class_name ]]APITestCase(APITestCase):
|
||||
"""Test cases for [[ model_class_name ]] API endpoints."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.user = User.objects.create_user(
|
||||
username="testuser", password="testpass123"
|
||||
)
|
||||
self.client.force_authenticate(user=self.user)
|
||||
self.test_data = {"name": "Test [[ model_class_name ]]"}
|
||||
|
||||
def test_create_[[ app_name ]](self):
|
||||
"""Test creating a [[ model_class_name ]]."""
|
||||
response = self.client.post("/api/v1/[[ app_name ]]/", self.test_data)
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
self.assertEqual([[ model_class_name ]].objects.count(), 1)
|
||||
|
||||
def test_list_[[ app_name ]]s(self):
|
||||
"""Test listing all [[ model_class_name ]]s."""
|
||||
[[ model_class_name ]].objects.create(**self.test_data)
|
||||
response = self.client.get("/api/v1/[[ app_name ]]/")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(len(response.data), 1)
|
||||
|
||||
def test_retrieve_[[ app_name ]](self):
|
||||
"""Test retrieving a single [[ model_class_name ]]."""
|
||||
instance = [[ model_class_name ]].objects.create(**self.test_data)
|
||||
response = self.client.get(f"/api/v1/[[ app_name ]]/{instance.id}/")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data["name"], self.test_data["name"])
|
||||
|
||||
def test_update_[[ app_name ]](self):
|
||||
"""Test updating a [[ model_class_name ]]."""
|
||||
instance = [[ model_class_name ]].objects.create(**self.test_data)
|
||||
updated_data = {"name": "Updated [[ model_class_name ]]"}
|
||||
response = self.client.patch(f"/api/v1/[[ app_name ]]/{instance.id}/", updated_data)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
instance.refresh_from_db()
|
||||
self.assertEqual(instance.name, updated_data["name"])
|
||||
|
||||
def test_delete_[[ app_name ]](self):
|
||||
"""Test deleting a [[ model_class_name ]]."""
|
||||
instance = [[ model_class_name ]].objects.create(**self.test_data)
|
||||
response = self.client.delete(f"/api/v1/[[ app_name ]]/{instance.id}/")
|
||||
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||
self.assertEqual([[ model_class_name ]].objects.count(), 0)
|
||||
@@ -1,62 +0,0 @@
|
||||
from rest_framework import viewsets, status
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
|
||||
from [[ app_module ]].models.[[ app_name ]] import [[ model_class_name ]]
|
||||
from [[ app_module ]].serializers.[[ app_name ]]_serializer import [[ model_class_name ]]Serializer
|
||||
from [[ app_module ]].services.[[ app_name ]]_service import [[ model_class_name ]]Service
|
||||
|
||||
|
||||
class [[ model_class_name ]]ViewSet(viewsets.ModelViewSet):
|
||||
"""ViewSet for [[ model_class_name ]] CRUD operations."""
|
||||
|
||||
queryset = [[ model_class_name ]].objects.all()
|
||||
serializer_class = [[ model_class_name ]]Serializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def list(self, request):
|
||||
"""List all [[ model_class_name ]] instances."""
|
||||
instances = [[ model_class_name ]]Service.get_all()
|
||||
serializer = self.get_serializer(instances, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
def retrieve(self, request, pk=None):
|
||||
"""Retrieve a single [[ model_class_name ]] instance."""
|
||||
instance = [[ model_class_name ]]Service.get_by_id(pk)
|
||||
if not instance:
|
||||
return Response(
|
||||
{"error": "[[ model_class_name ]] not found"}, status=status.HTTP_404_NOT_FOUND
|
||||
)
|
||||
serializer = self.get_serializer(instance)
|
||||
return Response(serializer.data)
|
||||
|
||||
def create(self, request):
|
||||
"""Create a new [[ model_class_name ]] instance."""
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
instance = [[ model_class_name ]]Service.create(serializer.validated_data)
|
||||
return Response(
|
||||
self.get_serializer(instance).data, status=status.HTTP_201_CREATED
|
||||
)
|
||||
|
||||
def update(self, request, pk=None):
|
||||
"""Update an existing [[ model_class_name ]] instance."""
|
||||
instance = [[ model_class_name ]]Service.get_by_id(pk)
|
||||
if not instance:
|
||||
return Response(
|
||||
{"error": "[[ model_class_name ]] not found"}, status=status.HTTP_404_NOT_FOUND
|
||||
)
|
||||
serializer = self.get_serializer(instance, data=request.data, partial=True)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
updated_instance = [[ model_class_name ]]Service.update(instance, serializer.validated_data)
|
||||
return Response(self.get_serializer(updated_instance).data)
|
||||
|
||||
def destroy(self, request, pk=None):
|
||||
"""Delete a [[ model_class_name ]] instance."""
|
||||
instance = [[ model_class_name ]]Service.get_by_id(pk)
|
||||
if not instance:
|
||||
return Response(
|
||||
{"error": "[[ model_class_name ]] not found"}, status=status.HTTP_404_NOT_FOUND
|
||||
)
|
||||
[[ model_class_name ]]Service.delete(instance)
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
@@ -1,9 +0,0 @@
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db import models
|
||||
|
||||
|
||||
class User(AbstractUser):
|
||||
"""Custom user model extending Django's AbstractUser."""
|
||||
|
||||
# Add custom fields here if needed
|
||||
pass
|
||||
@@ -1,9 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from apps.users.models.user import User
|
||||
|
||||
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ["id", "username", "email", "first_name", "last_name"]
|
||||
@@ -1,10 +0,0 @@
|
||||
from typing import Optional
|
||||
|
||||
from apps.users.models.user import User
|
||||
|
||||
|
||||
def get_user_by_id(user_id: int) -> Optional[User]:
|
||||
try:
|
||||
return User.objects.get(id=user_id)
|
||||
except User.DoesNotExist:
|
||||
return None
|
||||
@@ -1,12 +0,0 @@
|
||||
import pytest
|
||||
from django.urls import reverse
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_users_list_empty():
|
||||
client = APIClient()
|
||||
url = reverse("v1:users-list")
|
||||
response = client.get(url)
|
||||
assert response.status_code == 200
|
||||
assert response.json() == []
|
||||
@@ -1,13 +0,0 @@
|
||||
from rest_framework import status, viewsets
|
||||
from rest_framework.response import Response
|
||||
|
||||
from apps.users.models.user import User
|
||||
from apps.users.serializers.user_serializer import UserSerializer
|
||||
|
||||
|
||||
class UserViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
queryset = User.objects.all()
|
||||
serializer_class = UserSerializer
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
return super().list(request, *args, **kwargs)
|
||||
@@ -1,5 +0,0 @@
|
||||
class ApplicationError(Exception):
|
||||
"""Base application exception."""
|
||||
|
||||
def __init__(self, message: str):
|
||||
super().__init__(message)
|
||||
@@ -1,14 +0,0 @@
|
||||
import logging
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RequestLoggerMiddleware:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
logger.info("%s %s", request.method, request.path)
|
||||
response = self.get_response(request)
|
||||
return response
|
||||
@@ -1,9 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class TimestampedModel(models.Model):
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
@@ -1,17 +0,0 @@
|
||||
# @IF include_header
|
||||
"""generated with djinit"""
|
||||
|
||||
# @ENDIF
|
||||
from rest_framework.response import Response
|
||||
|
||||
|
||||
def success(data=None, status_code=200):
|
||||
# @IF include_docstrings """Return a successful response."""
|
||||
# @ENDIF
|
||||
return Response({"success": True, "data": data}, status=status_code)
|
||||
|
||||
|
||||
def error(message, status_code=400):
|
||||
# @IF include_docstrings """Return an error response."""
|
||||
# @ENDIF
|
||||
return Response({"success": False, "message": message}, status=status_code)
|
||||
@@ -1,8 +0,0 @@
|
||||
"""generated with djinit"""
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CoreConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "apps.core"
|
||||
@@ -1,4 +0,0 @@
|
||||
from .base import AbstractBaseModel
|
||||
from .mixins import TimestampedModel
|
||||
|
||||
__all__ = ["AbstractBaseModel", "TimestampedModel"]
|
||||
@@ -1,13 +0,0 @@
|
||||
"""generated with djinit"""
|
||||
|
||||
from django.db import models
|
||||
|
||||
|
||||
class AbstractBaseModel(models.Model):
|
||||
"""Abstract base model with common fields."""
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
@@ -1,13 +0,0 @@
|
||||
"""generated with djinit"""
|
||||
|
||||
from django.db import models
|
||||
|
||||
|
||||
class TimestampedModel(models.Model):
|
||||
"""Mixin for models that need timestamp fields."""
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
@@ -1,3 +0,0 @@
|
||||
from .responses import success, error
|
||||
|
||||
__all__ = ["success", "error"]
|
||||
@@ -1,109 +0,0 @@
|
||||
name: Django CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, develop ]
|
||||
pull_request:
|
||||
branches: [ main, develop ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.12", "3.13"]
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: test_db
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python {{ '{{ matrix.python-version }}' }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: {{ '{{ matrix.python-version }}' }}
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v4
|
||||
with:
|
||||
version: "latest"
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync
|
||||
|
||||
- name: Run linting
|
||||
run: uv run ruff check .
|
||||
|
||||
- name: Format check
|
||||
run: uv run ruff format --check .
|
||||
|
||||
- name: Set up PostgreSQL
|
||||
env:
|
||||
DB_NAME: test_db
|
||||
DB_USER: postgres
|
||||
DB_PASSWORD: postgres
|
||||
DB_HOST: localhost
|
||||
DB_PORT: 5432
|
||||
run: |
|
||||
echo "PostgreSQL is ready"
|
||||
|
||||
- name: Run Django tests
|
||||
env:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
ALLOWED_HOSTS: localhost
|
||||
DB_NAME: test_db
|
||||
DB_USER: postgres
|
||||
DB_PASSWORD: postgres
|
||||
DB_HOST: localhost
|
||||
DB_PORT: 5432
|
||||
run: |
|
||||
uv run python manage.py check
|
||||
uv run python manage.py test
|
||||
|
||||
- name: Check migrations
|
||||
env:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
ALLOWED_HOSTS: localhost
|
||||
DB_NAME: test_db
|
||||
DB_USER: postgres
|
||||
DB_PASSWORD: postgres
|
||||
DB_HOST: localhost
|
||||
DB_PORT: 5432
|
||||
run: |
|
||||
uv run python manage.py makemigrations --check --dry-run
|
||||
|
||||
security:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v4
|
||||
with:
|
||||
version: "latest"
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync
|
||||
|
||||
- name: Run security check
|
||||
run: uv run python manage.py check --deploy
|
||||
@@ -1,133 +0,0 @@
|
||||
# GitLab CI/CD configuration for [[ project_name ]]
|
||||
|
||||
stages:
|
||||
- test
|
||||
- lint
|
||||
- security
|
||||
|
||||
variables:
|
||||
POSTGRES_DB: test_db
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/test_db"
|
||||
|
||||
# Use Docker-in-Docker
|
||||
services:
|
||||
- postgres:15
|
||||
|
||||
before_script:
|
||||
- python --version
|
||||
- pip install uv
|
||||
- uv sync
|
||||
|
||||
# Lint stage
|
||||
lint:
|
||||
stage: lint
|
||||
image: python:3.13
|
||||
script:
|
||||
- uv run ruff check .
|
||||
- uv run ruff format --check .
|
||||
only:
|
||||
- branches
|
||||
- merge_requests
|
||||
|
||||
# Test stage
|
||||
test:
|
||||
stage: test
|
||||
image: python:3.13
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
ALLOWED_HOSTS: localhost
|
||||
script:
|
||||
- uv run python manage.py check
|
||||
- uv run python manage.py migrate
|
||||
- uv run python manage.py test
|
||||
only:
|
||||
- branches
|
||||
- merge_requests
|
||||
|
||||
# Multiple Python versions
|
||||
test:python310:
|
||||
stage: test
|
||||
image: python:3.10
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
ALLOWED_HOSTS: localhost
|
||||
script:
|
||||
- pip install uv
|
||||
- uv sync
|
||||
- uv run python manage.py check
|
||||
- uv run python manage.py migrate
|
||||
- uv run python manage.py test
|
||||
only:
|
||||
- branches
|
||||
- merge_requests
|
||||
|
||||
test:python311:
|
||||
stage: test
|
||||
image: python:3.11
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
ALLOWED_HOSTS: localhost
|
||||
script:
|
||||
- pip install uv
|
||||
- uv sync
|
||||
- uv run python manage.py check
|
||||
- uv run python manage.py migrate
|
||||
- uv run python manage.py test
|
||||
only:
|
||||
- branches
|
||||
- merge_requests
|
||||
|
||||
test:python312:
|
||||
stage: test
|
||||
image: python:3.12
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
ALLOWED_HOSTS: localhost
|
||||
script:
|
||||
- pip install uv
|
||||
- uv sync
|
||||
- uv run python manage.py check
|
||||
- uv run python manage.py migrate
|
||||
- uv run python manage.py test
|
||||
only:
|
||||
- branches
|
||||
- merge_requests
|
||||
|
||||
test:python313:
|
||||
stage: test
|
||||
image: python:3.13
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
ALLOWED_HOSTS: localhost
|
||||
script:
|
||||
- pip install uv
|
||||
- uv sync
|
||||
- uv run python manage.py check
|
||||
- uv run python manage.py migrate
|
||||
- uv run python manage.py test
|
||||
only:
|
||||
- branches
|
||||
- merge_requests
|
||||
|
||||
# Security check
|
||||
security:
|
||||
stage: security
|
||||
image: python:3.13
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: [[ project_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
script:
|
||||
- pip install uv
|
||||
- uv sync
|
||||
- uv run python manage.py check --deploy
|
||||
- uv run python manage.py validate_templates
|
||||
only:
|
||||
- main
|
||||
- develop
|
||||
@@ -1,32 +0,0 @@
|
||||
# Django settings
|
||||
DJANGO_SETTINGS_MODULE=[[ project_name ]].settings.development
|
||||
SECRET_KEY= your-secret-key
|
||||
ALLOWED_HOSTS=api.your-domain.com,www.your-domain.com
|
||||
|
||||
# @IF use_database_url
|
||||
# Database URL (recommended for production)
|
||||
# @IF database_type == 'mysql'
|
||||
DATABASE_URL=mysql://user:password@localhost:3306/[[ project_name ]]
|
||||
# @ELSE
|
||||
DATABASE_URL=postgres://user:password@localhost:5432/[[ project_name ]]
|
||||
# @ENDIF
|
||||
# @ELSE
|
||||
# Database (individual parameters)
|
||||
DB_NAME=
|
||||
DB_USER=
|
||||
DB_PASSWORD=
|
||||
DB_HOST=
|
||||
DB_PORT=
|
||||
# @ENDIF
|
||||
|
||||
# Frontend URL
|
||||
FRONTEND_URL=http://frontend.your-domain.com
|
||||
|
||||
# Email settings
|
||||
EMAIL_HOST=
|
||||
EMAIL_PORT=
|
||||
EMAIL_USE_TLS=
|
||||
EMAIL_HOST_USER=
|
||||
EMAIL_HOST_PASSWORD=
|
||||
DEFAULT_FROM_EMAIL=
|
||||
SERVER_EMAIL=
|
||||
@@ -1,93 +0,0 @@
|
||||
#--------------------------------------------------#
|
||||
# The following was generated by djinit: #
|
||||
#--------------------------------------------------#
|
||||
|
||||
# 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
|
||||
Whitespace-only changes.
@@ -1,219 +0,0 @@
|
||||
# Justfile for [[ project_name ]]
|
||||
# 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 {{ '{{' }}HOST{{ '}}' }}:{{ '{{' }}PORT{{ '}}' }}
|
||||
|
||||
# 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 {{ '{{' }}APP{{ '}}' }}
|
||||
|
||||
# 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 [[ project_name ]].wsgi:application --bind 0.0.0.0:8000
|
||||
|
||||
# Start production server with gunicorn workers
|
||||
server-prod WORKERS="4":
|
||||
uv run gunicorn [[ project_name ]].wsgi:application --bind 0.0.0.0:8000 --workers {{ '{{' }}WORKERS{{ '}}' }}
|
||||
|
||||
# Start production server with gunicorn and reload on file changes
|
||||
server-watch:
|
||||
uv run gunicorn [[ project_name ]].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/
|
||||
|
||||
# Pin dependencies in pyproject.toml using versions from uv.lock, keeping >= and extras
|
||||
pin-deps:
|
||||
#!/usr/bin/env -S uv run python
|
||||
import tomllib, re
|
||||
from pathlib import Path
|
||||
|
||||
lock = Path("uv.lock")
|
||||
if not lock.exists():
|
||||
print("uv.lock not found — run `uv sync` first")
|
||||
raise SystemExit(0)
|
||||
|
||||
data = tomllib.loads(lock.read_text())
|
||||
packages = {p["name"].lower(): p["version"] for p in data.get("package", [])}
|
||||
|
||||
py = Path("pyproject.toml")
|
||||
lines = py.read_text().splitlines()
|
||||
|
||||
pattern = re.compile(r'^(\s*)"([^"]+)"(,?)')
|
||||
|
||||
out = []
|
||||
for line in lines:
|
||||
m = pattern.match(line)
|
||||
if not m:
|
||||
out.append(line)
|
||||
continue
|
||||
|
||||
indent, dep, comma = m.groups()
|
||||
|
||||
base = dep
|
||||
extras = ""
|
||||
operator = ">="
|
||||
version = None
|
||||
|
||||
# extras: django[postgres]
|
||||
if "[" in base and "]" in base:
|
||||
before, after = base.split("[", 1)
|
||||
extras = "[" + after.split("]")[0] + "]"
|
||||
base = before
|
||||
|
||||
# detect >= or ==
|
||||
if ">=" in dep:
|
||||
operator = ">="
|
||||
base, version = dep.split(">=")
|
||||
base = base.strip()
|
||||
elif "==" in dep:
|
||||
operator = "=="
|
||||
base, version = dep.split("==")
|
||||
base = base.strip()
|
||||
else:
|
||||
base = dep
|
||||
|
||||
base_lower = base.lower()
|
||||
new_version = packages.get(base_lower)
|
||||
|
||||
if new_version:
|
||||
new_dep = f'{base}{extras}{operator}{new_version}'
|
||||
line = f'{indent}"{new_dep}"{comma}'
|
||||
|
||||
out.append(line)
|
||||
|
||||
py.write_text("\n".join(out) + "\n")
|
||||
print("Pinned dependencies (>= preserved, extras preserved).")
|
||||
|
||||
|
||||
# Setup project for development (using uv)
|
||||
setup:
|
||||
uv sync
|
||||
just pin-deps
|
||||
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 {{ '{{' }}PACKAGE{{ '}}' }}
|
||||
|
||||
# Install a dev package
|
||||
install-dev PACKAGE:
|
||||
uv add --dev {{ '{{' }}PACKAGE{{ '}}' }}
|
||||
|
||||
# Remove a package
|
||||
remove PACKAGE:
|
||||
uv remove {{ '{{' }}PACKAGE{{ '}}' }}
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""generated with djinit"""
|
||||
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
import environ
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", os.getenv("DJANGO_SETTINGS_MODULE", "[[ project_name ]].settings.production"))
|
||||
|
||||
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__":
|
||||
env = environ.Env()
|
||||
# Read .env file, if it exists
|
||||
environ.Env.read_env()
|
||||
|
||||
main()
|
||||
@@ -1,20 +0,0 @@
|
||||
# Procfile for [[ project_name ]]
|
||||
# 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 [[ project_name ]].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 [[ project_name ]].wsgi:application --bind 0.0.0.0:$PORT --workers 4 --threads 2 --timeout 120
|
||||
# web: gunicorn [[ project_name ]].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 [[ project_name ]] worker --loglevel=info
|
||||
# beat: celery -A [[ project_name ]] beat --loglevel=info
|
||||
|
||||
# Development server (for local testing with Foreman/Overmind)
|
||||
# web: python manage.py runserver 0.0.0.0:$PORT
|
||||
@@ -1,46 +0,0 @@
|
||||
[project]
|
||||
name = "[[ package_name ]]"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
license = { text = "MIT" }
|
||||
dependencies = [
|
||||
"django",
|
||||
"django-environ",
|
||||
"django-jazzmin",
|
||||
"djangorestframework",
|
||||
"djangorestframework-simplejwt",
|
||||
"drf-spectacular",
|
||||
"django-cors-headers",
|
||||
"whitenoise",
|
||||
# @IF database_type == 'mysql'
|
||||
"mysqlclient",
|
||||
# @ENDIF
|
||||
# @IF database_type == 'postgresql'
|
||||
"psycopg2-binary",
|
||||
# @ENDIF
|
||||
"gunicorn",
|
||||
]
|
||||
|
||||
[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 = ["[[ project_name ]]"]
|
||||
@@ -1,201 +0,0 @@
|
||||
# [[ project_name ]]
|
||||
|
||||
A Django project folder structure is generated with [djinitx/djinit/dj](https://github.com/S4NKALP/djinit).
|
||||
|
||||
## Features
|
||||
|
||||
- Modern project structure with environment-specific settings
|
||||
- Pre-configured REST API with JWT authentication
|
||||
- Essential dependencies and utilities
|
||||
- Production-ready configuration
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
## 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
|
||||
- `just install <package>` - Install package
|
||||
- `just remove <package>` - Remove package
|
||||
- `just server` - Run production server
|
||||
- `just ci` - Run all linting and checks
|
||||
- `just test-coverage` - Run tests with coverage
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
# @IF predefined_structure
|
||||
[[ project_name ]]/
|
||||
├── [[ module_name ]]/
|
||||
│ ├── settings/
|
||||
│ │ ├── base.py
|
||||
│ │ ├── development.py
|
||||
│ │ └── production.py
|
||||
│ ├── urls.py
|
||||
│ ├── wsgi.py
|
||||
│ └── asgi.py
|
||||
├── apps/
|
||||
│ ├── users/
|
||||
│ │ ├── apps.py
|
||||
│ │ ├── models/
|
||||
│ │ │ └── user.py
|
||||
│ │ ├── serializers/
|
||||
│ │ │ └── user_serializer.py
|
||||
│ │ ├── services/
|
||||
│ │ │ └── user_service.py
|
||||
│ │ ├── views/
|
||||
│ │ │ └── user_view.py
|
||||
│ │ ├── urls.py
|
||||
│ │ └── tests/
|
||||
│ │ └── test_user_api.py
|
||||
│ └── core/
|
||||
│ ├── utils/
|
||||
│ │ └── responses.py
|
||||
│ ├── mixins/
|
||||
│ │ └── timestamped_model.py
|
||||
│ ├── exceptions.py
|
||||
│ └── middleware/
|
||||
│ └── request_logger.py
|
||||
├── api/
|
||||
│ ├── __init__.py
|
||||
│ ├── urls.py
|
||||
│ └── v1/
|
||||
│ ├── __init__.py
|
||||
│ └── urls.py
|
||||
├── manage.py
|
||||
├── requirements.txt
|
||||
├── pyproject.toml
|
||||
├── uv.lock
|
||||
├── .env.sample
|
||||
├── justfile
|
||||
├── Procfile
|
||||
├── runtime.txt
|
||||
└── README.md
|
||||
# @ELSEIF unified_structure
|
||||
[[ project_name ]]/
|
||||
├── apps/
|
||||
│ ├── admin/
|
||||
│ │ └── __init__.py
|
||||
│ ├── api/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── urls.py
|
||||
│ │ └── v1/
|
||||
│ │ ├── __init__.py
|
||||
│ │ └── urls.py
|
||||
│ ├── models/
|
||||
│ │ └── __init__.py
|
||||
│ ├── serializers/
|
||||
│ │ └── __init__.py
|
||||
│ ├── tests/
|
||||
│ │ └── __init__.py
|
||||
│ ├── urls/
|
||||
│ │ └── __init__.py
|
||||
│ └── views/
|
||||
│ └── __init__.py
|
||||
├── core/
|
||||
│ ├── asgi.py
|
||||
│ ├── __init__.py
|
||||
│ ├── settings/
|
||||
│ │ ├── base.py
|
||||
│ │ ├── development.py
|
||||
│ │ ├── __init__.py
|
||||
│ │ └── production.py
|
||||
│ ├── urls.py
|
||||
│ └── wsgi.py
|
||||
├── manage.py
|
||||
├── requirements.txt
|
||||
├── pyproject.toml
|
||||
├── uv.lock
|
||||
├── .env.sample
|
||||
├── justfile
|
||||
├── Procfile
|
||||
├── runtime.txt
|
||||
└── README.md
|
||||
# @ELSE
|
||||
[[ project_name ]]/
|
||||
├── [[ module_name ]]/
|
||||
│ ├── settings/
|
||||
│ │ ├── base.py
|
||||
│ │ ├── development.py
|
||||
│ │ └── production.py
|
||||
│ ├── admin/
|
||||
│ ├── 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/
|
||||
│ ├── urls.py
|
||||
│ ├── wsgi.py
|
||||
│ └── asgi.py
|
||||
├── manage.py
|
||||
├── requirements.txt
|
||||
├── pyproject.toml
|
||||
├── uv.lock
|
||||
├── .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
|
||||
```
|
||||
|
||||
## API Documentation
|
||||
|
||||
When running in development mode, API documentation is available at:
|
||||
- Swagger UI: http://localhost:8000/docs/
|
||||
- Schema: http://localhost:8000/schema/
|
||||
@@ -1,19 +0,0 @@
|
||||
#--------------------------------------------------#
|
||||
# The following was generated by djinit: #
|
||||
#--------------------------------------------------#
|
||||
|
||||
Django
|
||||
django-environ
|
||||
django-jazzmin
|
||||
djangorestframework
|
||||
djangorestframework_simplejwt
|
||||
drf-spectacular
|
||||
django-cors-headers
|
||||
whitenoise
|
||||
# @IF database_type == 'mysql'
|
||||
mysqlclient
|
||||
# @ELSE
|
||||
psycopg[binary]
|
||||
# @ENDIF
|
||||
gunicorn
|
||||
dj-database-url # @IF use_database_url
|
||||
@@ -1 +0,0 @@
|
||||
python-[[ python_version ]]
|
||||
Reference in new issue
Block a user