文章目录
- Django模型--数据库(MySQL)-查询
- 1. 模型常用字段
- 2. 字段常用参数
- 3. 常用查询
- 4. 查询条件
- 4.1. Q 对象
- 4.2. 字段查找
- 1. [exact](https://docs.djangoproject.com/en/2.1/ref/models/querysets/#std:fieldlookup-exact)
- 2. [iexact](https://docs.djangoproject.com/en/2.1/ref/models/querysets/#std:fieldlookup-iexact)
- 3. [contains](https://docs.djangoproject.com/en/2.1/ref/models/querysets/#std:fieldlookup-contains)
- 4. [in](https://docs.djangoproject.com/en/2.1/ref/models/querysets/#in)
- 5. [gt](https://docs.djangoproject.com/en/2.1/ref/models/querysets/#gt)
- 6. [startswith](https://docs.djangoproject.com/en/2.1/ref/models/querysets/#startswith)
- 7. [endswith](https://docs.djangoproject.com/en/2.1/ref/models/querysets/#endswith)
- 8. [range](https://docs.djangoproject.com/en/2.1/ref/models/querysets/#range)
- 9.[date](https://docs.djangoproject.com/en/2.1/ref/models/querysets/#date)
- 10 [isnull](https://docs.djangoproject.com/en/2.1/ref/models/querysets/#isnull)
- 11. [order_by](https://docs.djangoproject.com/en/2.1/ref/models/querysets/#order-by)
- 5. 聚合
- 6. 分组
Django模型–数据库(MySQL)-查询
1. 模型常用字段
字段类型 | MySQL类型 | 描述 |
---|---|---|
IntegerField | int | 整型,安全 |
CharField | varchar | 字符串,对于大量文本,请使用TextField 。 |
TextField | text | max_length属性只会影响表单部件Textarea ,对数据库和模型无影响 |
BooleanField | tinyint | 传递True/False进去。如果要可以为空,则用NullBooleanField。 |
DateField | date | 设置DateField.auto_now每次保存对象时,自动设置该字段为当前时间。 设置DateField.auto_now_add当对象第一次被创建时自动设置当前时间。 |
DateTimeField | datetime | 传递datetime.datetime() |
- 详细字段信息请访问:官方文档
2. 字段常用参数
常用参数 | 描述 |
---|---|
primary_key | 为True则指定该字段为模型主键,django会自动添加一个AutoField 来保存主键 |
unique | 如果True ,该字段在整个表格中必须是唯一的,unique 意味着索引的创建。 |
null | 如果True ,表示可以存储空值,null 参数仅影响数据库存储 |
blank | 如果True ,表单验证将允许输入空值。blank 与验证相关 |
default | 设置默认值。 |
DateField.auto_now | 只有调用Model.save()方法才会调用,QuerySet.update方法将不会调用。 |
DateField.auto_now_add | 第一次添加进去,都会将当前时间设置进去。以后修改,不会修改这个值 |
- 详细字段信息请访问:字段参数
- 除非要覆盖默认的主键行为,否则任何字段不需要设置primary_key;
primary_key=True
暗示null=False
和unique=True
。- 主键字段是只读的。如果更改现有对象上的主键值,然后保存它,则将创建一个与旧对象并排的新对象。
- 避免
null
在基于字符串的字段上使用,例如CharField
和TextField
,会“无数据”有两个可能值:NULL
和空字符串。Django约定是使用空字符串,而不是NULL
。 DateField.auto_now
参数只是Date和DateTime以及Time类才有的。
3. 常用查询
3.1 通过模型类的管理器构造QuerySet
3.1.1 管理器
# 管理器指的是 模型类.objects
In [27]: User.objects
Out[27]: <django.db.models.manager.Manager at 0x7fdca807e6d8>
3.2.2 QuerySet
QuerySet 表示数据库中对象的集合。惰性的,创建时不会立刻调用数据库,需要加载内容(比如切片,读取等)时再调用数据库。
3.2 方法
1. all
#首先返回 QuerySet 对象
In [28]: stu = User.objects.all()
In [29]: type(stu)
Out[29]: django.db.models.query.QuerySet
#可以查看相应的 sql 语句
In [33]: print(stu.query)
SELECT `book_user`.`id`, `book_user`.`name`, `book_user`.`age`, `book_user`.`gender`, `book_user`.`c_time` FROM `book_user`
#读取数据,此时调用语句返回结果
In [34]: stu
Out[34]: <QuerySet [
<User: id<201500>name[柯南]gender(男)age|22|c_time:2019-03-14 02:39:15.224760+00:00>,
<User: id<201501>name[毛利]gender(男)age|22|c_time:2019-03-14 02:40:23.069295+00:00>,
<User: id<201502>name[元太]gender(男)age|22|c_time:2019-03-14 02:40:39.109599+00:00>,
<User: id<201504>name[高木]gender(男)age|22|c_time:2019-03-14 02:41:12.685040+00:00>
]>
2. first
# 获取第一条,返回对象
In [36]: User.objects.first()
Out[36]: <User: id<201500>name[柯南]gender(男)age|22|c_time:2019-03-14 02:39:15.224760+00:00>
3. last
# 获取最后一条,返回对象
In [43]: User.objects.last()
Out[43]: <User: id<201504>name[高木]gender(男)age|22|c_time:2019-03-14 02:41:12.685040+00:00>
4. get
#get(**kwargs) 根据传入参数查询,获取并返回一个对象,查询到多个则报错
In [45]: User.objects.get(pk=201501)
Out[45]: <User: id<201501>name[毛利]gender(男)age|22|c_time:2019-03-14 02:40:23.069295+00:00>
5. filter
#filter(**kwargs) 根据传入参数查询,获取并返回过滤后的 QuerySet ;
In [46]: User.objects.filter(gender=1)
Out[46]: <QuerySet [
<User: id<201500>name[柯南]gender(男)age|22|c_time:2019-03-14 02:39:15.224760+00:00>,
<User: id<201501>name[毛利]gender(男)age|22|c_time:2019-03-14 02:40:23.069295+00:00>,
<User: id<201502>name[元太]gender(男)age|22|c_time:2019-03-14 02:40:39.109599+00:00>,
<User: id<201504>name[高木]gender(男)age|22|c_time:2019-03-14 02:41:12.685040+00:00>
]>
6. exclude
#与filter(**kwargs)相反,根据传入参数查询,获取并返回不符合条件的的 QuerySet ;
In [50]: fil = User.objects.filter(gender=1).query
In [51]: exc = User.objects.exclude(gender=1).query
In [52]: print(fil)
SELECT `book_user`.`id`, `book_user`.`name`, `book_user`.`age`, `book_user`.`gender`, `book_user`.`c_time` FROM `book_user` WHERE `book_user`.`gender` = 1
In [53]: print(exc)
SELECT `book_user`.`id`, `book_user`.`name`, `book_user`.`age`, `book_user`.`gender`, `book_user`.`c_time` FROM `book_user` WHERE NOT (`book_user`.`gender` = 1)
7. values
# 指定字段
In [59]: fil = User.objects.filter(gender=1).values('name','gender')
# 返回QuerySet
In [60]: type(fil)
Out[60]: django.db.models.query.QuerySet
# 需要迭代时,返回字典列表,不再是对象列表
In [61]: fil
Out[61]: <QuerySet [
{'name': '柯南', 'gender': 1},
{'name': '毛利', 'gender': 1},
{'name': '元太', 'gender': 1},
{'name': '高木', 'gender': 1}
]>
- values(*fields) 返回QuerySet, 返回字典列表
values
操作sql
中Select
和from
关键字中间的参数部分,即提取的内容。
8. only
# 指定字段
In [65]: fil = User.objects.only('name', 'gender')
# 返回QuerySet
In [66]: type(fil)
Out[66]: django.db.models.query.QuerySet
# sql语句
In [68]: str(fil.query)
Out[68]: 'SELECT `book_user`.`id`, `book_user`.`name`, `book_user`.`gender` FROM `book_user`'
# 需要迭代时,返回字典列表,不再是对象列表
# 此处因为重写User的__str__方法,又获取了其他字段,可以用 dir 方法证明只有属性name和gender
In [69]: fil
Out[69]: <QuerySet [
<User: id<201500>name[柯南]gender(男)age|22|c_time:2019-03-14 02:39:15.224760+00:00>,
<User: id<201501>name[毛利]gender(男)age|22|c_time:2019-03-14 02:40:23.069295+00:00>,
<User: id<201502>name[元太]gender(男)age|22|c_time:2019-03-14 02:40:39.109599+00:00>,
<User: id<201504>name[高木]gender(男)age|22|c_time:2019-03-14 02:41:12.685040+00:00>
]>
# dir
In [70]: dir(fil[0])
Out[70]: [
...
'get_previous_by_c_time',
'id',
'name',
'objects',
'pk'
...
]
- values(*fields) 返回QuerySet, 返回对象列表;
- 操作
sql
中Select
和from
关键字中间的参数部分,即提取的内容。但是一定包含id
; - only 还能访问其他字段,从而提取,故开发中尝试用;
9. defer
#作用和 only 相反,取出除给定字段以外的字段,一定有 id
# 指定被排除的字段
In [71]: res = User.objects.defer('c_time', 'gender', 'id') #id 不会被排除
# 返回QuerySet
In [72]: type(fil)
Out[72]: django.db.models.query.QuerySet
# sql语句
In [73]: str(fil.query)
Out[73]: 'SELECT `book_user`.`id`, `book_user`.`name`, `book_user`.`gender` FROM `book_user`'
# 需要迭代时,返回字典列表,不再是对象列表
In [74]: fil
Out[74]: <QuerySet [
<User: id<201500>name[柯南]gender(男)age|22|c_time:2019-03-14 02:39:15.224760+00:00>,
<User: id<201501>name[毛利]gender(男)age|22|c_time:2019-03-14 02:40:23.069295+00:00>,
<User: id<201502>name[元太]gender(男)age|22|c_time:2019-03-14 02:40:39.109599+00:00>,
<User: id<201504>name[高木]gender(男)age|22|c_time:2019-03-14 02:41:12.685040+00:00>
]>
# dir
In [8]: dir(res[0])
Out[8]: [
...
'get_previous_by_c_time',
'id', # id 无法被排除,一定有
'name',
'objects'
...
]
- id 无法被排除
- 虽然被排除在外,任然可以访问,但需要再执行获取其他字段的
sql
语句
10. 切片
#切片
In [16]: li_user = User.objects.all()[2:4]
# sql 等价语句
In [17]: print(li_user.query)
SELECT `book_user`.`id`, `book_user`.`name`, `book_user`.`age`, `book_user`.`gender`, `book_user`.`c_time` FROM `book_user` LIMIT 2 OFFSET 2
#注意,切片[m:m]合理,但转化为sql语句时,‘limit 0’会抛出异常,故避免
#显示数据
In [18]: li_user
Out[18]: <QuerySet [
<User: id<201502>name[元太]gender(男)age|22|c_time:2019-03-14 02:40:39.109599+00:00>,
<User: id<201504>name[高木]gender(男)age|22|c_time:2019-03-14 02:41:12.685040+00:00>
]>
- 不支持负索引
- 可以用步长,但不推荐,效率低下
- 切片返回 对象列表 ,故不能再排序或过滤(等QuerySet方法)
11. 延伸
更多方法详见:官方文档
4. 查询条件
4.1. Q 对象
多条件的 or and 等查询需要 Q 对象:django.db.models.Q
1. AND(&
)
以下是等效的:
Model.objects.filter(x=1) & Model.objects.filter(y=2)
Model.objects.filter(x=1, y=2)
from django.db.models import Q
Model.objects.filter(Q(x=1) & Q(y=2))
#sql等价语句:SELECT ... WHERE x=1 AND y=2
2. OR(|
)
以下是等效的:
Model.objects.filter(x=1) | Model.objects.filter(y=2)
from django.db.models import Q
Model.objects.filter(Q(x=1) | Q(y=2))
#sql等价语句:SELECT ... WHERE x=1 OR y=2
4.2. 字段查找
字段查找是指定SQL WHERE
子句的内容的方式。它们被指定为QuerySet
方法的关键字参数filter()
, exclude()
以及get()
。
语法格式:Entry.objects.func(字段名__关键字=参数)
#举个栗子: 取出 dob 小于 '2006-01-01' 的 Entry.objects.filter(dob__lte='2006-01-01') #等价sql:SELECT * FROM blog_entry WHERE dob <= '2006-01-01';
1. exact
“精确”匹配。例如:
Entry.objects.get(headline__exact="Cat bites dog")
#生成SQL:SELECT ... WHERE headline = 'Cat bites dog';
2. iexact
不区分大小写的匹配项。所以,查询:
Blog.objects.get(name__iexact="beatles blog")
'''
会匹配Blog标题,甚至是。
"Beatles Blog"
"beatles blog"
"BeAtlES blOG"
...
'''
3. contains
区分大小写的遏制测试。例如
Entry.objects.get(headline__contains='Lennon')
#大致翻译为这个SQL:SELECT ... WHERE headline LIKE '%Lennon%';
还有一个不区分大小写的版本icontains
。
4. in
在给定的可迭代中; 通常是列表,元组或查询集。它不是常见的用例,但接受字符串(可迭代)。
Entry.objects.filter(id__in=[1, 3, 4])
Entry.objects.filter(headline__in='abc')
SQL等价物:
SELECT ... WHERE id IN (1, 3, 4);
SELECT ... WHERE headline IN ('a', 'b', 'c');
5. gt
例:
Entry.objects.filter(id__gt=4)
SQL等价物:
SELECT ... WHERE id > 4;
还有:gte,lt,lte
6. startswith
区分大小写的开头。
例:
Entry.objects.filter(headline__startswith='Lennon')
SQL等价物:
SELECT ... WHERE headline LIKE 'Lennon%';
此外还有istartswith
,在此基础上不区分大小写
7. endswith
区分大小写的结尾。
例:
Entry.objects.filter(headline__endswith='Lennon')
SQL等价物:
SELECT ... WHERE headline LIKE '%Lennon';
同样,存在iendswith
8. range
范围测试(包括在内)。
例:
import datetime
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2005, 3, 31)
Entry.objects.filter(pub_date__range=(start_date, end_date))
SQL等价物:
SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';
您可以在SQL中使用的range
任何地方使用BETWEEN
- 包括日期,数字甚至字符。
9.date
对于datetime字段,将值转换为日期。允许链接其他字段查找。采用日期值。
例:
Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
(此查找不包含等效的SQL代码片段,因为相关查询的实现因不同的数据库引擎而异。)
当USE_TZ
是True
,字段被滤波之前转换为当前时区。
同样存在 year
,month
,day
,week
,week_day
,time
,hour
,minute
,second
10 isnull
采用任一True
或False
,其对应于SQL查询 和分别。IS NULL``IS NOT NULL
例:
Entry.objects.filter(pub_date__isnull=True)
SQL等价物:
SELECT ... WHERE pub_date IS NULL;
11. order_by
默认情况下,a返回的结果按模型中选项QuerySet
给出的排序元组排序。您可以使用该方法在每个基础上覆盖它。``
例:
Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')
上面的结果将按pub_date
降序排序,然后按 headline
升序排序。前面的负号"-pub_date"
表示 降序。升序是隐含的。要随机订购,请使用"?"
,如下所示:
Entry.objects.order_by('?')
注意:order_by('?')
查询可能很昂贵且速度很慢,具体取决于您使用的数据库后端。
要按不同模型中的字段进行排序,请使用与查询模型关系时相同的语法。也就是说,字段的名称,后跟双下划线(__
),后跟新模型中字段的名称,依此类推,因为您想要加入多个模型。例如:
Entry.objects.order_by('blog__name', 'headline')
5. 聚合
通过管理器QuerySet
的 aggregate
方法实现,详见: 官方文档
In [22]: from django.db.models import Avg
In [23]: User.objects.aggregate(Avg('age'))
Out[23]: {'age__avg': 22.0}
上例中取出age
字段平均值
5.0 aggregate 和 annotate
上例子
# aggregate
In [22]: agg = User.objects.aggregate(Max('age'))
In [23]: type(agg)
Out[23]: dict
In [25]: agg
Out[25]: {'age__max': 22}
#aggregate 是在整张表中取‘age’字段最大值
# annotate
In [26]: ann = User.objects.annotate(Max('age'))
In [27]: type(ann)
Out[27]: django.db.models.query.QuerySet
In [28]: type(ann[0])
Out[28]: book.models.User
# 返回对象集合
In [30]: for i in ann:
...: print(i.age__max)
...:
22
22
22
22
#annotate 为每一条记录取'age'字段最大值
5.1 Avg
In [46]: res = User.objects.aggregate(Avg('age'))
In [47]: res
Out[47]: {'age__avg': 22.0}
- class Avg(expression, output_field=FloatField(), **extra)[source]。这只是参数,构造方法见源码Avg类父类构造函数
- 返回给定表达式的平均值,它必须是数值,除非指定不同的output_field。
- 默认的别名:字段__avg;返回类型:float(或指定任何output_field的类型)
5.2 Count
# class Count(expression, distinct=False, **extra)[source]
In [50]: res = User.objects.filter(age=22).aggregate(Count('age'))
In [51]: res
Out[51]: {'age__count': 4}
#distinct=True
In [52]: res = User.objects.filter(age=22).aggregate(Count('age', distinct=True))
...:
In [53]: res
Out[53]: {'age__count': 1}
- 返回与expression相关的对象的个数;
- 默认的别名:字段__count;返回类型:int
- 有一个可选的参数:distinct。如果distinct=True,Count 将只计算唯一的实例。默认值为False。
5.3 Max
#class Max(expression, output_field=None, **extra)[source]
In [5]: res = User.objects.aggregate(Max('pk'))
In [6]: res
Out[6]: {'pk__max': 201504}
- 返回expression的最大值。
- 默认的别名:__max;
- 返回类型:与输入字段的类型相同,如果提供则为
output_field
类型
5.4 Min
#class Min(expression, output_field=None, **extra)[source]
In [7]: res = User.objects.aggregate(Min('pk'))
In [8]: res
Out[8]: {'pk__min': 201500}
- 返回expression的最小值。
- 默认的别名:__min;
- 返回类型:与输入字段的类型相同,如果提供则为
output_field
类型
5.5 StdDev
# class StdDev(expression, sample=False, **extra)[source]
In [9]: User.objects.aggregate(StdDev('pk'))
Out[9]: {'pk__stddev': 1.479019945774904}
- 返回expression的标准差。
- 默认的别名:__stddev;
- 返回类型:float;
- 有一个可选的参数:sample。默认情况下,返回群体的标准差。如果sample=True,返回样本的标准差。SQLite 没有直接提供StdDev。
5.6 Variance
#class Variance(expression, sample=False, **extra)[source]
In [10]: User.objects.aggregate(Variance('pk'))
Out[10]: {'pk__variance': 2.1875}
- 返回expression的方差。
- 默认的别名:__variance
- 返回类型:float
- 有一个可选的参数:sample。SQLite 没有直接提供Variance。
5.7 Sum
# class Sum(expression, output_field=None, **extra)[source]
In [12]: User.objects.aggregate(Sum('age'))
Out[12]: {'age__sum': 88}
- 计算expression的所有值的和。
- 默认的别名:__sum;
- 返回类型:与输入字段的类型相同,如果提供则为output_field类型
6. 分组
6.1 认知
分组是基于聚合(annotate)的对数据进一步归类,分组一定要有聚合
6.2 使用
In [21]: User.objects.values('gender').annotate(人数=Count('pk'))
Out[21]: <QuerySet [{'人数': 4, 'gender': 1}, {'人数': 2, 'gender': 0}]>
#sql
In [24]: res = User.objects.values('gender').annotate(人数=Count('pk'))
In [25]: str(res.query)
Out[25]: 'SELECT `book_user`.`gender`, COUNT(`book_user`.`id`) AS `人数` FROM `book_user` GROUP BY `book_user`.`gender` ORDER BY NULL'
- 这是最简单的分组,基于提取关键字段达到分组效果。