Files
hertz_django/docs/开发规范.md
2025-11-17 16:39:30 +08:00

6.7 KiB
Raw Blame History

新功能开发规范

一、命名规范

  • APP 名称:hertz_studio_django_xxx,全小写,用下划线分隔
  • Python 包与模块:全小写,短名称,避免缩写不清晰
  • URL 命名空间:app_name = 'hertz_studio_django_xxx'
  • 数据库表名:Meta.db_table = 'hertz_xxx_model',避免与其他库冲突
  • 迁移文件命名:描述性动词+对象,如 0003_add_field_to_model

二、项目结构与约定

  • 标准结构:apps.pymodels.pyserializers.pyviews.pyurls.pyadmin.py
  • 静态资源:media/<app_name>/... 放置同路径资源以覆盖库静态文件
  • 配置集中:在 settings.py 维护,使用前缀化大写变量(如 YOLO_MODEL

三、接口返回规范

  • 统一使用 HertzResponse(路径:hertz_studio_django_utils/responses/HertzResponse.py
  • 成功:
    from hertz_studio_django_utils.responses.HertzResponse import HertzResponse
    return HertzResponse.success(data={'id': 1}, message='操作成功')
    
  • 失败:
    return HertzResponse.fail(message='业务失败')
    
  • 错误:
    return HertzResponse.error(message='系统错误', error=str(e))
    
  • 验证失败:
    return HertzResponse.validation_error(message='参数验证失败', errors=serializer.errors)
    
  • 统一键:success | code | message | data,禁止返回非标准顶层结构

四、API 设计规范

  • 路径语义化:/models//detections//alerts/
  • 方法约定:GET 查询、POST 创建/动作、PUT/PATCH 更新、DELETE 删除
  • 分页:请求参数 page, page_size;响应 total, items
  • 过滤与排序:查询参数 q, order_by, filters;谨慎开放可排序字段

五、认证与授权

  • 强制认证:业务敏感接口使用登录态(装饰器或 DRF 权限类)
  • 权限控制:按用户/组/角色配置;避免在视图中硬编码权限
  • 速率限制:对登录、验证码、检测等接口进行限流

六、日志与审计

  • 请求审计:记录请求方法、路径、用户、响应码、耗时
  • 业务事件:模型启用/删除、检测结果、告警变更记录
  • 脱敏:对密码、令牌、隐私字段进行统一脱敏

七、配置约定

  • 所有库配置集中在 settings.py,使用前缀化变量:
    • AIAI_MODEL_PROVIDERAI_DEFAULT_MODELAI_TIMEOUT
    • AuthAUTH_LOGIN_REDIRECT_URLAUTH_ENABLE_OAUTH
    • CaptchaCAPTCHA_TYPECAPTCHA_EXPIRE_SECONDS
    • LogLOG_LEVELLOG_SINKSLOG_REDACT_FIELDS
    • NoticeNOTICE_CHANNELSNOTICE_RETRY
    • MonitorMONITOR_PROBESMONITOR_ALERTS
    • WikiWIKI_MARKDOWNWIKI_SEARCH_BACKEND
    • YOLOYOLO_MODELYOLO_DEVICEYOLO_CONF_THRESHOLD
    • CodegenCODEGEN_TEMPLATES_DIRCODEGEN_OUTPUT_DIR

八、可扩展性(不改库源码)

  • 视图子类化 + 路由覆盖:在项目中继承库视图并替换路由匹配
    # urls.py
    from django.urls import path, include
    from your_app.views import MyDetectView
    urlpatterns = [
        path('yolo/detect/', MyDetectView.as_view(), name='detect'),
        path('yolo/', include('hertz_studio_django_yolo.urls', namespace='hertz_studio_django_yolo')),
    ]
    
  • 猴子补丁:在 AppConfig.ready() 将库函数替换为自定义函数
    # apps.py
    from django.apps import AppConfig
    class YourAppConfig(AppConfig):
        name = 'your_app'
        def ready(self):
            from hertz_studio_django_yolo import views as yviews
            from your_app.views import my_yolo_detection
            yviews.yolo_detection = my_yolo_detection
    
  • Admin 重注册:unregisterregister 自定义 ModelAdmin
  • 信号连接:在 ready() 中连接库暴露的信号以扩展行为

九、示例:覆写 YOLO 检测返回值

  • 目标位置:hertz_studio_django_yolo/views.py:586-603
  • 最小替换示例(路由覆盖):
    from rest_framework.decorators import api_view, parser_classes
    from rest_framework.parsers import MultiPartParser, FormParser
    from django.contrib.auth.decorators import login_required
    from hertz_studio_django_utils.responses.HertzResponse import HertzResponse
    from hertz_studio_django_yolo.views import _perform_detection
    from hertz_studio_django_yolo.models import YoloModel, DetectionRecord
    import uuid, os, time, shutil
    
    @api_view(['POST'])
    @parser_classes([MultiPartParser, FormParser])
    @login_required
    def my_yolo_detection(request):
        # 复用库的流程,省略若干步骤,仅演示返回体差异化
        serializer = hertz_studio_django_yolo.serializers.DetectionRequestSerializer(data=request.data)
        if not serializer.is_valid():
            return HertzResponse.validation_error(message='参数验证失败', errors=serializer.errors)
        uploaded_file = serializer.validated_data['file']
        yolo_model = YoloModel.get_enabled_model()
        original_path = '...'  # 省略:存储原始文件
        result_path, object_count, detected_categories, confidence_scores, avg_confidence = _perform_detection('...', yolo_model.model_path, 0.5, 'image', yolo_model)
        processing_time = time.time() - time.time()
        detection_record = DetectionRecord.objects.create(
            original_file=original_path,
            result_file='...',
            detection_type='image',
            model_name=f"{yolo_model.name} {yolo_model.version}",
            model=yolo_model,
            user=request.user,
            user_name=request.user.username,
            object_count=object_count,
            detected_categories=detected_categories,
            confidence_threshold=0.5,
            confidence_scores=confidence_scores,
            avg_confidence=avg_confidence,
            processing_time=processing_time
        )
        return HertzResponse.success(
            data={
                'id': detection_record.id,
                'file': {
                    'result_url': detection_record.result_file.url,
                    'original_url': detection_record.original_file.url
                },
                'stats': {
                    'count': object_count,
                    'categories': detected_categories,
                    'scores': confidence_scores,
                    'avg_score': round(avg_confidence, 4) if avg_confidence is not None else None,
                    'time': round(processing_time, 2)
                },
                'model': {
                    'name': yolo_model.name,
                    'version': yolo_model.version,
                    'threshold': 0.5
                },
                'user': {
                    'id': getattr(request.user, 'user_id', None),
                    'name': request.user.username
                }
            },
            message='检测完成'
        )