美文网首页
Django 的 orm 和 数据库问题

Django 的 orm 和 数据库问题

作者: vckah | 来源:发表于2018-06-20 17:13 被阅读0次

    常用方法

    • 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>",那么直接可以使用此 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 和 数据库问题

          本文链接:https://www.haomeiwen.com/subject/klnveftx.html