1.别人看不明白的代码要加注释
class Sample(TimedModel):
TODO = 'todo'
DOING = 'doing'
DONE = 'done'
上面的代码,别人看不懂状态的函数,于是加注释
class Sample(TimedModel):
TODO = 'todo' # origin state
DOING = 'doing' # this sample has been notified to superbox
DONE = 'done' # superbox uploaded related event generated from this sample
2.接收前端传过来的数据时,要判断哪些字段可以写入db中
class Meta:
model = RawEvent
read_only_fields = ('mark',)
fields = ('id', 'created_at', 'updated_at', 'uuid', 'camera', 'site', 'types', 'mediums') + read_only_fields
created_at字段不希望被前端设置,这样前端可能伪造创建时间
class Meta:
model = RawEvent
read_only_fields = ('mark', 'created_at', 'updated_at')
fields = ('id', 'uuid', 'camera', 'site', 'types', 'mediums') + read_only_fields
3.精简代码,规避多余的sql查询
chaxun
查询条件合并
qs = NotifyInfo.objects.filter(Q(camera=camera.pk) | Q(box=camera.box_id)).all()
for notify_obj in qs:
users.append(notify_obj.user)
if len(users) <= 0:
but,这两个查询其实不能合并哦,故意这样设计的,你发现原因了吗?
4.serializer不只是验证数据,还净化数据
serial = self.get_serializer(data=request.data)
if serial.is_valid():
EventService.r_create(request, serial, result)
uuid = request.data.get('uuid')
这时uuid不应该从request.data中拿,而应该从serializer.validated_data中拿
serial = self.get_serializer(data=request.data)
if serial.is_valid():
EventService.r_create(request, serial, result)
uuid = serial.validated_data['uuid']
5.race condition问题的规避
@detail_route(methods=('get', 'put'), permission_classes=(IsAuthenticated,))
def verified(self, request, *args, **kwargs):
...
raw_evt.mark = RawEvent.VERIFIED
raw_evt.user = request.user
raw_evt.save()
这是个响应http请求的函数,对raw_evt字段的更新希望是被原子执行的,要么加锁,要么用下面方法
@detail_route(methods=('get', 'put'), permission_classes=(IsAuthenticated,))
def verified(self, request, *args, **kwargs):
result = Result()
raw_evt = self.get_object()
if raw_evt.mark != RawEvent.UNKNOWN:
result.put_forbid_error(extra='already marked')
return result.get_response()
RawEvent.objects.filter(pk=raw_evt.pk, mark=RawEvent.UNKNOWN).update(mark=RawEvent.VERIFIED, user=request.user)
raw_evt.refresh_from_db()
...
6.接口最小暴露原则
from rest_framework.viewsets import ModelViewSet
class EventViewSet(ModelViewSet):
...
业务要求对Event只能创建,不允许更新,这时使用ModelViewSet范围就有点大了
from rest_framework import mixins
from rest_framework.viewsets import GenericViewSet
class EventViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
...
7.serializer.choiceField,就是省力用的
class SampleSerializer(serializers.Serializer):
mark = serializers.CharField(required=True)
def validate_mark(self, value):
if value not in ['misreport', 'verified']:
raise serializers.ValidationError('not a valid mark value')
return value
serializer.choiceField就是用来干这事儿的,让代码更简洁,降冗余
class SampleSerializer(serializers.Serializer):
mark = serializers.ChoiceField(choices=[('misreport', 'misreport'), ('verified', 'verified')], required=True)
8.去除冗余代码
sample = Sample.objects.filter(Q(pk=data['sample_id']) & ~Q(status=Sample.DONE)).first()
if not sample or not sample.camera_id or not sample.camera.box_id:
result.put_logic(Error.logic(cls.DM_SAMPLE_NOT_SUPPORTED, cls.EM_SAMPLE_NOT_SUPPORTED % dict(code=cls.DM_SAMPLE_NOT_SUPPORTED)))
result.send_channel(message)
return
now = Utils.utc_now()
if sample.user is not None and (now - sample.updated_at).seconds < 60:
result.put_logic(Error.logic(cls.DM_SAMPLE_NOT_SUPPORTED, cls.EM_SAMPLE_NOT_SUPPORTED % dict(code=cls.DM_SAMPLE_NOT_SUPPORTED)))
result.send_channel(message)
return
发现两个if中有两行相同的代码,一种做法是将if条件合并,我觉得更合理的方式是让两个if中返回不同的信息,毕竟是两种不同的异常。
9.返回客户端的string都要注意国际化
def validate_sample_id(self, value):
if not Sample.objects.filter(pk=value).exists():
raise serializers.ValidationError(f'sample_id:{value} not exist in db')
改成下面
def validate_sample_id(self, value):
if not Sample.objects.filter(pk=value).exists():
raise serializers.ValidationError(_("The specified sample %(sample_id)s doesn't exist.") % dict(sample_id=value))
return value
10.django template的国际化写法
xief
下面更好
{% load i18n %}{% autoescape off %} {% blocktrans %} Camera {{ rawevent.camera_id }} generated new event {% endblocktrans %}{% endautoescape %}