Django之ORM查询操作

1、测试准备

  1.1 终端打印ORM原生的sql语句

    方法:把下面配置加入到settings中。

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

  1.2 调用django环境建立测试文件

# 直接再项目下建立test.py文件
# 并把下面的语句添加进去
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "untitled15.settings")
    import django
    django.setup()

    from app01 import models

2、ORM查询操作

  2.1 单表操作即增删改查

# 创建数据
    book_obj = models.Book.objects.create()

    book_obj = models.Book()
    book_obj.save()
# 编辑数据 
    models.Book.objects.filter().update()

    book_obj = models.Book.objects.filter().first()
    book_obj.name = 'SUN'
    book_obj.save()
# 删除数据
    models.Book.objects.filter().delete()

    book_obj = models.Book.objects.filter().first()
    book_obj.delete()  # 了解即可
# 查看数据
    models.Book.objects.all()

    models.Book.objects.filter()

  2.2 all()

    返回结果类型:QuerySet对象,(一个大列表)

    说明:查询所有结果, 但是在django中做了优化,限制只取前21条数据

# 示例
books_obj = models.Book.objects.all()
print(books_obj)

# 结果:(列表中是一个个数据对象)
# <QuerySet [<Book: Book object>, <Book: Book object>,...>

# 强调:
# 1. 可以使用索引取值,但不支持负数
print(books_obj[1])    # 结果是: Book object
print(books_obj[-1])    # 结果是:异常AssertionError: Negative indexing is not supported.

# 2. 内部优化,只取前21条,如果要拿到后面的数据,会再去取一次
books_obj = models.Book.objects.all()
books_obj[22]

# 执行的结果是:(使用上面的LOGGING配置打印sql语句)
# (0.000) SELECT `app01_book`.`id`, `app01_book`.`name`, `app01_book`.`price`, `app01_book`.`publish_id` FROM `app01_book` LIMIT 21; args=()
# (0.001) SELECT `app01_book`.`id`, `app01_book`.`name`, `app01_book`.`price`,`app01_book`.`publish_id` FROM `app01_book`; args=()

  2.3 属性query

    返回类型: Query对象

    说明:调用后返回原生的sql语句,只能QuerySet对象使用

books_obj = models.Book.objects.all()
print(books_obj.query)

# SELECT `app01_book`.`id`, `app01_book`.`name`, `app01_book`.`price`, `app01_book`.`publish_id` FROM `app01_book`

  2.4 filter(**kwargs) 和 exclude(**kwargs)

    返回类型:QuerySet对象

    说明:filter() 筛选出与所给筛选条件相匹配的对象,多个条件是and的关系,筛选条件可以是:对象,或字典

      exclude()筛选出与所给筛选条件不匹配的对象,多个条件是and的关系

book_obj = models.Book.objects.filter(publish_id=1, price=45)
for obj in book_obj:
    print(obj.id, obj.name, obj.price,obj.publish_id, obj.publish.name)

book_obj = models.Book.objects.exclude(publish_id=1, price=45)
for obj in book_obj:
    print(obj.id, obj.name, obj.price,obj.publish_id)

    筛选条件:神奇的双下划线 "__"

常用的筛选条件的对应关系
sql语句筛选条件
django筛选条件
id in (1,2,3)
id__in=[1,2,3]
id > 2
id__gt = 2
id >= 2
id__gte = 2
id < 2
id__lt = 2
id <= 2
id__lte = 2
id between 10 and 20
id__range = [10, 20]
name like"sun%"
name__startswith = "sun"
name like "%sun%"
name_contains = "sun"
对于时间类型数据的查找:
__year日期字段的年份
__month日期字段的月
__day日期字段的日# 示例: # 找到2019年的出版社 author = models.Publish.objects.filter(create_time__year="2019").values() # 找到2019年4月27日的出版社 from datetime import datetime author = models.Publish.objects.filter(create_time=datetime.datetime(2019, 4,27)).values() # 找到2019年4月份的出版社 from datetime import datetime, timedelta author = models.Publish.objects.filter(create_time__range=(datetime(2019,4,1), datetime(2019,5,1)-timedelta(days=1)))

  2.5 order_by(*field)、reverse() 和 distinct()

    返回类型:QuerySet对象

    说明:order_by 对查询结果排序,支持'-id',负号表示降序;支持多个参数,可以有多个排序条件

       reverse() 对排序后的结果反向排序。order_by("id").reverse()相当于order_by("-id")

      distinct() 把取出的结果去重。在mysql中obj.values("id").distinct(), 在PostgreSQL中objects.distinct("id")

