Django ORM模型:想说爱你不容易

作者:Vamei 出处:http://www.cnblogs.com/vamei 严禁转载。

 

使用Python的Django模型的话,一般都会用它自带的ORM(Object-relational mapping)模型。这个ORM模型的设计比较简单,学起来不会特别花时间。不过,Django的ORM模型有自己的一套语法,有时候会觉得别扭。这里聊一下我自己的体会。

 

模型设计

这一部分算处理得比较好的部分。Django的数据模型的建立过程很简单,就是继承django.db.models中的Model类,然后给它增加属性。每一个属性可以对应关系数据库中的一个字段。比如在一个叫myapp的Django App下,创建models.py文件:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=10)

通过manage.py的makemigrations和migrate命令,就可以执行数据库的迁移。上面的name属性,就对应了生成的myapp_person表中名为"name"的一列。这里的max_length=10对应了限制条件:

VARCHAR(10)

(在MySQL V4中,代表了10个字节;在MySQL V5中,代表了10个字符。)

 

除了上面的字符类型,其他常见的字段类型,在Django都有对应的*Field来表达,比如TextField、DateField、DateTimeField、IntegerField、DecimalField。此外,还有一些常见的限制条件,除了上面的max_length,还有default、unique、null、primary_key等等。数字类型的限制条件有max、min、max_digits、decimal_places。这些限制条件都通过参数的形式传给属性。有一些限制条件是Django提供的,并没有数据库层面的对应物,比如blank。

(当blank参数为真时,对应字段可以为留为空白。)

 

在基本的模型设计上,Django ORM没有留什么坑。

 

关系

Django中的一对一、多对一、多对多关系可以通过下面方式表达:

from django.db import models

class Company(models.Model):
    name = models.CharField(max_length=10)

class Group(models.Model):
    name = models.CharField(max_length=10)

class Person(models.Model):
    name = models.CharField(max_length=10)

class Customer(models.Model):    name    = models.CharField(max_length=10)
    person  = models.OneToOneField(Person)
    company = models.ForeignKey(Company, on_delete=models.CASCADE)
    groups  = models.ManyToManyField(Group)

Customer的定义中,用到一对一、多对一、多对多关系。它们分别通过OneToOneField、ForeignKey和ManyToManyField来实现。

 

需要注意的是,在Django ORM中,只能通过ForeignKey来定义多对一关系,不能显示地定义一对多关系。但你可以使用模型对象的*_set语法来反向调用多对一关系。比如说:

company.customer_set   #company是一个Company的实例

 

就可以根据一对多关系,调到该公司下的所有客户。此外,多对多关系也可以用类似的方式反向调用,比如:

group.customer_set

此外,你还可以在模型中加入related_name参数,从而在反省调用时,改用"*_set"之外的其他名称,比如:

class Customer(models.Model):
   person  = models.OneToOneField(Person)
   address = models.CharField(max_length=100)
   company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name="customers")

如果两个模型之间有多个关系时,related_name可以防止*_set重名。

 

总的来说,上面的解决方案可以实现功能,并不影响使用。但我总是觉得这个解决方案有些丑陋。由于不能显式地表达两个模型之间的关系,模型之间的关系看起来不够明了。特别是读代码时,第一个类定义完全没法提示一对多的关系。我必须要看到了第二个类定义,才能搞明白两个模型之间的关系。真希望有一种显式说明关系的办法,降低读代码时的认知负担。

 

查询

Django ORM可以通过一些方法来实现。其中的很多方法返回的是Django自定义的QuerySet类的迭代器。Python看到迭代器时会懒惰求值,所以这些方法返回时并不会真正进行数据库操作。这样,多个方法串联操作时,就避免了重复操作数据库。返回QuerySet的常见方法包括:

all()
filter()
exclude()
annotate()
order_by()
reverse()
distinct()
...

对于依赖具体数据的操作,QuerySet会求值。比如遍历QuerySet时,就会先执行数据库操作。用len()获得QuerySet长度时,也会造成QuerySet估值。此外QuerySet一些方法,比get()、count()、earlist()、exists()等,都会对QuerySet进行求值。因此,在写程序时,要注意QuerySet求值的时间点,避免重复的数据库操作。

 

