“写代码”技术的提高有两个阶段:第一个阶段是让代码能实现功能、并且少出错;第二个阶段是让代码更易于理解、潜在问题更少。
第一个阶段要怎么做是非常清晰的,功能能实现,调用接口有合适的数据返回,就算完成了任务。到了第二阶段,“应该怎么做”这个问题就变得隐晦了,变得更加没有标准答案了,因为每个人对“易于理解”这个要求的理解是不同的。虽然没有标准答案,但是答案大纲还是有的,有一些规律或要求是被大多数人认可的。
本篇,我们就试着梳理一些“隐晦”的共识,让在第二阶段的读者多一盏路灯。
举个例子来说明这个问题:假设我们需要定义一张数据表,这张数据表中的每一行是A校期末考试中一名学生的成绩,那么这张数据表本身就是A校期末考试的总成绩单。这时我们需要为这个数据表写一个Model类,那么这个Model类的名字应该是StudentScore(一个学生的成绩),还是ScoreReport(学校成绩单)呢?
答案应该是StudentScore。为什么用StudentScore更合理呢?让我们看一下我们通常是如何使用这个类的。
比如当我们想新建一条数据的时候,我们会这样写:
这段代码,即便是不懂Python的人来看,也可以大致猜出,这是在为张良创建一条学生成绩记录,并保存到数据库。如果我们当初将Model命名为ScoreReport,代码就变成了这样:
这段代码,看的人会有这样的困惑:创建了一个成绩单,里面写入了一个学生的成绩,那这个成绩单是只有一个学生的成绩吗?如果有第二个学生,需要再创建一个成绩单吗?还是report这个对象有一些方法,可以再添加第二个学生?
可以看出,命名为ScoreReport会给代码阅读者带来很多困惑,不如命名为StudentScore表意清晰。在调用delete方法时这个表意的差别更加明显,score.delete()很清楚地表达了删除的是一个学生的成绩,如果用report.delete(),读者很容易误解为把整个成绩单都删除了。
所以,给Model类取名字的时候要依据一行数据的含义命名,而不是依据整张表的含义命名。
用相似的思维方法,我们还可以得出一个结论,就是“给Model类取名字的时候要用单数,而不是复数。”为什么这样?大家可以自己思考一下。
这个其实是PEP8标准中要求的一部分,这里单独拿出来说,是因为很多同学主要靠工具来完成PEP8的格式化,而工具是不会为你纠正这个命名问题的。
Python由于其动态性,编辑器往往没法告诉你某个变量是什么类型,这实际上增加了代码阅读者理解代码数据结构的难度,也对Python变量的命名提出了更为苛刻的要求。
举个例子,我们在Django中经常需要查询某表的很多行,然后遍历这些行,对它们依次进行一些操作,一个比较典型的代码是这样的:
这里有几个要点:
通过Model.objects.filter(...)这个句式得到的是多行的一种集合。所以这个句式的结果变量应该用复数。
因为集合变量名用了复数,在for循环中就不必动脑筋再想一个临时变量了,直接将复数变单数作为临时变量名。
这样的命名方式也很容易可以看出score和scores之间是什么样的关系。
但是有些同学不注意变量的单复数,经常会写出这样的代码:
这样的代码,让阅读者搞不清楚score到底是一个成绩还是多个成绩?obj和score是什么关系?如果相同函数中还有另外一个查询,要如何命名?叫obj2?总体来说,歧义太多,让人困惑。
即便是前面一种比较好的命名方式,小编也认为仍有改进的空间,比如可以把scores改叫score_queryset。这样改更加突出了这个变量的类型,是一个QuerySet,这样也可以提醒编码者,这个变量是不能JSON化的,是不能直接通过HttpResponse返回的。
很多新手分不清blank=True和null=True有什么区别,导致胡乱使用,这里我们解释一下:
blank=True表示该字段在代码层面可不可以不传值,如果blank=False(默认情况),但是通过Model创建对象时却没有给该字段赋值,Django会报错。
null=True表示该字段在数据库层面可不可以不传值,如果null=False(默认情况),写入数据库时该字段没有赋值,或者赋值为None,则数据库会报错。
那为什么说要尽量少用null=True呢?是因为数据库表的一列中只要有一个null值,那么这一列的索引都会失效。null会导致数据库查询性能的大幅下降。
我们再反过来想,当你用了blank=True或者null=True的时候,你实际上想表达什么意思?你想的是这个字段可以不传值。而我们知道数据库中的一列无论你传不传值,本质上都是有值的,只不过这个值可能是null。而由于null会导致索引失效,所以我们希望在你不传值的时候,数据库中写入的值最好不要是null。那么想到这里,你就会意识到,你应该自己设置一个默认值。所以当你想写blank=True,null=True的时候,其实你更应该写blank=True, default=XXX。这样,当你不传值的时候,数据库中存储的是一个你意料中特殊值。如果当前定义的这个字段是整数,那么这个特殊值经常是0;如果当前定义的这个字段是字符串,那么这个特殊值经常是“”(空字符串)。无论如何,不是null就好。
当然,也不是没有例外。ForeignKey字段如果允许不填值,blank=True并且null=True是可以接受的。因为虽然ForeignKey底层本质上是一个整数,但是这个整数如果你填写非正整数,Django在取值的时候会报错。所以不得已地,这里可以用下null=True。(自我安慰一下,ForeignKey里需要null=True的场景还是不太多、比较少)
今天先总结到这里,未完待续~~ 如果你也有什么想说的,欢迎在下方留言哦。