book_obj = models.Book.objects.order_by("price", "-id")

# 说明:先按照"price"从小到大排序(升序),如果相等,按照"id"从大到小排序。

  2.6 get()、first()和last()

    返回类型:定义表的类的实例化对象

    说明:first() 返回第一条记录

      last() 返回最后一条记录

      get() 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误

book_obj = models.Book.objects
print(book_obj.first())
print(book_obj.last())
print(book_obj.get(id=1))

# 结果是:三个Book object

print(book_obj.get(price=45))
# 结果是报错:
app01.models.MultipleObjectsReturned: get() returned more than one Book -- it returned 9!

book_obj = models.Book.objects.filter(id__gt=3)  # 条件:id > 3
print(book_obj.get(id=1))
# 结果是报错:
app01.models.DoesNotExist: Book matching query does not exist.

  2.7 values() 和values_list() 

    返回类型:QuerySet 对象

    说明:values() 返回的是每个元素为字典的QuerSet 对象

       values_list() 返回的是每个元素为元组的QuerSet 对象

models.Strudents.objects.values("id", "name")
# 结果:
<QuerySet{"id": 1, "name": "sun"}, {"id": 2, "name": "yw"}>

models.Strudents.objects.values_list("id", "name")
# 结果:
<QuerySet(1,  "sun"), (2, "yw")>

  2.8 count()

    返回类型:整数类型

    说明:返回数据库中匹配查询(QuerySet)的对象数量

book_obj = models.Book.objects.filter(id__gt=3)
print(book_obj.count())

  2.9 exists()

    返回类型:bool类型

    说明:对查询出来的QuerySet进行有误数据的判断,有就返回True,否则返回False;可以直接用queryset对象判断

返回False;可以直接用queryset对象判断

book_obj = models.Book.objects.filter(id__gt=3)
pprint(book_obj.exists())

# 以下两种方式效果一样
if book_obj:
    pass

if book_obj.exists():
    pass

  2.10 aggregate(*args, **kwargs)

    返回类型:字典

    功能:对筛选的结果进行聚合

from django.db.models import Avg, Max, Min, Sum, Count
book = models.Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
print(book)
# 结果:
# {'price__avg': 52.956522, 'price__max': Decimal('123.00'), 'price__min': Decimal('1.00')}

# 可以设置key名
book = models.Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
print(book)
# {'avg_price': 52.956522, 'max': Decimal('123.00'), 'min': Decimal('1.00')}

  2.11 annotate()

    返回类型:QuerSet对象

    说明:分组,调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数),如果涉及到跨表,则会先关联join,再分组查询。

# 统计每种价格的书本数
book_list = models.Book.objects.values("price").annotate(num=Count("id"))
print(book_list.query)
print(book_list)

# 结果:
# SELECT `app01_book`.`price`, COUNT(`app01_book`.`id`) AS `num` FROM `app01_book` GROUP BY `app01_book`.`price` ORDER BY NULL
# <QuerySet [{'price': Decimal('45.00'), 'num': 9}, {'price': Decimal('35.00'), 'num': 1}...]>

# 分析:
# 如果annotate前values,则values内的字段即分组条件,得到的结果是一个个字典组成的QuerySet对象
# 如果annotate前不写values,则默认以该表的id分组,得到的是一个个表的对象组成的QuerySet对象,如:
book_list = models.Book.objects.annotate(num=Count("id"))
print(book_list.query)
print(book_list)

# 结果:
# SELECT `app01_book`.`id`, `app01_book`.`name`, `app01_book`.`price`, `app01_book`.`publish_id`, COUNT(`app01_book`.`price`) AS `num` FROM `app01_book` GROUP BY `app01_book`.`id` ORDER BY NULL
# <QuerySet [<Book: Book object>, <Book: Book object> '...(remaining elements truncated)...']>