SQL的WHERE条件可以通过参数的形式来传给方法。这些参数一般是"[字段]__[运算符]"的命名方式,比如:

Customer.objects.filter(name__contains="abc")

除了contains,还有in、gt、lt、startswith、date、range等等操作符,能实现的WHERE条件确实够全的了。

 

不过,这又是一个有点别扭的地方,即通过命名方式来控制查询行为。我看过有的ORM是用lambda的形式来表达WHERE条件,还有的会做一个类似于contains()的方法,都要比Django ORM的方式好看。如果是跨表查询,Django的方式就更丑了:

Customer.objects.filter(company__name__contains="xxx")

无限的双下划线啊……

 

聚合

Django实现聚合的方式简直是噩梦。貌似ORM对表达GROUP BY很无力,源代码里的注释就认输了:

聚合的aggregate()和annotate()方法可以实现基本的功能,但稍微复杂一点,代码就变得魔幻了:

看到一大串values()、annotate()变来变去,有没有觉得头晕?我觉得这种情况下,可以直接上原始的SQL查询语句了,没必要再自己折腾自己。

 

F表达式和Q表达式

F表达式指代了一列,对于update操作时引用列的值有用。Q表达式代表了WHERE的一个条件,可以用于多个WHERE条件的连接。这些都是Django ORM用来弥补缺陷的。就拿Q表达式来说。查询方法中跟多个参数的话,相当于多个WHERE条件。这些条件会默认为AND关系。为了表达OR和NOT关系,Django ORM就造了个Q表达式,比如:

filter(Q(name__contains="abc")|Q(name__startswith("xxx")))

为了弥补缺陷,Django ORM又增加了一种语法风格。于是,学习路上又多了一个坑……

 

总结

总的来说,Django ORM在实现基础的数据库操作方面没问题。但如果需要构建复杂的SQL语句,与其在Django ORM里绕来绕去,还不如直接用原始的SQL语句。这个是我最强烈的一个感受。当然,Django ORM还是可用的工具。我写这篇文章的目的,是提醒大家不要误把糟糕的设计当做精巧的语法。

时间: 2024-09-20 05:54:50

Django ORM模型:想说爱你不容易的相关文章

一位seoer的心声:百度想说爱你不容易

我是从08年开始自己学习建站,我想说:我是个菜鸟,但是菜鸟也有建站的权力.网站对于菜鸟来说是自己的奋斗方向以及对自己努力付出后的一种成就的肯定与展现. 从刚开始的学习html代码,在到后来的asp,我慢慢的开始深入了解了网站建设的魅力与前景.我清楚的记得我的第一个个人主页是在刚上大学那会做的,当时连续通宵了2个晚上做出了10多个页面的全静态网站.很有成就感!但那会根本没有什么SEO,百度都没这么有名,还没上市呢-- 和很多刚入门的站长一样,我以为有了网站就算是成功了.后来发现我的网站根本没什么人

百度文库外链 想说爱你不容易

对于国内做百度优化的SEOer来说都有一个不争的共识,百度总会更加的照顾自家的产品(国内的反垄断意识还不强).对于这个现象我们可以从很多方面看出来,当我们百度一个关键词的时候,经常性的出现在第一的往往是百度自己的产品,如百度知道.百度百科.百度贴吧.百度文库等等 .针对这个现象,很多SEOer都很喜欢通过借助百度产品的外链来提高权重及排名.笔者的站点是教育站点,在做百度文库上很有优势,但是在做了一阶段后,笔者心中的感受是:百度文科,想说爱你不容易. 对于百度文库外链的效果,业内人士一直存在着不同

原创,想说爱你不容易

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 我相信每个站长都希望能够原创,但是又有多少个站长能够原创呢?在这里我就告诉大家一个事实:不是你的能力不行,而是你的网站让你无法原创. 近日,一口气拜读了关于网站推广方面的许多文章,大家都不约而同地提到原创,诸如原创如何重要?怎么来原创?原创怎么起作用?等等.我想了想,原创好是好,但你网站内容能不能原创,就不好说了,这得看你网站是如何定位的.

OpenGL ES 灯光,想说爱你并不是很容易的事

OpenGL ES 灯光,想说爱你并不是很容易的事 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. 头疼!!!坚持把这个总结写完吧,要不,明天又得重新疏理一会儿思路才能开始工作......噢,忘

大冰箱,想说爱你不容易

[IT168 厂商动态]在不少人的眼里,大冰箱是冰箱里的"高富帅",与时尚的家居生活完美匹配.可是,实际情况却不尽然.据卡萨帝官方微博开展的一个网络调查显示,拥有大冰箱的用户却并没有享受到卓越的使用体验,相反却遭受着大冰箱带来的很多不便,不少人在面对华丽时尚的大冰箱时只能无奈地叹息:大冰箱,想说爱你不容易!痛点一:体积大 入户难想要买个冰箱竟然还要拆门?一名叫"会飞的鱼"网友在微博上吐槽,"好不容易买个大冰箱,搬到家门口却傻了眼,别说家门,连小区的电梯都进

想说爱你不容易 阿里云OS对比安卓无明显优势

一周前得了一部 阿里云手机,答应写篇评测.但是说实话,手机换了好几部,评测却真没有写过,只好挂着评测的名头来写写阿里云这个系统了.     在这里,我不想过多地去讨论云OS到底和安卓有 多少纠缠和渊源,今天只是想从一个用户最直观的角度,用一家之言,来为它把把脉吧.       1.简约不等于简单     阿里云系统给我的最大感觉就是对安卓做了 大量的减法,"去掉"很多安卓上的功能.     尽管这其中不乏人性化的设计,比如上下滑动关机和解锁.但是显然"简约的美丽"和

王强:谈太阳能网络媒体 想说爱你不容易

中介交易 SEO诊断 淘宝客 云主机 技术大厅 王强谈太阳能网络媒体,想说爱你不容易 当前国际原油价格持续飙升,国内天然气也出现短缺,能源问题已成为世界各国共同关注的重大战略问题.<可再生能源法>的颁布和实施,阐明了我国对节能减排.发展清洁.可再生能源战略的重视.作为可再生能源的主力军,太阳能这个永不衰竭的清洁能源,给能源紧缺问题带来了新的希望.目前太阳能行业正面临着前所未有的发展机遇,吸引了传统的家电巨头也纷纷涉足这个领域,开拓新的发展市场.行业的发展离不开政策.资讯.供求等信息的支持,此时

推拿O2O 想说爱你还不容易

<屌丝男士>第四季最后一集里,乔杉终于圆了"大保健"的梦想,可惜却临阵脱逃.终极原因就是怕有偷拍,名誉会一扫而空.这不禁让身为观众的我深思,如果是在家里享受上门服务的推拿和按摩,乔杉是不是就毫无顾忌了呢?事实上,上门推拿O2O近段时间正呈现火爆趋势,众多创业团队纷纷涌入其中. 与上门洗衣.上门美甲.上门洗车.上门烧菜等门类繁多,且早已形成规模的上门服务相比,上门推拿起步较晚.原因就在于上门推拿涉及到用户的健康和财产安全,再加上涉黄的可能性以及传统意识上的阻碍等,不确定因素太

想说爱你很容易 关键字如何排名百度第一页

如何有步骤提供百度的排名是所以站长在倾心追求的.很多站长都说想说爱你不容易,总在感叹我的网站怎么在百度上收索不到,关键字排名都在几十名之外.但是今天我要和这些站长们说.百度,想说爱你很容易,可以让你的站关键字排名百度第一页.通过以下几点,你也会做到. 1.需要选择合适的域名,最好包含所排名的关键字拼音,北京礼品网网站地址 www.lipin-bj.cn主要针对于礼品,所选择的域名就包括了礼品的拼音 2.合理的网站结构,合理的层次结构.网站采用非动态页面,尽量采用静态页面.或采用伪静态页面可以看看