作者: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还是可用的工具。我写这篇文章的目的,是提醒大家不要误把糟糕的设计当做精巧的语法。

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

  1. django ORM模型表的一对多、多对多关系、万能双下划线查询

    一.外键使用 在 MySQL 中,如果使用InnoDB引擎,则支持外键约束.(另一种常用的MyIsam引擎不支持外键) 定义外键的语法为fieldname=models.ForeignKey(to_c ...

  2. Django ORM模型

    Object Relational Mapping(ORM) 一,ORM介绍 1, ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象 ...

  3. Django ORM模型的一点体会

    作者:Vamei 出处:http://www.cnblogs.com/vamei 严禁转载. 使用Python的Django模型的话,一般都会用它自带的ORM(Object-relational ma ...

  4. 4.Django|ORM模型层

    ORM简介 MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的 ...

  5. Django框架 (七) Django ORM模型

    ORM简介 查询数据层次图解:如果操作mysql,ORM是在pymysq之上又进行了一层封装

  6. Python之路【第二十九篇】:django ORM模型层

    ORM简介 MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的 ...

  7. 通过ORM模型看python对象创建过程

    简易django ORM模型如下所示: #!/usr/bin/env python # encoding: utf-8 """ @version: 1.0 @author ...

  8. python 全栈开发,Day70(模板自定义标签和过滤器,模板继承 (extend),Django的模型层-ORM简介)

    昨日内容回顾 视图函数: request对象 request.path 请求路径 request.GET GET请求数据 QueryDict {} request.POST POST请求数据 Quer ...

  9. django创建ORM模型、通过ORM模型操作单个表、ORM模型常用字段

    一.ORM简介 ORM ,全称Object Relational Mapping,中文叫做对象关系映射,通过ORM我们可以通过类的方式去操作数据库,而不用再写原生的SQL语句.通过把表映射成类,把行作 ...

随机推荐

  1. Android 如何解决dialog弹出时无法捕捉Activity的back事件

    Android 如何解决dialog弹出时无法捕捉Activity的back事件 在一些情况下,我们需要捕捉back键事件,然后在捕捉到的事件里写入我们需要进行的处理,通常可以采用下面三种办法捕捉到b ...

  2. JavaWeb:servlet实现下载与上传功能

    本文内容: servlet实现下载功能 servlet实现上传功能 首发日期:2018-07-21 servlet实现下载功能 实现流程 1.首先制作一个jsp页面,主要是用来触发下载的.这里可以根据 ...

  3. python leetcode 字符串相乘

    给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式. 示例 1: 输入: num1 = "2", num ...

  4. C#多线程图片爬虫

    写了个简单的多线程图片爬虫,整理一下.数据已经爬下来了,图片URL需要自行拼接,首先从Lawyers表中取的RawData字段,RawData中有一个list字段是json格式的数据,需要的只是lis ...

  5. ORACLE归档日志比联机重做日志小很多的情况总结

    ORACLE归档日志比联机重做日志小很多的情况   前几天一网友在群里反馈他遇到归档日志比联机重做日志(redo log)小很多的情况,个人第一次遇到这种情况,非常感兴趣,于是在一番交流沟通后,终于弄 ...

  6. spring4笔记----使用装配注入合作者Bean的三种方式

    no :不自动装配 byName :id(name)与setter方法去set前缀,并小写首字母后同名的Bean完成注入,如有多个匹配则抛异常 byType :spring容器找全部bean,如果找到 ...

  7. 如何下载 Google Play 应用的apk

    Google Play 不能直接下载apk安装包,解决办法,安装插件下载 第一步 FQ就不说了 第二步 安装google浏览器 APK Downloader插件 第三步 打开Google play网站 ...

  8. 使用Java+MySQL+Apache开发后台项目(一)

    做前端开发的人越来越多,后端维护的人才越来越稀缺,这种趋势正在慢慢扩展.像我这种人总喜欢反其道而行之,做后端开发的人虽然减少了,但是工作量和工作资质都要求的更高了,随着人工智能的发展,需要后台处理的数 ...

  9. netsh winsock reset命令,作用是重置 Winsock 目录

    Win xp重置 编辑 要为 Windows XP 重置 Winsock,请按照下列步骤操作: 1.单击“开始”,运行中输入cmd. 2.然后输入命令 netsh winsock reset. 3.重 ...

  10. linux中find命令高级用法

    前言 在<Linux中的文件查找技巧>一文中,我们已经知道了文件查找的基本方法,今天我们介绍find命令的一些高级使用技巧.它能满足我们一些更加复杂的需求. 查找空文件或空目录 有时候需要 ...