Model管理
模型是一个项目的基础,在最开始构建模型的时候,应该对项目有清晰的理解,尽量考虑周全以减少后期模型变动。
模型方法管理
所有与model相关的基础操作都放在model内。可以将通用的基础业务逻辑放在model方法中。这样在写复杂的业务逻辑时,流程可以很清晰,步骤拆分成函数,一个函数负责一个功能,很方便debug。
文件划分
+ ec
+ order.py #model
+ def 业务基础操作
+ ec_manager
+ order_api.py #业务部分
+ def 复杂业务流程
状态管理
-
当一个模型有多个状态的时候,用统一的state字段来管理这些状态
#bad class Order: is_new = BoolField() is_confirmed = BoolField() is_done = BoolField() ... # good clss Order: state = CharField(choices=["new","confirmed","done"])
-
state还是status,团队统一,选一个就好,不要混用
-
choices怎么写?
- choices分为存储字段和显示字段
- 如果是前后端分离的项目,或者需要提供API,state的存储用语义化的字符串,不要用数字。
- choices的定义放在model内,不要放在其它文件内。例:
from model_utils import Choices class Order(models.Model): STATE = Choices( ('new', '新订单', _('new')), ('confirmed', '已接单', _('confirmed')) ) state = models.CharField(choices=STATUSES, default=STATUSES.draft) ## 业务中使用状态时,避免拼写错误 this_order.state == Order.STATE.draft
-
开发之前一定要设计好目录结构,代码组织规范。以免后期因为改动代码结构引入不必要的bug。
RPC同步数据时的注意事项
A系统与B系统同步数据,每次A系统的指定记录有变更时,向B系统推送此变更信息。
# bad
# 数据保存成功,rpc调用失败
with transaction.atomic():
record.name = 'xxx'
record.save()
do_rpc(record)
# bad
# rpc调用成功,后续数据保存失败,导致事务回滚。
with transaction.atomic():
do_rpc(record)
record.name = 'xxx'
record.save()
# good
with transaction.atomic():
record.name = 'xxx'
record.save()
# rpc 操作放在最后面
do_rpc(record)
# good -
# rpc调用时间较长,导致事务长时间挂起,退而求其次可以采用如下方法
record.name = 'xxx'
is_rpc_ok = do_rpc(record)
if is_rpc_ok:
# 在此之前做好充分的数据校验,保证save “一定成功”
record.save()
# good +
# 更好的做法是将时间较长的rpc调用转换成异步任务
with transaction.atomic():
record.name = 'xxx'
record.save()
# rpc 操作放在最后面
do_async_rpc(record)
如果是同步推送数据,RPC操作应该放在事务内,并且放在最后面。
如果RPC调用时间较长,将RPC转为异步任务。
时区处理
Django作为只前后端分离项目中的后端。为保证API中时间的统一性,将Django的时区设置为UTC标准时区,即0时区。
#settings.py
USE_TZ = True
TIME_ZONE = 'UTC'
Django admin 前端采用的还是模板机制,作为运维后台,如果希望后台的时间显示正常(根据实际时区变化),可以加入中间件处理时区信息。
# coding=utf-8
import pytz
from django.utils import timezone
from django.utils.deprecation import MiddlewareMixin
class TimezoneMiddleware(MiddlewareMixin):
def process_request(self, request):
tzname = request.session.get('django_timezone')
if request.path.startswith('/{}'.format('admin')):
# 访问后台,切换成中国时区
# 通过的做法是根据请求头,获取用户时区信息,做动态时区切换。这里偷懒了。
tzname = 'Asia/Shanghai'
if tzname:
timezone.activate(pytz.timezone(tzname))
else:
timezone.deactivate()