-
Notifications
You must be signed in to change notification settings - Fork 66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
用户管理开发规范[初稿] #1001
Comments
其他: 使用新版 HTTP 协议实现 (标准 HTTP 状态码 + 统一的响应体) |
|
drf 继承关系图: 当前规范建议只继承图中右下角的类 |
urls.py中使用聚合的方式组织相同前缀的 path urlpatterns = [
path(
"<page_slug>-<page_id>/",
include(
[
path("history/", views.history),
path("edit/", views.edit),
path("discuss/", views.discuss),
path("permissions/", views.permissions),
]
),
),
] |
serializer命名风格 |
目前都是基于Python3.6开发,尽可能添加类型注解 |
1.1.1 里面有说明, 强制要求 typehint |
使用lint检查项目的分层依赖 |
引入Pydantic 2.x,作为数据类
|
view层调用biz层,参数和返回值不直接使用Dict,而是需要使用Pydantic定义的数据类,pydantic 实践参考如下:
|
正确地使用 201/204, 而不要所有的地方都用 200(前端需要同时支持判定 20x 为成功) |
|
允许使用Mixin,但必须保证职责单一,不能将多种职责复合在一个Mixin里 |
Input/Output SLZ 中,如果有不确定的 Dict 类型需求,更推荐使用 serializers.JSONField 讨论参考:#1207 (comment) |
相对引用与绝对引用
|
是否限制相对引用,可以只能引用 一层,比如 可以from .demo imprt a ,但不能 from .demo.a.b.c.d import e ? |
|
class XSLZ(serializers.Serializer):
class Meta:
ref_name = "the_import_path_of_this_serializer" |
NIT: serializer_class规则 (同DRF保持一致)
|
|
|
转到 内部规范, 进入讨论修订阶段 |
用户管理开发规范[初稿]
基于 Python 的
Django
+DRF
框架开发, 开发规范汇总[toc]
分层与单一职责
当前分层:
slz + viewset
, 处理参数校验和响应, 不允许放复杂业务逻辑其他:
apps
和apis
, 避免混用/引入不必要的复杂度apps
放供产品
使用的接口层代码apis
放开发 API
, 接入网关/ESB 供外部调用的接口代码1. Python 基础规范
请先阅读:
下面补充一些额外规范
1.1 基础规范
1.1.1 强制要求 typehint
1.1.2 优先使用
f-string
除了logger, 其他字符串拼装尽量使用
f-string
1.1.3 函数的存在多个
return
, 返回值必须保持一致return
返回 1 个参数, 另一个return
返回 2 个raise
要么都返回bool
, 不要混用1.1.4 单一职责, 函数不能有副作用
一个函数只做一件事情, 函数不能有副作用
1.2 日志与异常
1.2.4 logger打印要包含上下文, 有异常的地方要用
logger.exception
1.2.2 使用统一的
logging.getLogger
禁止通过其他方式获取
logger
1.2.3 禁止吞掉异常
1.2.4 禁止在代码中使用print打印日志
1.2.5 日志打印中禁止打印敏感信息, 例如密码/解密的明文等信息
2. view层
2.1 serializer
2.1.1 命名规则
驼峰命名, 以大写
SLZ
结尾(或者写全Serializer
, 但是必须所有地方保持一致, 不要混用), 区分入参及响应使用的SLZ
FooBarInputSLZ
FooBarOutputSLZ
其中
FooBar
是viewset
的前缀2.1.2 DRF的输出都必须使用Serializer, 禁止直接构造dict放入response
好处:
2.1.2 同一个模型的serializer公共字段尽量复用
2.1.3 尽量避免使用
serializers.ModelSerializer
简单模型/字段少/无特殊处理逻辑等场景, 可以使用, 例如示例中只包含
Meta.model/Meta.fields
, 不存在覆写一些父类方法改变/定制某些行为;否则, 尽量避免使用
如果使用, 使用
Meta.fields
声明白名单, 避免使用fields = "__all__"
; 防止未来增删敏感字段被意外暴露或被修改2.1.4 复杂的字段校验使用
validate_{field_name}()
; 复杂的整体校验使用validate()
2.1.5 字段处理/转换使用
SerializerMethodField
注意: 尽量避免在
SerializerMethodField
中做数据库查询/IO 等代价较高的操作, 可能带来性能问题; e.g. 在一个method中做了一次db查询, 如果翻页返回 100 个, 那么单次接口请求会有 100 个db查询(查询放大)2.1.6 使用
serializer_context
来避免查询放大全局查询一次, 而不是每个对象查询一次
2.1.7 除非你理解
to_representation
及to_internal_value
, 否则不要使用如果输入的表单和业务模型数据结构差异很大, 需要做全局转换, 可以使用
to_internal_value
统一处理; 但是需要注意validate
是否生效的问题;如果需要做较大的转换, 例如得到的模型数据相对返回的数据结构差异很大, 需要做大量的转换/映射/内嵌/判断等, 此时可以直接定义
to_representation(self, obj)
简化2.2 viewset
2.2.1 命名规则
注意, Api小写;
建议用
Api
也可以用
View
, 但是必须所有地方保持一致, 不能混用2.2.2 View细粒度拆分
切忌大而全, 这样会导致
serializer_class or get_serializer_class
/pagination_class
/filter_backends
/get_serializer_context
等等代码出现大量的判断, 导致逻辑揉在一起, 读一段代码不是线性的, 到了每个方法都有一些逻辑判断;2.2.3 只能继承于
generics.xxxAPIView
DRF继承关系图
单一
generics.CreateAPIView
generics.ListAPIView
generics.DestroyAPIView
generics.RetrieveAPIView
generics.UpdateAPIView
复合
generics.ListCreateAPIView
generics.RetrieveUpdateAPIView
generics.RetrieveDestroyAPIView
generics.RetrieveUpdateDestroyAPIView
原则:
单一
的父类, 有需要再继承复合
的父类2.2.4 禁止对 DRF Viewset/View 做封装增加中间层
某些项目基于某些原因增加一层中间层, 封装的同时加入了一批约定属性及约定方法, 来实现业务公共逻辑
复杂度极具上升, 且因为存在两层约定(DRF 一层, 封装类一层), 在加上继承子类里面的逻辑, 整体的调用链路/数据流将变得不可理解, 认知负担非常大, 容易出bug
2.2.5 尽量避免使用
viewsets.ModelViewSet
除非模型非常简单, 没有附加的一些处理逻辑, 并且未来也不会加入, 否则尽量避免使用
2.2.6 使用 DRF
约定
扩展实现公共逻辑根据需求, 合理定义相关的
组件
, 拼装组合出最终的结果permission_classes
权限控制pagination_class
翻页filter_backends
过滤这些扩展, 尽量做到
望名知意
, 细粒度可自由组合, 切忌实现大而全的扩展2.3 url
2.3.1 符合 Restful 风格
遵循 Restful 风格, 避免在路径中使用动词
create/serach
3. biz 层
4. model 层
4.1 model
4.1.1 命名规则
驼峰命名, 并且需要跟业务概念一致
4.1.2 只允许包含简单的方法和property
避免在model的方法做一些复杂操作
尽量避免在方法/property中做db查询; (不可预测的查询放大, 例如某个分页查询page_size = 100返回的slz中引用了property, 会带来100次查询)
4.1.3 避免在model方法中查询另一张表/几张表
不合理的依赖, 可能带来循环引用
4.2 manager
复杂的model查询/操作等, 写到
managers.py
中4.2.1 方法命名规则
4.2.2 尽量只包含对当前模型的操作
5 DRF
5.1 DRF 特性使用
5.1.1 使用
permission_classes
做权限控制但是注意, 一个类只做一件事情
The text was updated successfully, but these errors were encountered: