Django中ORM元类学习及查询API

1. Django ORM实现原理较为复杂,这里仅讨论元类在其中的部分作用。

  1. 首先要知道元类是什么

    python中一切皆对象,那么定义的类也是对象,而类是由什么创建的呢?

    自定义一个类,在使用type()函数,可以看见这个类是type类型的,所以自定义的这个类必然是type创建的。

    实际上:类是由元类创建的,当定义一个类时如果没有指定元类,就会一层层向上找,如果父类中也没有指定元类,再向上找模块,模块也没有,就会使用内置的type()来创建类。

    如果指定了元类,那么就会改变这一默认行为,使用指定的元类来创建类。

    所有自定义的元类都必须继承 type, 且需要重写 __ new __()方法,该方法用来创建实例

    它接受四个参数:需要实例化的类对象,类的名字,类继承的父类集合,类的字典属性

    可在该方法内控制类对象的生成过程。

  2. 为何要继承models.Model?

    • 自动添加的自增主键

      Model指定了元类为ModelBase(继承自type),所有自定义的Model都是由ModelBase创建出来的。ModelBase的__ new __()方法中引用了 _prepare()方法,该方法中判断了是否自定义了主键,如果没有则添加一个自增主键。

    • 自动添加的查询管理器

      Django会为每一个Model自动添加一个名称为objects的查询管理器,其实现也是在_prepare() 中。最终可知Model.objects并不是一个Manager对象,而是ManagerDescriptor实例,ManagerDescriptor是一个描述符,是对Manager访问的一种保护,主要实现对Model实例和抽象Model的拒绝访问。

      利用Manager(objects)就可以实现对Model的增删改查,有些方法还会返回QuerySet,这是怎么实现的呢?

      • Manager中并没有定义什么,而是继承了BaseManager.from_queryset(QuerySet),并利用_get_queryset_methods()将一些方法注入给Manager,例如:filter,get,create等。
      • _get_queryset_methods()中遍历QuerySet的方法定义,然后跳过BaseManager同名的方法,跳过queryset_only为True的方法,跳过以” _ ”开头的方法,将其他方法注入BaseManagerFromQuerySet中。

2. Model的查询API

  1. 创建Model实例

    • 调用Model的save()来创建,先创建一个实例,在保存实例。没有返回值。
    • 调用Manager的create()方法,返回Model实例。
  2. 返回单实例的查询方法

    • Manager的get()方法,可同时列出多个查询条件,会抛出两种异常DoesNotExist和MultipleObjectsReturned
      • Django支持在查询时使用pk来代替主键名称
    • Manager的get_or_create()方法,返回元组(object,created),查到了则返回,否则创建。
    • 类似的方法还有firstlast,分别返回queryset的第一项与最后一项
      • first()效果同使用索引[0],不同的时如果没有查到会报IndexError
      • QuerySet不支持负值索引
  3. 返回QuerySet的查询方法

    • Manager的all()方法,返回所有数据实例。

      • QuerySet是惰性求值,定义时并不会访问数据库,只有在迭代或者获取个数的时候才会到数据库进行查询
    • reverse()方法,获取全量数据,排列顺序与all相反

    • order_by()方法,可自定义怕徐规则,可指定多个排序字段

      • order_by()方法会清除之前的所有排序
      • 不传递参数,则没有任何排序规则,默认的也没有
      • 传递 代表随机排序,通常无意义且耗时
    • filter()方法,获取全量数据的一个子集,等同于使用where子句过滤不符合条件的记录

      • 不传递任何参数等同于all
      • 常用查询条件

      #####################

      contains(), startswith(), endswith()都有对应的忽略大小写的版本,即在方法名前面添加 字母 i 就可以了

    • exclude()方法,反向过滤,相等于在filter前面加了一个not

      • 实际上exclude和filter确实使用了同一个方法,即_filter_or_exclude(),只是用了一个布尔值标记自己的正向过滤还是反向过滤
    • 链式查询由于filter,exclude这样的方法返回的是QuerySet,所以可在其后继续调用filter,exclude

    • values()方法获取字典结果,该方法同样会返回QuerySet,但并不是Model的实例对象,而是字典类型,字典的键对应Model的字段名

    • values_list()方法,和values()及其相似,只不过返回的是元组而不是字典,元组元素的顺序与传递的字段顺序相同

    • 对QuerySet进行切片操作,例如Comment.objects.all()[:2],QuerySet不支持负值索引

      • 切片操作会返回另一个QuerySet,单并不会触发数据库的查询
      • 如果使用的是step切片语法,则会触发数据库查询并返回Model实例列表
      • 切片后的QuerySet不能再进行过滤和排序操作
  4. 返回RawQuerySet的查询方法

    Djang的ORM非常强大,支持使用原生sql语句实现查询

    • Comment.objects.raw(‘select * from post_comment‘)
    • 不要拼接SQL语句,可能存在注入风险,raw方法提供params参数,接收一个列表或者字典来替换sql语句中的占位符
    • RawQuerySet支持索引和切片,但比较低效
  5. 返回其他类型的查询方法

    • count()方法,在 数据库层面执行select count(*) 返回数字类型
    • exist()s方法,判断是否存在
    • 使用update()方法,更新实例,update只更新特定的列,而save会更新所有的列
    • delete()方法,删除实例,返回二元组,第一个元素标识删除的实例个数,第二个元素是字典类型,记录每一个Model类型删除的实例个数,如果两个表存在关联关系,且设置了级联删除,则会一起删除

