社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Django

Django后台代码质量改善建议集锦(一)

Python程序员 • 7 年前 • 763 次点击  

“写代码”技术的提高有两个阶段:第一个阶段是让代码能实现功能、并且少出错;第二个阶段是让代码更易于理解、潜在问题更少。


第一个阶段要怎么做是非常清晰的,功能能实现,调用接口有合适的数据返回,就算完成了任务。到了第二阶段,“应该怎么做”这个问题就变得隐晦了,变得更加没有标准答案了,因为每个人对“易于理解”这个要求的理解是不同的。虽然没有标准答案,但是答案大纲还是有的,有一些规律或要求是被大多数人认可的。


本篇,我们就试着梳理一些“隐晦”的共识,让在第二阶段的读者多一盏路灯。


1、定义Model类时,以一行数据的含义命名


举个例子来说明这个问题:假设我们需要定义一张数据表,这张数据表中的每一行是A校期末考试中一名学生的成绩,那么这张数据表本身就是A校期末考试的总成绩单。这时我们需要为这个数据表写一个Model类,那么这个Model类的名字应该是StudentScore(一个学生的成绩),还是ScoreReport(学校成绩单)呢?


答案应该是StudentScore。为什么用StudentScore更合理呢?让我们看一下我们通常是如何使用这个类的。


比如当我们想新建一条数据的时候,我们会这样写:

这段代码,即便是不懂Python的人来看,也可以大致猜出,这是在为张良创建一条学生成绩记录,并保存到数据库。如果我们当初将Model命名为ScoreReport,代码就变成了这样:

这段代码,看的人会有这样的困惑:创建了一个成绩单,里面写入了一个学生的成绩,那这个成绩单是只有一个学生的成绩吗?如果有第二个学生,需要再创建一个成绩单吗?还是report这个对象有一些方法,可以再添加第二个学生?


可以看出,命名为ScoreReport会给代码阅读者带来很多困惑,不如命名为StudentScore表意清晰。在调用delete方法时这个表意的差别更加明显,score.delete()很清楚地表达了删除的是一个学生的成绩,如果用report.delete(),读者很容易误解为把整个成绩单都删除了。


所以,给Model类取名字的时候要依据一行数据的含义命名,而不是依据整张表的含义命名。


用相似的思维方法,我们还可以得出一个结论,就是“给Model类取名字的时候要用单数,而不是复数。”为什么这样?大家可以自己思考一下。

2、变量命名单复数一定要分清


这个其实是PEP8标准中要求的一部分,这里单独拿出来说,是因为很多同学主要靠工具来完成PEP8的格式化,而工具是不会为你纠正这个命名问题的。


Python由于其动态性,编辑器往往没法告诉你某个变量是什么类型,这实际上增加了代码阅读者理解代码数据结构的难度,也对Python变量的命名提出了更为苛刻的要求。


举个例子,我们在Django中经常需要查询某表的很多行,然后遍历这些行,对它们依次进行一些操作,一个比较典型的代码是这样的:

这里有几个要点:

  1. 通过Model.objects.filter(...)这个句式得到的是多行的一种集合。所以这个句式的结果变量应该用复数。

  2. 因为集合变量名用了复数,在for循环中就不必动脑筋再想一个临时变量了,直接将复数变单数作为临时变量名。

  3. 这样的命名方式也很容易可以看出score和scores之间是什么样的关系。


但是有些同学不注意变量的单复数,经常会写出这样的代码:

这样的代码,让阅读者搞不清楚score到底是一个成绩还是多个成绩?obj和score是什么关系?如果相同函数中还有另外一个查询,要如何命名?叫obj2?总体来说,歧义太多,让人困惑。


即便是前面一种比较好的命名方式,小编也认为仍有改进的空间,比如可以把scores改叫score_queryset。这样改更加突出了这个变量的类型,是一个QuerySet,这样也可以提醒编码者,这个变量是不能JSON化的,是不能直接通过HttpResponse返回的。


3、尽量少用null=True


很多新手分不清blank=True和null=True有什么区别,导致胡乱使用,这里我们解释一下:

  1. blank=True表示该字段在代码层面可不可以不传值,如果blank=False(默认情况),但是通过Model创建对象时却没有给该字段赋值,Django会报错。

  2. 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的场景还是不太多、比较少)



今天先总结到这里,未完待续~~ 如果你也有什么想说的,欢迎在下方留言哦。


今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/ZEHPnbUsbQ
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/26660