# 示例1:
# 统计每本数作者个数
book_list = models.Book.objects.annotate(num=Count("authors")).values_list('name','num')
print(book_list)

# 结果:
# SELECT `app01_book`.`name`, COUNT(`app01_book2author`.`author_id`) AS `num` FROM `app01_book` LEFT OUTER JOIN `app01_book2author` ON (`app01_book`.`id` = `app01_book2author`.`book_id`) GROUP BY `app01_book`.`id` ORDER BY NULL

# <QuerySet [('绩效使能', 2), ('址簿', 2) '...(remaining elements truncated)...']>
注意:
如果报错:   
 django.db.utils.InternalError:(1055, "'djangoday3.app01_book.name' isn't in GROUP BY")
原因:    
数据库设置了"ONLY_FULL_GROUP_BY",这个模式表示,分组之后只能拿到分组的字段,
取消方法:    
    配置文件my.ini中取消,或:    
    select @@sql_mode;    
    set global sql_mde = "上面的结果去掉ONLY_FULL_GROUP_BY"

  2.12 F查询与Q查询

    说明:F("字段")支持同一张表的两个不同字段比较,同时F()支持与常数之间的加减乘除和取模运算

from django.db.models import F
res = models.Book.objects.filter(maichu__gt=F('kucun')).values('name')

Book.objects.all().update(price=F("price")+30) 

    Q()查询可以提供复杂查询,支持and,or, not,对应的是&(,),|,~

from django.db.models import Q
res = models.Book.objects.filter(Q(name='三体')|Q(price='128)) # or
res = models.Book.objects.filter(Q(name='三体'),Q(price='128')) # and
res = models.Book.objects.filter(name='三体',price='128')  # and
res = models.Book.objects.filter(~Q(price='128'))  # not

Q 另一种用法
q1 = Q()
q1.connector = "OR"
q1.children.append(("id", 1))
q1.children.append(("id", 2))

q2 = Q()
q2.connector = "OR"
q2.children.append(("c1", 1))
q2.children.append(("c1", 2))

conn = Q()
conn.add(q1, "AND")
conn.add(q2, "AND")

变成另一种写法:
condition_dict = {
    "q1": [("id", 1), ("id", 2)],
    "q2": [("c1", 1), ("c1", 2)]
}
conn = Q()
for key, value in condition_dict.items():
    q = Q()
    q.connector = "OR"
    for i in v:
        q.children.append(i)
    conn.add(q, "AND")

  2.13 extra() 

    说明:支持额外的查询条件以及相关表、排序

# 语法:
models.Book.objects.extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)

#参数说明:
# 映射
# select = {"a": "....%s.....", "b": "...%s..."}  (value值是原生sql语句)
# select_params = [1,2] (按顺序对应上面的%s)

# 条件
# where = ["...%s..", "...", "%s"] (每个元素都可以是原生sql语句),and连接
# params = [1,2,3](按顺序对应上面的%s)

#
# tables = []

# 排序
# order_by = [...]

# 示例:
res = models.Book.objects.extra(
    select={"nums": "select count(1) from app01_author where id > %s"},
    select_params=[1],
    where=["price > %s"],
    params=[45],
    tables=["app01_author"],
    order_by=["id"]
)

# 对应的sql语句
# SELECT
#   `app01_book`.`id`,
#   `app01_book`.`name`,
#   `app01_book`.`price`,
#   `app01_book`.`publish_id`,
#   (select count(1) from app01_author where id > 1) AS `nums`,
# FROM
#     `app01_book`,
#     `app01_author`
# WHERE
#     (price > 45)
# ORDER BY
#     `app01_book`.`id` ASC
# 对查找到的字段起别名

# 第一种方法:extra(select={"new_name": "old_name"})(不能用于连表字段),
Users.objects.filter().extra(select={'new_name': 'old_name'}).values('new_name')

# 第二种方法:.annotate(new_names=F("old_name")), 可用于连表字段
Users.objects.filter(id=1).annotate(new_name=F('old_name')).values('new_name')
或者    
authors = Author.objects.values("name","authorinfo__addr").annotate(addr=F("authorinfo__addr"))

  2.14 原生sql语句

from django.db import connection, connections

cursor = connection.cursor()
# cursor = connections["default"].cursor(), 和上句效果一样
cursor.execute("select * from app01_book")

# res = cursor.fetchall()
# res = cursor.fetchone()
res = cursor.fetchmany(2)
print(res)

   2.15 defer() 和 only()(查询优化)

    说明:only("a")  只取a字段,但也能取出其他字段只是需要重新查询数据库

      defer("a")  除a字段之外,的所有字段

books = models.Book.objects.filter(id__lt=3).only("price")
for book in books:
    print(book.price)

# 结果:
# 45.00 
# 78.00
# sql语句
# (0.000) SELECT `app01_book`.`id`, `app01_book`.`price` FROM `app01_book` WHERE `app01_book`.`id` < 3; args=(3,)

books = models.Book.objects.filter(id__lt=3).only("price")
for book in books:
    print(book.name)

# 结果:
# 绩效使能
# 地址簿
(0.000) SELECT `app01_book`.`id`, `app01_book`.`price` FROM `app01_book` WHERE `app01_book`.`id` < 3; args=(3,)
(0.001) SELECT `app01_book`.`id`, `app01_book`.`name` FROM `app01_book` WHERE `app01_book`.`id` = 1; args=(1,)
(0.001) SELECT `app01_book`.`id`, `app01_book`.`name` FROM `app01_book` WHERE `app01_book`.`id` = 2; args=(2,)

  2.16 dates() 和datetimes()

def dates(self, field_name, kind, order='ASC'):
    """ 根据时间进行某一部分进行去重查找并截取指定内容
     kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
     order只能是:"ASC"  "DESC"
     并获取转换后的时间
        - year : 年-01-01
        - month: 年-月-01
        - day  : 年-月-日
    """
# 示例
publish_dates = models.Publish.objects.dates("create_time", "month")
print(publish_dates)
# 结果:
# <QuerySet [datetime.date(2019, 2, 1), datetime.date(2019, 4, 1), datetime.date(2019, 5, 1)]>
# (0.001) SELECT DISTINCT CAST(DATE_FORMAT(`app01_publish`.`create_time`, '%Y-%m-01') AS DATE) AS `datefield` FROM `app01_publish` WHERE `app01_publish`.`create_time` IS NOT NULL ORDER BY `datefield` ASC LIMIT 21; args=()


def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
    """
    # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
    # kind只能是 "year", "month", "day", "hour", "minute", "second"
    # order只能是:"ASC"  "DESC"
    # tzinfo时区对象
    """
    ...
# 示例:
models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))


