Django 的 orm 和 数据库问题

常用方法

  • all()
    查询所有结果
  • filter()
    筛选满足所给条件的对象,若不存在不会报错
  • get()
    筛选满足所给条件的对象,但返回结果只有一个。如果返回对象超过一个或没有都会报错
  • exclude()
    不满足筛选条件的对象。例如 exclude(id=5) 返回那些 id 不等于 5 的对象
  • values()
    返回一个 QuerySet 得到一个字典,字段:值
  • values_list()
    和 values 一样,只不过返回一个元组序列,只有 值
  • reverse()
    对结果进行反向排序,只有在模型定义了 ordering 或使用了 order_by() 方法才可以使用
  • distinct()
    从返回结果中删除重复记录
  • count()
    返回匹配条件的数量
  • first()
    返回第一条记录
  • last()
    返回最后一条记录
  • exists()
    如果 QuerySet 包含数据,返回 True,否则返回 Flase
  • defer()
    用来排除某些字段,避免将其转换为 Python 对象
  • only
    仅选择需要的字段,但是还是需要查询 id

单表查询

from models import Book
# Books 数据中 3 < id < 7 的数据
Book.objects.filter(id__lt=7, id__gt=3)
# Books 数据中 id 等于 [12, 34, 65] 的数据
Book.objects.filter(id__in=[12, 34, 65])
# Books 数据中 id 不等于 [12, 34, 65] 的数据
Book.objects.exclude(id__in=[12, 34, 65])
# Books 数据中 id 在 10-23 的数据,等价于 between and
Books.objects.filter(id__range=[10, 23]) 
# Books 数据中 name 包含 'test' 的数据
# icontains 表示大小写不敏感,一般用于英文 
Book.objects.exclude(name__contains='test')
# 关于字符串还有
startswith,istartswith, endswith, iendswith 
# 查找 时间字段
Book.objects.filter(created_time__year=1998)
# 还包括 __month __day 

外键查找

正向查找 .属性即可找到相关的对象
反向查找 obj.表名set 查到的是对象,若要查找属性 小写表名_属性

如果在外键中设置了 related_name = "",那么直接可以使用此 name。用来代替 <表名>_set。注:一对一反向查询的时候不需要使用 _set,因为只可能取到一个对象。

  • 利用 双下划线 跨表查询具体值
    查询 book id 是 1 的出版社的名字,sql 语句是执行 inner join 操作
    models.Book.objects.filter(id=1).values("publisher__name") 返回字典
    models.Book.objects.filter(id=1).values_list("publisher__name") 返回元组

注意只有得到的是一个 QuerySet 对象时才能 values,values_list。如果得到的是一个具体对象,则只能查看其属性。
基于对象查找时使用 _set,在 sql 中对应子查询,就是两条 sql 语句。一般是多条 orm 语句,对象。
基于 QuerySet 查询的时候,不用加 set,采用双下划线,在 sql 中类似于 join 操作。查询出来的 queryset 对象有一个属性 query 查看对应的 sql 语句。一般是一条 orm 语句,后面常跟 values()

多对多

正向查找和外键查找类似
例子:作者和书 多对多 关系

# 通过作者创建一本书
author_obj.books.create()
还有 .add()
添加多个的时候需要使用 * 号表达式,还可以直接添加 id
set() 更新对象  remove 删除关联的对象
clear()  删除关联的所有对象

聚合查询和分组查询

  • 聚合
    aggregate()
    from django.db.models import Avg, Sum, Max, Min, Count 这就是 sql 那些内置函数
    查询所有数据的一个函数
例子,一个 Book 的表,现在要求它的平均价格
ret = Books.objects.all().aggregate(price_avg=Avg("price"))
sql 语句:select avg(price) as price__avg from book
  • 分组
    annotate() 对应 sql 中的 group by 操作
sql:
select Count(1) from xxx group by field
orm: 
 models.xxx.objects.values('field').annotate(Count('id'))

F 和 Q()