3. 存在关联关系的查询

  1. 反向查询,小写模型名_set
  2. 跨关联关系查询,双下划线
  3. 跨关联关系多值查询
    • 只使用一个filter,将多个条件写在一个过滤器内,则需要同时满足这多个条件
    • 如果使用filter链式调用,虽然总的条件相同,但将不同的条件分开放在不同的filter内,采用链式调用,则可能获取到不同的结果,详情见![http://www.baidu.com]

4. F对象与Q对象

  1. F对象查询,主要用于 操作数据库中某一列的值,可以在 没有实际访问数据库 的情况下对字段的值进行引用

    F对象功能强大,支持跨关联关系。且使用F对象,可以避免线程不安全导致的脏数据。使用F对象,让数据库负责更新过程。F对象描述的是SQl表达式,而不是Python运算。

  2. Q对象查询,用于复杂查询,将关键字封装在一起,并传递给filter,exclude,get等方法。多个Q对象可以使用 &,| 进行组合,产生一个新的Q对象。使用 ~可实现NOT查询。Q对象也可以与关键字参数组合在一起,但这种情况下,Q对象必须放在最前面。

5. 聚合与分组查询

  1. 集合查询
    • aggregate是QuerySet的一个终止子句,返回字典类型,键是聚合的标识,值是聚合的结果
    • 常用聚合函数:Avg(), Count(), Max(), Min(), Sum()
    • 返回字典的键名是Django根据字段名和聚合函数的名称拼接而来的,可以指定名称
    • 可以传递多个聚合函数,以 逗号 分隔
  2. 分组查询
    • 默认对每个Model对象都生成一个统计值,通过annotate方法完成,annotate不是终止子句,它返回一个QuerySet对象,可对结果进行在加工。如果annotate前面有values,则先按照values中的字段将Model分组,再对每个分组计算统计值。
    • 如果查询语句中含有values(),即在annotate前面有values,则先按照values中指定的字段对Model对象进行分组(相等于GROUP BY),再对每个分组计算统计值。如果Model有默认的排序字段,那么该排序字段也会被自动的加入GROUP BY中,可使用不带参数的order_by()方法将排序清除。
    • 如果查询语句中没有values(),而values()在annotate后面,则仅限制需要的字段,没有特殊作用。

Django中ORM元类学习及查询API

上一篇:解决Windows10 UWP应用/Edge无法联网问题


下一篇:Web APIs - FileReader - FileReader.readAsDataURL()