Django REST Framework バックエンド開発規約
トリガー条件
- Django プロジェクトを開発する
- REST API を実装する
- DRF ViewSet と Serializer を使用する
- JWT 認証を実装する
- 権限管理を実装する
Part 1: 技術スタック
| 層 | 選定 | 説明 |
|---|
| 実行環境 | Python 3.11+ | 型注釈を強化 |
| Web フレームワーク | Django 4.2+ | フルスタックフレームワーク、ORM 内蔵 |
| REST API | Django REST Framework (DRF) | ViewSet、Serializer、認証 |
| データベース | MySQL 8.x (mysqlclient) | InnoDB エンジン |
| キャッシュ | Redis 7.x (django-redis) | 分散キャッシュ |
| 認証 | PyJWT + DRF Authentication | トークンの発行/検証 |
| API ドキュメント | drf-spectacular | OpenAPI 3.0 を自動生成 |
Part 2: ディレクトリ構成
project/
├── apps/ # 应用模块
│ ├── system/ # 系统管理(Django App)
│ │ ├── users/ # 用户管理(models, serializers, views, urls, filters)
│ │ ├── roles/ # 角色管理
│ │ ├── menus/ # 菜单管理
│ │ ├── dept/ # 部门管理
│ │ ├── dicts/ # 字典管理
│ │ ├── configs/ # 系统配置
│ │ ├── notices/ # 通知公告
│ │ ├── logs/ # 日志管理
│ │ └── utils/ # decorators, exception_handler, rate_limit
│ ├── auth/ # 认证模块
│ │ ├── views.py, serializers.py, urls.py
│ │ ├── utils/ # jwt_authentication, redis_token_authentication
│ │ └── models/ # user_session, user_social
│ ├── codegen/ # 代码生成器(models, views, urls, templates/)
│ ├── file/ # 文件管理
│ └── message/ # 消息模块(SSE)
├── core/ # 公共模块
│ ├── response.py # 统一响应 + 分页
│ ├── viewsets.py # ViewSet 基类
│ ├── serializers.py # Serializer 基类
│ ├── exceptions/ # BusinessException + 全局处理器
│ ├── permissions/ # 接口权限 + 数据权限
│ └── middleware/ # rate_limit, request_context
├── config/ # 项目配置
│ ├── settings/{base,dev,prod}.py
│ ├── urls.py # 根路由
│ └── env.py
├── sql/mysql/youlai_admin_django.sql
├── manage.py
└── requirements.txt
設計原則:
core/ はすべての App で共有される共通インフラストラクチャであり、業務 App に依存しません
apps/ 内の各 App はエンティティごとにサブモジュール(user/role/menu)へ分割し、各サブモジュールに models/serializers/views/urls/filters を含めます
- ViewSet + Serializer が標準パターンです。複雑な業務ロジックは
services.py に切り出します
Part 3: 命名規約
3.1 ファイル命名
| 種類 | 規約 | 例 |
|---|
| モジュールディレクトリ | 小文字の複数形 | users/, roles/, menus/ |
| モデル | models.py | apps/system/users/models.py |
| Serializer | serializers.py | 同上 |
| ビュー | views.py | 同上 |
| ルーティング | urls.py | 同上 |
| フィルター | filters.py | 同上 |
3.2 クラス命名
| 種類 | 規約 | 例 |
|---|
| モデル | PascalCase、Sys プレフィックス | SysUser, SysRole, SysMenu |
| Serializer | 機能 + Serializer | UserSerializer, UserFormSerializer |
| ViewSet | 機能 + ViewSet | UserViewSet |
| Filter | 機能 + Filter | UserFilter |
3.3 メソッド命名
| アクション | DRF 組み込みメソッド | カスタム action |
|---|
| 一覧取得 | list() | page() |
| 詳細取得 | retrieve() | — |
| 新規作成 | create() | — |
| 更新 | update() | — |
| 削除 | destroy() | — |
| 一括削除 | — | batch() |
| ドロップダウン選択肢 | — | options() |
3.4 変数命名
| 種類 | 規約 | 例 |
|---|
| 変数 | snake_case | user_list |
| 定数 | UPPER_SNAKE_CASE | MAX_PAGE_SIZE |
| プライベートメソッド | _ プレフィックス | _parse_date() |
| ブール値 | is_/has_ プレフィックス | is_deleted, has_permission |
Part 4: RESTful API 規約
4.1 標準 CRUD パス
| 操作 | メソッド | パス |
|---|
| ページ分割リスト | GET | /api/v1/users/page/ |
| 詳細 | GET | /api/v1/users/{id}/ |
| 新規作成 | POST | /api/v1/users/ |
| 更新 | PUT | /api/v1/users/{id}/ |
| 削除 | DELETE | /api/v1/users/{id}/ |
| 一括削除 | DELETE | /api/v1/users/batch/ |
| ドロップダウン選択肢 | GET | /api/v1/users/options/ |
4.2 ViewSet テンプレート
@extend_schema(tags=["用户管理"])
class UserViewSet(ModelViewSet):
queryset = SysUser.objects.all()
serializer_class = UserSerializer
filterset_class = UserFilter
permission_classes = [HasPermission]
@extend_schema(summary="用户分页列表")
@action(detail=False, methods=["get"])
def page(self, request):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
return page_result(self.get_serializer(page, many=True).data, self.paginator.count)
@extend_schema(summary="新增用户")
def create(self, request, *args, **kwargs):
serializer = UserFormSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
return success_result({"id": user.id})
@extend_schema(summary="更新用户")
def update(self, request, *args, **kwargs):
instance = self.get_object()
serializer = UserFormSerializer(instance, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return success_result()
@extend_schema(summary="删除用户")
def destroy(self, request, *args, **kwargs):
self.get_object().delete()
return success_result()
Part 5: レスポンス形式と例外処理
5.1 統一レスポンス
def success_result(data=None, msg="操作成功"):
return Response({"code": 200, "msg": msg, "data": data})
def page_result(list_data, total, msg="操作成功"):
return Response({"code": 200, "msg": msg, "data": {"list": list_data, "total": total}})
def error_result(code=400, msg="操作失败", data=None):
return Response({"code": code, "msg": msg, "data": data})
5.2 業務例外
from rest_framework.exceptions import APIException
class BusinessException(APIException):
status_code = 200 # 统一 200,业务码在 body 中
default_detail = "业务异常"
default_code = 400
def __init__(self, detail=None, code=400):
self.detail = detail or self.default_detail
self.code = code
5.3 グローバル例外処理
class GlobalExceptionHandler:
def __call__(self, exc, context):
if isinstance(exc, BusinessException):
return error_result(code=exc.code, msg=str(exc.detail))
if isinstance(exc, APIException):
return error_result(code=exc.status_code, msg=str(exc.detail))
return error_result(code=500, msg="系统异常")
Part 6: モデル規約
class BaseModel(models.Model):
id = models.BigAutoField(primary_key=True)
create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
is_deleted = models.BooleanField(default=False, verbose_name="是否删除")
class Meta:
abstract = True
def delete(self, *args, **kwargs):
"""逻辑删除。"""
self.is_deleted = True
self.save()
class SysUser(BaseModel):
username = models.CharField(max_length=50, unique=True, verbose_name="用户名")
password = models.CharField(max_length=100, verbose_name="密码")
nickname = models.CharField(max_length=50, verbose_name="昵称")
mobile = models.CharField(max_length=20, blank=True, verbose_name="手机号")
status = models.SmallIntegerField(default=1, verbose_name="状态")
dept = models.ForeignKey("SysDept", on_delete=models.SET_NULL, null=True, verbose_name="部门")
class Meta:
db_table = "sys_user"
Part 7: Serializer 規約
class UserSerializer(serializers.ModelSerializer):
"""用户 VO 序列化器。"""
class Meta:
model = SysUser
fields = ["id", "username", "nickname", "mobile", "email", "status", "create_time"]
class UserFormSerializer(serializers.ModelSerializer):
"""用户表单序列化器。"""
class Meta:
model = SysUser
fields = ["id", "username", "password", "nickname", "mobile", "email", "status", "dept"]
def validate_username(self, value):
if SysUser.objects.filter(username=value, is_deleted=False).exclude(
id=self.instance.id if self.instance else None).exists():
raise serializers.ValidationError("用户名已存在")
return value
def create(self, validated_data):
validated_data["password"] = bcrypt.hashpw(validated_data["password"])
return super().create(validated_data)
Part 8: 認証規約
8.1 JWT 認証
class JWTAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.META.get("HTTP_AUTHORIZATION", "").replace("Bearer ", "")
if not token:
return None
try:
payload = jwt.decode(token, settings.JWT_SECRET, algorithms=["HS256"])
except jwt.ExpiredSignatureError:
raise AuthenticationFailed("Token 已过期")
except jwt.InvalidTokenError:
raise AuthenticationFailed("无效 Token")
user = SysUser.objects.get(id=payload["sub"], is_deleted=False)
return (user, token)
8.2 権限クラス
class HasPermission(BasePermission):
def has_permission(self, request, view):
required = getattr(view, "required_permissions", [])
if not required:
return True
return any(p in request.user.permissions for p in required)
Part 9: コメント規約
- docstring を優先:すべての公開クラスとメソッドに docstring を付ける必要があります
- 中国語を優先:専門用語は英語のままにし、説明には中国語を使用します
class UserViewSet(ModelViewSet):
"""用户管理 ViewSet。
提供用户分页查询、增删改查、下拉选项等接口。
所有接口(除 options 外)需认证。
"""
@action(detail=False, methods=["get"])
def page(self, request):
"""用户分页列表。
Args:
request: DRF Request 对象,Query 参数含 pageNum/pageSize/keywords/status。
Returns:
Response: {"code": 200, "data": {"list": [...], "total": int}}
"""
...
Part 10: コード品質チェックリスト
よくあるアンチパターン
| アンチパターン | 正しい方法 |
|---|
return Response(data) で直接返す | success_result(data) で統一的にラップする |
try/except Exception: pass | BusinessException を送出するか、ログを記録する |
User.objects.get(id=id) を例外処理なしで呼び出す | DoesNotExist を捕捉し、BusinessException を送出する |
| View に複雑な業務ロジックを書く | Model Manager または services.py に切り出す |
request.data["key"] を検証せずに使用する | Serializer の is_valid(raise_exception=True) を使用する |