一键导入
django-workflow
Django framework workflow guidelines. Activate when working with Django projects, manage.py, django-admin, or Django-specific patterns.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Django framework workflow guidelines. Activate when working with Django projects, manage.py, django-admin, or Django-specific patterns.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Language-agnostic API design patterns covering REST and GraphQL, including resource naming, HTTP methods, status codes, versioning, pagination, filtering, authentication, error handling, and schema design. Activate when working with APIs, REST endpoints, GraphQL schemas, API documentation, OpenAPI/Swagger, JWT, OAuth2, endpoint design, API versioning, rate limiting, or GraphQL resolvers.
Git workflow and commit guidelines. Trigger keywords: git, commit, push, .git, version control. MUST be activated before ANY git commit, push, or version control operation. Includes security scanning for secrets (API keys, tokens, .env files), commit message formatting with HEREDOC, logical commit grouping (docs, test, feat, fix, refactor, chore, build, deps), push behavior rules, safety rules for hooks and force pushes, and CRITICAL safeguards for destructive operations (filter-branch, gc --prune, reset --hard). Activate when user requests committing changes, pushing code, creating commits, rewriting history, or performing any git operations including analyzing uncommitted changes.
Testing workflow patterns and quality standards. Activate when working with tests, test files, test directories, code quality tools, coverage reports, or testing tasks. Includes zero-warnings policy, targeted testing during development, mocking patterns, and best practices across languages.
Ansible automation workflow guidelines. Activate when working with Ansible playbooks, ansible-playbook, inventory files (.yml, .ini), or Ansible-specific patterns.
Claude Code AI-assisted development workflow. Activate when discussing Claude Code usage, AI-assisted coding, prompting strategies, or Claude Code-specific patterns.
Guidelines for containerized projects using Docker, Dockerfile, docker-compose, container, and containerization. Covers multi-stage builds, security, signal handling, entrypoint scripts, and deployment workflows.
| name | django-workflow |
| description | Django framework workflow guidelines. Activate when working with Django projects, manage.py, django-admin, or Django-specific patterns. |
| location | user |
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
| Task | Tool | Command |
|---|---|---|
| Lint | Ruff | uv run ruff check . |
| Format | Ruff | uv run ruff format . |
| Type check | django-stubs + mypy | uv run mypy . |
| Security | bandit | uv run bandit -r . |
| Test | pytest-django | uv run pytest |
| Migrations | squawk | squawk migrations/*.sql |
| Dev server | Django | uv run python manage.py runserver |
Django 5.2 introduces native composite primary key support. You SHOULD use CompositePrimaryKey for junction tables and legacy database integration:
from django.db import models
class OrderItem(models.Model):
order = models.ForeignKey("Order", on_delete=models.CASCADE)
product = models.ForeignKey("Product", on_delete=models.CASCADE)
quantity = models.PositiveIntegerField()
class Meta:
constraints = [
models.CompositePrimaryKey("order", "product"),
]
You SHOULD prefer async views for I/O-bound operations. Django 5.x has improved async ORM support:
from django.http import JsonResponse
async def fetch_items(request):
items = [item async for item in Item.objects.filter(active=True)]
return JsonResponse({"items": [i.name for i in items]})
Key async patterns:
async for with QuerySetsawait with aget(), afirst(), acount(), aexists()SynchronousOnlyOperation exceptionsYou MAY use the {% include %} tag with the only keyword to create isolated partials:
{% include "components/card.html" with title=item.title only %}
Django 5.x includes built-in CSP middleware. You SHOULD configure it in settings:
# settings/base.py
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"csp.middleware.CSPMiddleware", # or django.middleware.csp.ContentSecurityPolicyMiddleware
# ...
]
CONTENT_SECURITY_POLICY = {
"DIRECTIVES": {
"default-src": ["'self'"],
"script-src": ["'self'"],
"style-src": ["'self'", "'unsafe-inline'"],
"img-src": ["'self'", "data:", "https:"],
}
}
For simple background tasks, you MAY use Django's built-in task system (Django 5.1+):
from django.tasks import task
@task
def send_welcome_email(user_id: int) -> None:
user = User.objects.get(pk=user_id)
# Send email logic
For complex workflows, Celery remains RECOMMENDED.
Business logic MUST NOT reside in views. You MUST use the services pattern:
# services/order_service.py
from dataclasses import dataclass
from decimal import Decimal
from django.db import transaction
@dataclass
class OrderService:
"""Order-related business operations."""
@staticmethod
@transaction.atomic
def create_order(user, cart_items: list) -> "Order":
"""Create order from cart items with inventory validation."""
order = Order.objects.create(user=user, status=Order.Status.PENDING)
for item in cart_items:
if item.product.stock < item.quantity:
raise InsufficientStockError(item.product)
OrderItem.objects.create(
order=order,
product=item.product,
quantity=item.quantity,
price=item.product.price,
)
item.product.stock -= item.quantity
item.product.save(update_fields=["stock"])
return order
@staticmethod
def calculate_total(order: "Order") -> Decimal:
"""Calculate order total with discounts applied."""
return sum(item.subtotal for item in order.items.all())
Query logic MUST be encapsulated in selectors. Views MUST NOT contain complex querysets:
# selectors/product_selectors.py
from django.db.models import QuerySet, Q, Prefetch
class ProductSelectors:
"""Product query encapsulation."""
@staticmethod
def get_active_products() -> QuerySet:
return Product.objects.filter(
is_active=True,
stock__gt=0,
).select_related("category")
@staticmethod
def search_products(query: str) -> QuerySet:
return Product.objects.filter(
Q(name__icontains=query) | Q(description__icontains=query),
is_active=True,
)
@staticmethod
def get_product_with_reviews(product_id: int) -> Product:
return Product.objects.prefetch_related(
Prefetch(
"reviews",
queryset=Review.objects.filter(approved=True).order_by("-created_at"),
)
).get(pk=product_id)
Models MUST follow this field ordering:
from django.db import models
from django.utils.translation import gettext_lazy as _
class Product(models.Model):
"""Product model with standardized field ordering."""
# 1. Primary key (if custom)
# id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
# 2. Foreign keys and relations
category = models.ForeignKey(
"Category",
on_delete=models.PROTECT,
related_name="products",
)
# 3. Required fields
name = models.CharField(_("name"), max_length=200)
slug = models.SlugField(_("slug"), max_length=200, unique=True)
price = models.DecimalField(_("price"), max_digits=10, decimal_places=2)
# 4. Optional fields
description = models.TextField(_("description"), blank=True)
image = models.ImageField(_("image"), upload_to="products/", blank=True)
# 5. Boolean flags
is_active = models.BooleanField(_("active"), default=True)
is_featured = models.BooleanField(_("featured"), default=False)
# 6. Timestamps
created_at = models.DateTimeField(_("created at"), auto_now_add=True)
updated_at = models.DateTimeField(_("updated at"), auto_now=True)
# 7. Meta class
class Meta:
verbose_name = _("product")
verbose_name_plural = _("products")
ordering = ["-created_at"]
indexes = [
models.Index(fields=["slug"]),
models.Index(fields=["category", "is_active"]),
]
# 8. String representation
def __str__(self) -> str:
return self.name
# 9. Save/delete overrides
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.name)
super().save(*args, **kwargs)
# 10. Custom properties
@property
def is_in_stock(self) -> bool:
return self.stock > 0
# 11. Instance methods
def apply_discount(self, percentage: Decimal) -> Decimal:
return self.price * (1 - percentage / 100)
Settings MUST be organized in a package:
config/
├── settings/
│ ├── __init__.py # Imports from environment-specific module
│ ├── base.py # Shared settings
│ ├── local.py # Local development
│ ├── production.py # Production settings
│ └── test.py # Test settings
# config/settings/base.py
from pathlib import Path
import os
BASE_DIR = Path(__file__).resolve().parent.parent.parent
# Security - MUST be overridden in production
SECRET_KEY = "django-insecure-CHANGE-ME"
DEBUG = False
ALLOWED_HOSTS: list[str] = []
# Application definition
DJANGO_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
THIRD_PARTY_APPS = [
"rest_framework",
"django_extensions",
]
LOCAL_APPS = [
"apps.users",
"apps.products",
]
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
# config/settings/local.py
from .base import *
DEBUG = True
SECRET_KEY = "local-dev-only-key"
ALLOWED_HOSTS = ["localhost", "127.0.0.1"]
# Debug toolbar
INSTALLED_APPS += ["debug_toolbar"]
MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"]
INTERNAL_IPS = ["127.0.0.1"]
URLs MUST use include() and namespacing:
# config/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("api/v1/", include("apps.api.urls", namespace="api-v1")),
path("products/", include("apps.products.urls", namespace="products")),
path("users/", include("apps.users.urls", namespace="users")),
]
# apps/products/urls.py
from django.urls import path
from . import views
app_name = "products"
urlpatterns = [
path("", views.ProductListView.as_view(), name="list"),
path("<slug:slug>/", views.ProductDetailView.as_view(), name="detail"),
path("category/<slug:category_slug>/", views.CategoryView.as_view(), name="category"),
]
<app>:list (e.g., products:list)<app>:detail (e.g., products:detail)<app>:create<app>:update<app>:deleteYou SHOULD use CBVs for standard CRUD operations and FBVs for simple, one-off logic:
Use CBVs when:
Use FBVs when:
from django.views.generic import ListView, DetailView
from django.contrib.auth.mixins import LoginRequiredMixin
class ProductListView(ListView):
model = Product
template_name = "products/list.html"
context_object_name = "products"
paginate_by = 20
def get_queryset(self):
return ProductSelectors.get_active_products()
class ProductDetailView(DetailView):
model = Product
template_name = "products/detail.html"
slug_url_kwarg = "slug"
def get_object(self):
return ProductSelectors.get_product_with_reviews(self.kwargs["slug"])
Forms MUST separate validation from processing:
from django import forms
from django.core.exceptions import ValidationError
class OrderForm(forms.ModelForm):
"""Order creation form with custom validation."""
class Meta:
model = Order
fields = ["shipping_address", "payment_method"]
def clean_shipping_address(self):
address = self.cleaned_data["shipping_address"]
if not address.is_deliverable:
raise ValidationError("Shipping not available to this address.")
return address
def clean(self):
cleaned_data = super().clean()
# Cross-field validation here
return cleaned_data
def create_order(request):
form = OrderForm(request.POST or None)
if request.method == "POST" and form.is_valid():
order = OrderService.create_order(
user=request.user,
cart_items=request.user.cart.items.all(),
)
return redirect("orders:detail", pk=order.pk)
return render(request, "orders/create.html", {"form": form})
from django.contrib import admin
from django.utils.html import format_html
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ["name", "category", "price", "is_active", "created_at"]
list_filter = ["category", "is_active", "created_at"]
search_fields = ["name", "description"]
prepopulated_fields = {"slug": ("name",)}
readonly_fields = ["created_at", "updated_at"]
ordering = ["-created_at"]
fieldsets = (
(None, {"fields": ("name", "slug", "category")}),
("Pricing", {"fields": ("price", "discount_price")}),
("Content", {"fields": ("description", "image")}),
("Status", {"fields": ("is_active", "is_featured")}),
("Metadata", {"fields": ("created_at", "updated_at"), "classes": ("collapse",)}),
)
def thumbnail(self, obj):
if obj.image:
return format_html('<img src="{}" width="50" />', obj.image.url)
return "-"
thumbnail.short_description = "Image"
# Auto-generated, keep minimal
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("products", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="product",
name="sku",
field=models.CharField(max_length=50, null=True),
),
]
from django.db import migrations
def populate_skus(apps, schema_editor):
Product = apps.get_model("products", "Product")
for product in Product.objects.filter(sku__isnull=True):
product.sku = f"SKU-{product.pk:06d}"
product.save(update_fields=["sku"])
def reverse_skus(apps, schema_editor):
Product = apps.get_model("products", "Product")
Product.objects.update(sku=None)
class Migration(migrations.Migration):
dependencies = [
("products", "0002_add_sku_field"),
]
operations = [
migrations.RunPython(populate_skus, reverse_skus),
]
This MUST be done in three migrations:
# tests/test_services/test_order_service.py
import pytest
from decimal import Decimal
from apps.orders.services import OrderService
@pytest.mark.django_db
class TestOrderService:
def test_create_order_success(self, user, cart_with_items):
order = OrderService.create_order(user, cart_with_items)
assert order.user == user
assert order.items.count() == len(cart_with_items)
def test_create_order_insufficient_stock(self, user, cart_with_unavailable_item):
with pytest.raises(InsufficientStockError):
OrderService.create_order(user, cart_with_unavailable_item)
# conftest.py
import pytest
from django.contrib.auth import get_user_model
@pytest.fixture
def user(db):
User = get_user_model()
return User.objects.create_user(
email="test@example.com",
password="testpass123",
)
@pytest.fixture
def product(db, category):
return Product.objects.create(
name="Test Product",
slug="test-product",
category=category,
price=Decimal("29.99"),
stock=10,
)
DEBUG = False in productionSECRET_KEY from environment variableALLOWED_HOSTS configuredSECURE_SSL_REDIRECT = True)X_FRAME_OPTIONS)