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 %}
閱讀更多 漫IT 的文章