对两个字段的值做比较可以使用 F()
Django 支持 F() 对象和常数之间的数学操作,修改 char 字段时,可以使用 from django.db.models.functions import Concat
如果要执行 or 操作,可以使用 Q() 查询

事务

from django.db import transaction

try:
    from django.db import transaction
    with transaction.atomic():
        pass
except Exception as e:
    print(str(e))
  • ps 什么时候用 一对一?
    当 一张表 的某一些字段查询的时候比较频繁,另外一些字段查询的不是特别频繁时候,把不怎么常用的字段单独拿出来做成一张表,然后用一对一关联起来
    优势:既保证数据的完整性,又能较快查找

ManyToMany 的特点

当在一个模型中写 ManyToMany 时,Django 会自动创建第三张表。
当然也可以自己创建第三张表,创建一个新模型里面写两个外键即可,不过这样取数据的时候比较麻烦,因为要显示地通过第三张表来查询。
还有一种方法就是在自己创建的第三张表时使用 ManyToMany。可以在模型的 ManyToMany() 属性中设置 through=<你要通过哪张表关联>,through_fields=(,)。自己创建第三张表时,需要指定一个联合唯一索引,但是这样会失去 ORM 封装的部分特性例如 add 和 remove 方法。联合唯一索引例如

class Meta:
    unique_together = ("字段名", "字段名")

如果第三张表没有额外的字段,就用 Django 默认提供的,如果又额外的字段,则可以自己建表。

其他

  • select_related()
    1. 主要针对一对一和外键关系进行优化
    2. 会对表之间进行 join 连表操作,通过减少 SQL 查询的次数来优化
    3. 可以通过可变长参数指定需要 select_related 的字段名,也可以通过使用双下划线 __ 连接字段名来实现递归查询。没有指定的字段不会缓存
    4. 可以通过 depth 参数指定递归的深度,Django 会进行缓存
    5. 在 1.7 以前,只会保留最后一个链式调用,1.8 之后,就可以链式调用了。
  • prefetch_related()
    1. 对于 多对多和外键进行优化。
    2. 它会查询每个表,然后用 Python 处理它们之间的关系。在 SQL 中会使用 in
    3. 可以指定 preftech_related 的字段名,与 selected_related 类似。
  • 批量创建 bulk_create
创建好一个 obj 的列表之后就可以进行
models.A.objects.bulk_create(obj_list)

Django 还有另外一种表结构 contenttype
主要用来一张表同时和多张表关联。
例如:如果现在有一张普通课程表,但是还有一张vip课程表。如果要做价格策略,可以按照整个课程做,也可以按照 vi p卖,这样策略必然不同。可以在策略表中添加两个字段:一个是课程,一个是vip,对应的里面填写课程/vip课程的 id,否则填 0 即可。但这样在后期需要添加另一张表需要和价格策略做匹配的时候,还需要添加字段,不方便。Django 提供了在表中添加表名字段的方法,即表名称和 id。

数据库

Django 可以配置多个数据库,借此实现读写分离
settings.py 中设置 DATABASES = {},拷贝上一份即可,改动某些参数。
然后再执行 python manage.py migrate 的时候,后面可以跟上参数,以指定保存的数据库,--database 数据库名称
在查询时,使用 .using(db_name) 来查询,当然了也可以提前指定,新建脚本 myrouter.py

class Router:
    def db_for_read(self, model, **hints):
        return db_name
    def db_for_write(self, model, **hints):
        return 'default'

settings.py 中指定 DATABASE_ROUTERS = ['myrouter.Router',]
当然了,也可以指定 app。

class Router:
    def db_for_read(self, model, **hints):
        if model._meta.app_label == 'app01':
            return 'db1'
        if model._meta.app_label == 'app02':
            return 'db2'

    def db_for_write(self, model, **hints):
       if model._meta.app_label == 'app01':
            return 'db1'
       if model._meta.app_label == 'app02':
            return 'db2'

详情见官网文档,顺便说一句,Django 的文档是真的好。

你可能感兴趣的:(Django 的 orm 和 数据库问题)