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