"""
pip3 install pytz
import pytz
pytz.all_timezones
pytz.timezone(‘Asia/Shanghai’)
"""

  2.17 none()

    说明:得到一个空的QuerySet对象

models.Book.objects.none()
#结果:
<QuerySet []>

  2.18 bulk_create()(数据库优化)

    说明:批量创建

bulk_create(self, objs, batch_size=None)
# 参数:
    objs: 列表
    batch_size: 表示一次插入的个数,<999个

# 示例:
objs = [models.Publish(name="aaa", email="1@qq.com"), ...]
models.Publish.objects.bulk_create(objs, 100)

  2.19 get_or_create 和 update_or_create()

get_or_create(self, defaults=None, **kwargs)

# 说明:
#     如果存在,则获取,否则,创建
#     defaults 指定创建时,其他字段的值
# 示例:
author, is_created = models.Author.objects.get_or_create(name="sun", defaults={"authorinfo": 2})
print(author, is_created)
# 存在的结果:Author object False
# 不存在的结果:Author object True

update_or_create(self, defaults=None, **kwargs)
#     如果存在,则更新,否则,创建
#     defaults 指定创建时或更新时的其他字段
# 使用方法如上

  2.20 in_bulk()  

    说明:根据主键进行的查找(得到的是字典格式)

book1 = models.Book.objects.in_bulk([1,2,3])
print(book1)
book2 = models.Book.objects.filter(id__in = [1,2,3])
print(book2)

结果:
{1: <Book: Book object>, 2: <Book: Book object>, 3: <Book: Book object>}
<QuerySet [<Book: Book object>, <Book: Book object>, <Book: Book object>]>

 

posted @ 2019-04-30 11:29  yw_sun  阅读(831)  评论(0)    收藏  举报