Django REST API — 项目 CLAUDE.md¶
使用 PostgreSQL 和 Celery 的 Django REST Framework API 实战示例。 复制到项目根目录,并根据你的服务定制。
项目概述¶
技术栈: Python 3.12+、Django 5.x、Django REST Framework、PostgreSQL、Celery + Redis、pytest、Docker Compose
架构: 基于业务领域拆分为多个应用的领域驱动设计。API 层使用 DRF,异步任务使用 Celery,测试使用 pytest。所有端点返回 JSON,不进行模板渲染。
基本规则¶
Python 规则¶
- 所有函数签名使用 type hints — 使用
from __future__ import annotations - 禁止使用
print()— 使用logging.getLogger(__name__) - 字符串格式化使用 f-strings,禁止使用
%或.format() - 文件操作使用
pathlib.Path代替os.path - 使用 isort 对 import 排序:stdlib、third-party、local 顺序(由 ruff 强制)
数据库¶
- 所有查询使用 Django ORM — raw SQL 仅使用
.raw()和参数化查询 - 迁移提交到 git — 禁止在生产环境使用
--fake - 使用
select_related()和prefetch_related()防止 N+1 查询 - 所有模型必须包含
created_at和updated_at自动字段 - 为所有在
filter()、order_by()或WHERE子句中使用的字段添加索引
# 나쁜 예: N+1 쿼리
orders = Order.objects.all()
for order in orders:
print(order.customer.name) # 각 주문마다 DB를 조회함
# 좋은 예: join을 사용한 단일 쿼리
orders = Order.objects.select_related("customer").all()
认证¶
- 通过
djangorestframework-simplejwt使用 JWT — access token(15 分钟)+ refresh token(7 天) - 为所有视图指定 permission 类 — 不要依赖默认值
- 默认使用
IsAuthenticated,对象级访问添加自定义 permission - 启用 token blacklisting 以支持登出
序列化器¶
- 简单的 CRUD 使用
ModelSerializer,复杂的校验使用Serializer - 当输入/输出格式不同时,分离读 serializer 和写 serializer
- 校验在 serializer 层进行 — 保持视图精简
class CreateOrderSerializer(serializers.Serializer):
product_id = serializers.UUIDField()
quantity = serializers.IntegerField(min_value=1, max_value=100)
def validate_product_id(self, value):
if not Product.objects.filter(id=value, active=True).exists():
raise serializers.ValidationError("Product not found or inactive")
return value
class OrderDetailSerializer(serializers.ModelSerializer):
customer = CustomerSerializer(read_only=True)
product = ProductSerializer(read_only=True)
class Meta:
model = Order
fields = ["id", "customer", "product", "quantity", "total", "status", "created_at"]
错误处理¶
- 使用 DRF exception handler 以保持一致的错误响应
- 自定义业务逻辑异常定义在
core/exceptions.py中 - 不要向客户端暴露内部错误详情
# core/exceptions.py
from rest_framework.exceptions import APIException
class InsufficientStockError(APIException):
status_code = 409
default_detail = "Insufficient stock for this order"
default_code = "insufficient_stock"
代码风格¶
- 禁止在代码或注释中使用 emoji
- 最大行长度:120 个字符(由 ruff 强制)
- 类:PascalCase,函数/变量:snake_case,常量:UPPER_SNAKE_CASE
- 保持视图精简 — 业务逻辑放在服务函数或模型方法中
文件结构¶
config/
settings/
base.py # 공유 설정
local.py # 개발 환경 오버라이드 (DEBUG=True)
production.py # 프로덕션 설정
urls.py # 루트 URL 설정
celery.py # Celery 앱 설정
apps/
accounts/ # 사용자 인증, 회원가입, 프로필
models.py
serializers.py
views.py
services.py # 비즈니스 로직
tests/
test_views.py
test_services.py
factories.py # Factory Boy 팩토리
orders/ # 주문 관리
models.py
serializers.py
views.py
services.py
tasks.py # Celery 작업
tests/
products/ # 상품 카탈로그
models.py
serializers.py
views.py
tests/
core/
exceptions.py # 커스텀 API 예외
permissions.py # 공유 permission 클래스
pagination.py # 커스텀 페이지네이션
middleware.py # 요청 로깅, 타이밍
tests/
主要模式¶
服务层¶
# apps/orders/services.py
from django.db import transaction
def create_order(*, customer, product_id: uuid.UUID, quantity: int) -> Order:
"""재고 검증과 결제 보류를 포함한 주문 생성."""
with transaction.atomic():
product = Product.objects.select_for_update().get(id=product_id)
if product.stock < quantity:
raise InsufficientStockError()
order = Order.objects.create(
customer=customer,
product=product,
quantity=quantity,
total=product.price * quantity,
)
product.stock -= quantity
product.save(update_fields=["stock", "updated_at"])
# 비동기: 주문 확인 이메일 발송
send_order_confirmation.delay(order.id)
return order
视图模式¶
# apps/orders/views.py
class OrderViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
pagination_class = StandardPagination
def get_serializer_class(self):
if self.action == "create":
return CreateOrderSerializer
return OrderDetailSerializer
def get_queryset(self):
return (
Order.objects
.filter(customer=self.request.user)
.select_related("product", "customer")
.order_by("-created_at")
)
def perform_create(self, serializer):
order = create_order(
customer=self.request.user,
product_id=serializer.validated_data["product_id"],
quantity=serializer.validated_data["quantity"],
)
serializer.instance = order
测试模式(pytest + Factory Boy)¶
# apps/orders/tests/factories.py
import factory
from apps.accounts.tests.factories import UserFactory
from apps.products.tests.factories import ProductFactory
class OrderFactory(factory.django.DjangoModelFactory):
class Meta:
model = "orders.Order"
customer = factory.SubFactory(UserFactory)
product = factory.SubFactory(ProductFactory, stock=100)
quantity = 1
total = factory.LazyAttribute(lambda o: o.product.price * o.quantity)
# apps/orders/tests/test_views.py
import pytest
from rest_framework.test import APIClient
@pytest.mark.django_db
class TestCreateOrder:
def setup_method(self):
self.client = APIClient()
self.user = UserFactory()
self.client.force_authenticate(self.user)
def test_create_order_success(self):
product = ProductFactory(price=29_99, stock=10)
response = self.client.post("/api/orders/", {
"product_id": str(product.id),
"quantity": 2,
})
assert response.status_code == 201
assert response.data["total"] == 59_98
def test_create_order_insufficient_stock(self):
product = ProductFactory(stock=0)
response = self.client.post("/api/orders/", {
"product_id": str(product.id),
"quantity": 1,
})
assert response.status_code == 409
def test_create_order_unauthenticated(self):
self.client.force_authenticate(None)
response = self.client.post("/api/orders/", {})
assert response.status_code == 401
环境变量¶
# Django
SECRET_KEY=
DEBUG=False
ALLOWED_HOSTS=api.example.com
# 데이터베이스
DATABASE_URL=postgres://user:pass@localhost:5432/myapp
# Redis (Celery broker + 캐시)
REDIS_URL=redis://localhost:6379/0
# JWT
JWT_ACCESS_TOKEN_LIFETIME=15 # 분
JWT_REFRESH_TOKEN_LIFETIME=10080 # 분 (7일)
# 이메일
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
EMAIL_HOST=smtp.example.com
测试策略¶
# 전체 테스트 실행
pytest --cov=apps --cov-report=term-missing
# 특정 앱 테스트 실행
pytest apps/orders/tests/ -v
# 병렬 실행
pytest -n auto
# 마지막 실행에서 실패한 테스트만 실행
pytest --lf
ECC 工作流¶
# 계획 수립
/plan "Add order refund system with Stripe integration"
# TDD로 개발
/tdd # pytest 기반 TDD 워크플로우
# 리뷰
/python-review # Python 전용 코드 리뷰
/security-scan # Django 보안 감사
/code-review # 일반 품질 검사
# 검증
/verify # 빌드, 린트, 테스트, 보안 스캔
Git 工作流¶
feat:新功能,fix:修复 bug,refactor:代码变更- 从
main创建 feature 分支,必须提交 PR - CI:ruff(lint + 格式化)、mypy(类型检查)、pytest(测试)、safety(依赖检查)
- 部署:通过 Docker 镜像、Kubernetes 或 Railway 管理
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 ev/ECC