python操作mysql

python 与 mysql 相关的记录。

django orm对数据库的操作原理

django orm对数据库的操作原理

django models

简介

一个模型(model)是单一的,与数据库相关的数据源。它包含了存储数据必需的字段和行为。一般情况下,每个模型映射一个单独的数据库表。

  • 每个模型是 django.db.models.Model 的一个Python子类。
  • 该模型的每个属性都代表一个数据库字段。
  • 综上,Django给你自动生成数据库的访问API。
  • 数据表名 appname_modelnme 是根据模型的元数据生成,但可以进行自定义。

字段类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
from django.db import models

models.AutoField
# 根据已有id自增长的整形唯一字段,一般每个model类不需设置该字段,因为django会为每个model自动设置。
# django默认会为每个model类添加如下语句:id = models.AutoField(primary_key=True)当其他字段添加了primary_key属性,则不会创建id字段了,每个model类仅能有一个主键
models.BigAutoField
# 一个64位的自增整数字段,范围1到9223372036854775807。
models.BigIntegerField
# 一个64位的整数字段,范围从-9223372036854775808到9223372036854775807。该字段的默认表单部件是TextInput。
models.BinaryField
# 存储原始二进制数据的字段。它可以被分配字节、bytearray或memoryview。
models.BooleanField
# 布尔型字段,默认的表单窗口部件是CheckBoxInput,若null=True则为NullBooleanSelect。
models.CharField(max_length=200)
# 字符型字段,默认的表单窗口部件是TextInput。该字段类型有一个必需参数:max_length 在数据库水平限定了字符串最大长度
models.CommaSeparatedIntegerField
models.DateField
# 日期字段,字段的值是python中datetime.date的实例,默认的表单窗口是TextInput有几个可选的参数:
# auto_now=True/False:当设置为True时,每当该对象使用save()时,该字段的值就会被更新。
# auto_now_add=True/False: 当设置为True时,该字段的值为该对象被创建时的日期
models.DateTimeField
# 日期和时间字段,值为datetime.datetime实例。可选参数:
# auto_now=True/False:当设置为True时,每当该对象使用save()时,该字段的值就会被更新。
# auto_now_add=True/False: 当设置为True时,该字段的值为该对象被创建时的日期
models.DecimalField(max_digits=10, decimal_places=2)
# 混合精度的小数型数字字段。有两个必需的参数:
# max_digits=ingt_number:限定数字的最大位数(包含小数位)
# decimal_places=int_number:存储数字的小数位
models.DurationField
# 用于存储时间周期的字段。
models.EmailField
# 邮件字段,使用EmailValidator进行验证。
models.FileField
# 一个文件上传字段。不能设置primary_key,有两个可选参数:
# upload_to 如果使用默认的FileSystomStorage,文件将会存储到settings文件中配置的MEDIA_ROOT路径中。
# storage 用来设定文件存储仓库。
models.FilePathField
# 这个字段的值被限制在系统上某个目录中的所有文件名集合中。有三个参数
# path='': 该参数必需。上行所说的‘某个目录’的绝对路径。Example: "/home/images".
# match='pattern': 可选参数。格式是正则表达式。用来拣选符合匹配正则表达式的文件
# recursive=True/False: 可选参数,默认为False。设定是否递归该目录下所有子目录的所有文件。
models.FloatField
# 浮点字段,默认的表单窗口部件是NumberInput。
# FloatField在内部使用Python中的float对象,而DecimalField在内部使用Python中的decimal对象。
models.GenericIPAddressField(protocol='both', unpack_ipv4=False, **options)
# ip地址字段
# protocol='both/ipv4/ipv6' 默认为both
models.IPAddressField
models.IntegerField
# 整数字段,范围 -2147483648 to 2147483647。
models.NullBooleanField
# 类似于BooleanField,不同的是其允许值为null。
models.PositiveIntegerField
# 非负整数字段,范围 0 to 2147483647。
models.PositiveSmallIntegerField
# 非负整数字段,范围 0 to 32767。
models.SlugField
# 只包含字母、数字、下划线或连字符。它们通常在url中使用。
models.SmallIntegerField
models.TextField
# 与CharField类似,但一般用来存储体积较大的文本。
models.TimeField
# 时间字段,其值为datetime.time实例
models.URLField
# URL字段,
models.UUIDField
# 通用唯一标识字段,当不想用django默认设置的AutoField字段时,可以用该字段代替。
1
2
3
4
5
6
# -*- coding: utf-8 -*-

from django.db import models

class Example(models.Model):
pass

字段选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 以下参数对所以字段类型均有效,且是可选的。
null 如果设置为True, 当该字段为空时,Django 会将数据库中该字段设置为 NULL,默认为 False。
blank 如果设置为 True ,该字段允许为空。默认为 False 。
choices 可选的,限制了该选项的字段值必须是所指定的choice中的一个。
db_column 数据库column名称。默认为本字段的名称。
db_index 如果为True的话,该字段的数据库索引将会被创建
db_tablespace
default 该字段的默认值。可以是一个值或者是个可调用的对象,如果是个可调用对象,每次实例化模型时都会调用该对象。
editable 默认为True,若为False,则不会在admin/界面显示
error_messages error_messages参数允许覆盖字段将引发的默认消息。传入一个字典,其中的键与要覆盖的错误消息相匹配。
help_text 额外的“帮助”文本,随表单控件一同显示。即便你的字段未用于表单,它对于生成文档也是很有用的。
primary_key 如果设置为 True ,将该字段设置为该模型的主键。
unique 如果设置为 True,这个字段必须在整个表中保持值唯一。
unique_for_date 将其设置为DateField或DateTimeField的名称,以要求该字段对于日期字段的值是惟一的。
unique_for_month 与unique_for_date类似,但要求字段相对于月份是唯一的。
unique_for_year 与unique_for_date类似,但要求字段相对于年是唯一的。
verbose_name 字段可读的名称。如果没有给出详细名称,Django将使用字段的属性名称自动创建它,将下划线转换为空格。
validators 该字段运行的验证器列表。

# 关联字段
ForeignKey, ManyToManyField 和 OneToOneField,第一个参数为模型的类名,后面可以添加一个 verbose_name 参数:

1. ForeignKey 多对一的关系。
需要两个位置参数:被关联的类和 on_delete 选项。
如果要创建一个递归关系 -- 一个与其自身有多对一关系的对象 -- 则使用 models.ForeignKey('self', on_delete=models.CASCADE)。
poll = models.ForeignKey(
Poll,
on_delete=models.CASCADE,
verbose_name="the related poll",
)
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(
Place,
on_delete=models.CASCADE,
verbose_name="related place",
)

ForeignKey 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class ForeignKey(
to,
on_delete,
related_name=None,
related_query_name=None,
limit_choices_to=None,
parent_link=False,
to_field=None,
db_constraint=True,
swappable=True,
**kwargs
)

# 参数注解
to:
所关联的模型;
当引用的模型为其他app中的模型时,要加上app名称标签:'app_name.model_name';
数据库会自动在外键字段上创建索引,可以使用db_index=False关闭该功能。
on_delete:
当删除 "一" 模型对象时,django会根据该参数的值对与该对象相关联的其他对象(也就是 ‘多’)进行操作;
models.CASCADE: 默认为models.CASCADE 级联删除。当删除'一'时,‘多’会被删除。
modles.PROTECT: 当删除一个具有外键关系的对象时,会引发一个异常,阻止删除该对象。
models.SET_NULL: 设置删除对象所关联的外键字段为null。但字段的null属性必需为True。
models.SET_DEFAULT: 设置删除对象所关联的外键字段为默认的值。
models.SET(value): 设置删除对象所关联的对象的外键字段为value,value也可以是一个可调用函数。
models.DO_NOTHING: 不做任何操作
related_name:
设置从关联对象到自身的关系的名称,若值为'+' 则关联对象与自身无逆向关系。
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='+',
)
related_query_name:
用于目标模型的反向筛选器名称。如果设置为related_name或default_related_name则使用它,否则默认为模型名。
limit_choices_to:
当使用ModelForm或admin呈现此字段时,设置此字段可用选项的限制(默认情况下,queryset中的所有对象都可以选择)。可以使用字典、Q对象或返回字典或Q对象的可调用对象。
to_field:
设置所关联对象的关联字段,如果引用不同的字段,该字段必须具有unique=True。默认为关联对象的主键字段。
db_constraint:
是否创建外键约束,默认为true。

源代码

多对一的关系。需要两个位置参数:被关联的类和 on_delete 选项。
如果要创建一个递归关系,一个与其自身有多对一关系的对象,则使用 models.ForeignKey('self', on_delete=models.CASCADE)

1
2
3
4
5
6
7
8
9
10
from django.db import models

class Manufacturer(models.Model):
# ...
pass

class Car(models.Model):
manufacturer = models.Foreignkey(
'Manufacturer', on_delete = models.CASCADE
)

ManyToManyField 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class ManyToManyField(
to,
related_name=None,
related_query_name=None,
limit_choices_to=None,
symmetrical=None, through=None,
through_fields=None,
db_constraint=True,
db_table=None,
swappable=True,
**kwargs
)

# 参数注解
db_table:
多对多关系会在两个模型所对应的表中间创建一个‘中间表’ ,将多对多转换为两个多对一,该选项为这个中间表设置名称。一般来说django会默认为中间表创建名称,但人们读起来可能搞不清楚究竟中间表关联到了哪里。
through:
Django会自动生成一个表来管理多对多关系。
但是,如果希望手动指定中间表,可以使用through选项指定表示要使用的中间表的Django模型。
though_field:
当though参数被使用时,该参数才会有效。指定使用哪些中间模型字段来确立两个模型的多对多关系。
其它参数参考 ForeignKey

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from django.db import models

class Person(models.Model):
name = models.CharField(max_length=50)

class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership',
through_fields=('group', 'person'),
)

class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
inviter = models.ForeignKey(
Person,
on_delete=models.CASCADE,
related_name="membership_invites",
)
invite_reason = models.CharField(max_length=64)

# Membership 有两个外键 Person(person和inviter)
# 通过 through_fields 显式的指定Django应该使用哪个外键
# through_fields 接受一个2元组(fiels1,fields2)
# fields1:多对多关系字段被定义的那个模型的外键名称(本例中的group)
# fields2:是目标模型的外键名称(本例中的person)。

OneToOneField 示例

1
2
3
4
5
class OneToOneField(
to,
on_delete,
to_field=None, **kwargs
)

model操作方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# mysite/blog/models.py 

from django.db import models

class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()

def __str__(self):
return self.name

class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()

def __str__(self):
return self.name

class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
number_of_comments = models.IntegerField()
number_of_pingbacks = models.IntegerField()
rating = models.IntegerField()

def __str__(self):
return self.headline

创建对象

1
2
3
4
5
Model.save(force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None)

force_insert: boolean,强制创建
force_update:boolean,强制更新
update_fields:更新的字段列表

要创建一个对象,用关键字参数初始化它,然后调用 save() 将其存入数据库。
这在幕后执行了 INSERT SQL 语句。Django 在你显式调用 save() 才操作数据库。
save() 方法没有返回值。

1
2
3
>>> from blog.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save()

保存 ForeignKey 字段
本例为 Entry 类的实例 entry 更新了 blog 属性,假设 Entry 和 Blog 的实例均已保存在数据库中(因此能在下面检索它们):

1
2
3
4
5
>>> from blog.models import Blog, Entry
>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()

保存 ManyToManyField 字段
在字段上使用 add() 方法为关联关系添加一条记录。本例将 Author 实例 joe 添加至 entry 对象:

1
2
3
>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)

要一次添加多行记录至 ManyToManyField 字段,在一次调用 add() 时传入多个参数,像这样:

1
2
3
4
5
>>> john = Author.objects.create(name="John")
>>> paul = Author.objects.create(name="Paul")
>>> george = Author.objects.create(name="George")
>>> ringo = Author.objects.create(name="Ringo")
>>> entry.authors.add(john, paul, george, ringo)

更新对象

1
2
3
4
5
>>> from blog.models import Blog
>>> b = Blog.objects.get(name='Beatles Blog')
>>> b.name = 'Lucy'
>>> b.save()
>>> # b.save(update_fields=['name'])

字段查询

字段查询即你如何制定 SQL WHERE 子句。它们以关键字参数的形式传递给 QuerySet 方法 filter()exclude()get()

基本的查询关键字参数遵照 field__lookuptype=value。(有个双下划线)。例如:

1
>>> Entry.objects.filter(pub_date__lte='2006-01-01')

转换为 SQL 语句大致如下:

1
SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
exact

精确匹配。如果提供的用于比较的值是None,它将被解释为SQL NULL。

1
2
Entry.objects.get(id__exact=14)
Entry.objects.get(id__exact=None)
1
2
SELECT ... WHERE id = 14;
SELECT ... WHERE id IS NULL;
iexact

不区分大小写的精确匹配。如果提供的用于比较的值是None,它将被解释为SQL NULL。

1
2
Blog.objects.get(name__iexact='beatles blog')
Blog.objects.get(name__iexact=None)
1
2
SELECT ... WHERE name ILIKE 'beatles blog';
SELECT ... WHERE name IS NULL;
contains
1
Entry.objects.get(headline__contains='Lennon')
1
SELECT ... WHERE headline LIKE binary '%Lennon%';
icontains
1
Entry.objects.get(headline__icontains='Lennon')
1
SELECT ... WHERE headline ILIKE '%Lennon%';
in

在给定的迭代中;通常是列表、元组或查询集。

1
2
Entry.objects.filter(id__in=[1, 3, 4])
Entry.objects.filter(headline__in='abc')
1
2
SELECT ... WHERE id IN (1, 3, 4);
SELECT ... WHERE headline IN ('a', 'b', 'c');

也可以使用queryset来动态地评估值列表,而不是提供一个文字值列表:

1
2
inner_qs = Blog.objects.filter(name__contains='Cheddar')
entries = Entry.objects.filter(blog__in=inner_qs)
1
SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%')
gt
1
Entry.objects.filter(id__gt=4)
1
SELECT ... WHERE id > 4;
gte
lt
lte
startswith
1
Entry.objects.filter(headline__startswith='Lennon')
1
SELECT ... WHERE headline LIKE binary 'Lennon%';
istartswith
1
Entry.objects.filter(headline__istartswith='Lennon')
1
SELECT ... WHERE headline ILIKE 'Lennon%';
endswith
1
Entry.objects.filter(headline__endswith='Lennon')
1
SELECT ... WHERE headline LIKE '%Lennon';
iendswith
1
Entry.objects.filter(headline__iendswith='Lennon')
1
SELECT ... WHERE headline LIKE '%Lennon';
range
1
2
3
4
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))
1
SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';
date

对于datetime字段,将值强制转换为date。允许链接其他字段查找。获取日期值。

1
2
Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

注:当USE_TZ为真时,字段将在过滤前转换为当前时区。这需要在数据库中定义时区。

year
1
2
Entry.objects.filter(pub_date__year=2005)
Entry.objects.filter(pub_date__year__gte=2005)
1
2
SELECT ... WHERE pub_date BETWEEN '2005-01-01' AND '2005-12-31';
SELECT ... WHERE pub_date >= '2005-01-01';

注:当USE_TZ为真时,字段将在过滤前转换为当前时区。这需要在数据库中定义时区。

month
1
2
Entry.objects.filter(pub_date__month=12)
Entry.objects.filter(pub_date__month__gte=6)
1
2
SELECT ... WHERE EXTRACT('month' FROM pub_date) = '12';
SELECT ... WHERE EXTRACT('month' FROM pub_date) >= '6';

注:当USE_TZ为真时,字段将在过滤前转换为当前时区。这需要在数据库中定义时区

day
1
2
Entry.objects.filter(pub_date__day=3)
Entry.objects.filter(pub_date__day__gte=3)
1
2
SELECT ... WHERE EXTRACT('day' FROM pub_date) = '3';
SELECT ... WHERE EXTRACT('day' FROM pub_date) >= '3';

注:当USE_TZ为真时,字段将在过滤前转换为当前时区。这需要在数据库中定义时区

week

对于date和datetime字段,根据ISO-8601返回周数(1-52或53),即。在美国,一周从周一开始,第一周包含全年的第一个周四。

1
2
Entry.objects.filter(pub_date__week=52)
Entry.objects.filter(pub_date__week__gte=32, pub_date__week__lte=38)

注:当USE_TZ为真时,字段将在过滤前转换为当前时区。这需要在数据库中定义时区

week_day

对于日期和日期时间字段,“星期几”匹配。允许链接其他字段查找。
获取一个整数值,表示一周的日期从1(星期日)到7(星期六)。

1
2
Entry.objects.filter(pub_date__week_day=2)
Entry.objects.filter(pub_date__week_day__gte=2)
quarter

获取一个1到4之间的整数值,表示一年中的季度。
检索第二季度(4月1日至6月30日)条目的示例:

1
Entry.objects.filter(pub_date__quarter=2)
time
1
2
Entry.objects.filter(pub_date__time=datetime.time(14, 30))
Entry.objects.filter(pub_date__time__range=(datetime.time(8), datetime.time(17)))
hour

取0到23之间的整数。

1
2
3
Event.objects.filter(timestamp__hour=23)
Event.objects.filter(time__hour=5)
Event.objects.filter(timestamp__hour__gte=12)
1
2
3
SELECT ... WHERE EXTRACT('hour' FROM timestamp) = '23';
SELECT ... WHERE EXTRACT('hour' FROM time) = '5';
SELECT ... WHERE EXTRACT('hour' FROM timestamp) >= '12';
minute

取0到之间59的整数。

1
2
3
Event.objects.filter(timestamp__minute=29)
Event.objects.filter(time__minute=46)
Event.objects.filter(timestamp__minute__gte=29)
1
2
3
SELECT ... WHERE EXTRACT('minute' FROM timestamp) = '29';
SELECT ... WHERE EXTRACT('minute' FROM time) = '46';
SELECT ... WHERE EXTRACT('minute' FROM timestamp) >= '29';
second

取0到之间59的整数。

1
2
3
Event.objects.filter(timestamp__second=31)
Event.objects.filter(time__second=2)
Event.objects.filter(timestamp__second__gte=31)
1
2
3
SELECT ... WHERE EXTRACT('second' FROM timestamp) = '31';
SELECT ... WHERE EXTRACT('second' FROM time) = '2';
SELECT ... WHERE EXTRACT('second' FROM timestamp) >= '31';
isnull

取True或False,分别对应于IS NULL和IS NOT NULL的SQL查询。

1
Entry.objects.filter(pub_date__isnull=True)
1
SELECT ... WHERE pub_date IS NULL;
regex
1
Entry.objects.get(title__regex=r'^(An?|The) +')
1
2
3
4
SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL
SELECT ... WHERE REGEXP_LIKE(title, '^(An?|The) +', 'c'); -- Oracle
SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL
SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite
iregex
1
Entry.objects.get(title__iregex=r'^(an?|the) +')
1
2
3
4
SELECT ... WHERE title REGEXP '^(an?|the) +'; -- MySQL
SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'i'); -- Oracle
SELECT ... WHERE title ~* '^(an?|the) +'; -- PostgreSQL
SELECT ... WHERE title REGEXP '(?i)^(an?|the) +'; -- SQLite

检索对象

要从数据库检索对象,要通过模型类的 Manager 构建一个 QuerySet。

一个 QuerySet 代表来自数据库中对象的一个集合。它可以有 0 个,1 个或者多个 filters. Filters,可以根据给定参数缩小查询结果量。在 SQL 的层面上, QuerySet 对应 SELECT 语句,而filters对应类似 WHERELIMIT 的限制子句。

你能通过模型的 Manager 获取 QuerySet。每个模型至少有一个 Manager,默认名称是 objects。像这样直接通过模型类使用它:

1
2
3
4
5
6
7
8
>>> Blog.objects
<django.db.models.manager.Manager object at ...>
>>> b = Blog(name='Foo', tagline='Bar')
>>> b.objects
Traceback:
...
AttributeError: "Manager isn't accessible via Blog instances."
# Managers 只能通过模型类访问,而不是通过模型实例,目的是强制分离 “表级” 操作和 “行级” 操作。

检索全部对象.

1
>>> all_entries = Entry.objects.all()

通过过滤器检索指定对象.

filter(\kwargs)** :返回一个新的 QuerySet,包含的对象满足给定查询参数exclude(\kwargs)**: 返回一个新的 QuerySet,包含的对象 不 满足给定查询参数。

例如,要包含获取 2006 年的博客条目(entries blog)的 QuerySet,像这样使用 filter():

1
Entry.objects.filter(pub_date__year=2006)

get() 检索单个对象

Manager 上使用 get() 方法,它会直接返回这个对象:

1
>>> one_entry = Entry.objects.get(pk=1)

如果没有满足查询条件的结果, get() 会抛出一个 DoesNotExist 异常。

有不止一个记录满足 get() 查询条件时发出警告。这时会抛出MultipleObjectsReturned

QuerySet API

class QuerySet(model=None,query=None,using=None,hints=None)**[源代码]

filter(**kwargs)
exclude(**kwargs)
annotate(args, *kwargs)
1
2
3
4
5
6
7
8
>>> from django.db.models import Count
>>> q = Blog.objects.annotate(Count('entry'))
# The name of the first blog
>>> q[0].name
'Blogasaurus'
# The number of entries on the first blog
>>> q[0].entry__count
42
1
2
3
4
>>> q = Blog.objects.annotate(number_of_entries=Count('entry'))
# The number of entries on the first blog, using the name provided
>>> q[0].number_of_entries
42
order_by(*fields)

默认情况下,查询集返回的结果按照模型元数据中的排序选项给出的排序元组进行排序。您可以使用order_by方法在每个查询集的基础上覆盖它。

1
2
3
4
5
Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')

# + 升序排序
# - 降序排序
# ? 随机排序
reverse()
distinct(*fields)

默认情况下,查询集不会消除重复行。实际上,这很少出现问题,因为像blog .object .all()这样的简单查询没有引入重复结果行的可能性。但是,如果查询跨越多个表,那么在计算一个查询集时可能会得到重复的结果。这时就要使用distinct()了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> Author.objects.distinct()
[...]

>>> Entry.objects.order_by('pub_date').distinct('pub_date')
[...]

>>> Entry.objects.order_by('blog').distinct('blog')
[...]

>>> Entry.objects.order_by('author', 'pub_date').distinct('author', 'pub_date')
[...]

>>> Entry.objects.order_by('blog__name', 'mod_date').distinct('blog__name', 'mod_date')
[...]

>>> Entry.objects.order_by('author', 'pub_date').distinct('author')
[...]
values(fields, *expressions)

当用作可迭代时,返回一个查询集,该查询集返回字典,而不是模型实例。

每个字典都表示一个对象,其键与模型对象的属性名相对应。例:

1
2
3
4
5
6
7
# This list contains a Blog object.
>>> Blog.objects.filter(name__startswith='Beatles')
<QuerySet [<Blog: Beatles Blog>]>

# This list contains a dictionary.
>>> Blog.objects.filter(name__startswith='Beatles').values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>

values()方法接受可选的位置参数*fields,它指定SELECT应该被限制的字段名。如果指定字段,每个字典将只包含指定字段的字段键/值。如果不指定字段,每个字典将包含数据库表中每个字段的键和值。

1
2
3
4
>>> Blog.objects.values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
>>> Blog.objects.values('id', 'name')
<QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>

values()方法也接受可选的关键字参数

1
2
3
>>> from django.db.models.functions import Lower
>>> Blog.objects.values(lower_name=Lower('name'))
<QuerySet [{'lower_name': 'beatles blog'}]>

也可以使用内置和自定义查找。例如:

1
2
3
4
5
>>> from django.db.models import CharField
>>> from django.db.models.functions import Lower
>>> CharField.register_lookup(Lower)
>>> Blog.objects.values('name__lower')
<QuerySet [{'name__lower': 'beatles blog'}]>
values_list(*fields, flat=False, named=False)

这类似于values(),不同的是,它在遍历时返回元组,而不是返回字典。每个元组包含传入values_list()调用的各自字段或表达式的值—因此第一项是第一个字段,以此类推。

1
2
3
4
5
>>> Entry.objects.values_list('id', 'headline')
<QuerySet [(1, 'First entry'), ...]>
>>> from django.db.models.functions import Lower
>>> Entry.objects.values_list('id', Lower('headline'))
<QuerySet [(1, 'first entry'), ...]>

如果只传入单个字段,还可以传入平面参数。如果为真,则意味着返回的结果是单个值,而不是一个元组。

1
2
3
4
5
>>> Entry.objects.values_list('id').order_by('id')
<QuerySet[(1,), (2,), (3,), ...]>

>>> Entry.objects.values_list('id', flat=True).order_by('id')
<QuerySet [1, 2, 3, ...]>

您可以传递named=True来获得namedtuple()的结果

1
2
>>> Entry.objects.values_list('id', 'headline', named=True)
<QuerySet [Row(id=1, headline='First entry'), ...]>

常见的需求是获取某个模型实例的特定字段值。为此,使用values_list(),然后调用get():

1
2
>>> Entry.objects.values_list('headline', flat=True).get(pk=1)
'First entry'
dates(field, kind, order=’ASC’)

field:模型的日期字段的名称。

kind: “year”、“month”、“week”或者“day”。

order: 默认为“ASC”,应该是“ASC”或“DESC”。指定了如何对结果排序。

1
2
3
4
5
6
7
8
9
10
11
12
>>> Entry.objects.dates('pub_date', 'year')
[datetime.date(2005, 1, 1)]
>>> Entry.objects.dates('pub_date', 'month')
[datetime.date(2005, 2, 1), datetime.date(2005, 3, 1)]
>>> Entry.objects.dates('pub_date', 'week')
[datetime.date(2005, 2, 14), datetime.date(2005, 3, 14)]
>>> Entry.objects.dates('pub_date', 'day')
[datetime.date(2005, 2, 20), datetime.date(2005, 3, 20)]
>>> Entry.objects.dates('pub_date', 'day', order='DESC')
[datetime.date(2005, 3, 20), datetime.date(2005, 2, 20)]
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
[datetime.date(2005, 3, 20)]
datetimes(field_name, kind, order=’ASC’, tzinfo=None)

field_name: 应该是模型的DateTimeField的名称。

kind: “year”, “month”, “week”, “day”, “hour”, “minute”, 或 “second”。

order: 默认为“ASC”,应该是“ASC”或“DESC”。指定了如何对结果排序。

tzinfo: 将日期时间转换到的相应的时区时间。

none()

调用none()将创建一个从不返回任何对象的查询集,并且在访问结果时不会执行任何查询。none()查询集是EmptyQuerySet的一个实例。

1
2
3
4
5
>>> Entry.objects.none()
<QuerySet []>
>>> from django.db.models.query import EmptyQuerySet
>>> isinstance(Entry.objects.none(), EmptyQuerySet)
True
all()
intersection(*other_qs)
difference(*other_qs)
union(*other_qs, all=False)

默认情况下,UNION操作符只选择不同的值。若要允许重复值,请使用all=True参数。

多个结果集合并时,其它操作只能使用第一个结果集中的字段进行相关操作。

1
2
3
>>> qs1 = Author.objects.values_list('name')
>>> qs2 = Entry.objects.values_list('headline')
>>> qs1.union(qs2).order_by('name')

此外,在生成的查询集中只允许LIMITOFFSETCOUNT(*)ORDER BY和指定列(即切片、COUNT()、order_by()和values()/values_list())。此外,数据库对组合查询中允许的操作进行了限制。例如,大多数数据库在组合查询中不允许限制或偏移。

extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)

有时候,Django查询语法本身不能轻松地表达复杂的WHERE子句。对于这些边缘情况,Django提供了extra() QuerySet修饰符—用于将特定子句注入到查询集生成的SQL中的钩子。

defer(*fields)

延迟加载给定字段

1
Entry.objects.defer("headline", "body")

延迟字段的查询集仍将返回模型实例。如果您访问该字段,将从数据库检索每个延迟字段(一次一个,而不是一次访问所有延迟字段)。

您可以多次调用defer()。每次调用都会向延迟集添加新的字段:

1
2
# Defers both the body and headline fields.
Entry.objects.defer("body").filter(rating=5).defer("headline")

在使用 select_related() 方法时,可使用标准的双下划线符号来分隔相关的字段来延迟相关模型中的字段的加载:

1
Blog.objects.select_related().defer("entry__headline", "entry__body")

如果您想要清除延迟字段集,可以将None作为参数传递给defer():

1
2
# Load all fields immediately.
my_queryset.defer(None)
only(*fields)

如果您的模型中几乎所有字段都需要延迟,那么使用only()指定字段的补充集可以得到更简单的代码。

假设您有一个包含nameagebiography字段的模型。以下两个查询集在延迟字段方面是相同的:

1
2
Person.objects.defer("age", "biography")
Person.objects.only("name")

由于defer()的行为是递增的(向延迟列表中添加字段),所以您可以组合调用only()和defer(),这样事情的行为就会符合逻辑:

1
2
3
4
5
6
# Final result is that everything except "headline" is deferred.
Entry.objects.only("headline", "body").defer("body")

# Final result loads headline and body immediately (only() replaces any
# existing set of fields).
Entry.objects.defer("body").only("headline", "body")
using(alias)
1
2
3
4
5
# queries the database with the 'default' alias.
>>> Entry.objects.all()

# queries the database with the 'backup' alias
>>> Entry.objects.using('backup')
select_for_update(nowait=False,skip_locked=False,of=())

返回一个queryset,它将锁定行,直到事务结束,生成一个SELECT…用于更新受支持数据库上的SQL语句。

1
2
3
4
5
6
from django.db import transaction

entries = Entry.objects.select_for_update().filter(author=request.user)
with transaction.atomic():
for entry in entries:
...
raw(raw_query, params=None, translations=None)

将查询字段映射为模型字段.

raw() 字段将查询语句中的字段映射至模型中的字段。

查询语句中的字段排序并不重要。换而言之,以下两种查询是一致的:

1
2
3
4
>>> Person.objects.raw('SELECT id, first_name, last_name, birth_date FROM myapp_person')
...
>>> Person.objects.raw('SELECT last_name, birth_date, first_name, id FROM myapp_person')
...

匹配是根据名字来的。这意味着你可以使用 SQL 的 AS 子句将查询语句中的字段映射至模型中的字段。所以,若你还有一些数据表包含了 Person 数据,你可以很方便的将其映射至 Person 实例:

1
2
3
4
5
>>> Person.objects.raw('''SELECT first AS first_name,
... last AS last_name,
... bd AS birth_date,
... pk AS id,
... FROM some_other_table''')

raw()translations 参数将查询语句中的字段映射至模型中的字段。这是一个字典,将查询语句中的字段名映射至模型中的字段名。例如,上面的查询也能这样写:

1
2
>>> name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
>>> Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)

索引查询.

raw() 支持索引,所以,若你只需要第一个结果就这样写:

1
>>> first_person = Person.objects.raw('SELECT * FROM myapp_person')[0]

不过,索引和切片不是在数据库层面上实现的。若数据库中有非常多的 Person 对象,更搞笑的方式是在 SQL 层面使用 limit 子句:

1
>>> first_person = Person.objects.raw('SELECT * FROM myapp_person LIMIT 1')[0]

延迟模型字段.

1
>>> people = Person.objects.raw('SELECT id, first_name FROM myapp_person')
1
2
3
4
5
6
>>> for p in Person.objects.raw('SELECT id, first_name FROM myapp_person'):
... print(p.first_name, # This will be retrieved by the original query
... p.last_name) # This will be retrieved on demand
...
John Smith
Jane Jones

将参数传给 raw()

如果你需要执行参数化的查询,可以使用 raw()params 参数:

1
2
>>> lname = 'Doe'
>>> Person.objects.raw('SELECT * FROM myapp_person WHERE last_name = %s', [lname])

params 是一个参数字典。你将用一个列表替换查询字符串中 %s 占位符,或用字典替换 %(key)s 占位符(其中, key 理所应当由字典 key 替换),不论你使用哪个数据库引擎。这些占位符会被 params 参数的值替换。

AND (&)
1
2
3
4
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))
1
SELECT ... WHERE x=1 AND y=2
or(|)
1
2
3
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))
1
SELECT ... WHERE x=1 OR y=2

不返回QuerySets的方法

get(**kwargs)
1
2
3
4
5
6
from django.core.exceptions import ObjectDoesNotExist
try:
e = Entry.objects.get(id=3)
b = Blog.objects.get(id=1)
except ObjectDoesNotExist:
print("Either the entry or blog doesn't exist.")
creteate(**kwargs)
1
2
3
4
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
or
p = Person(first_name="Bruce", last_name="Springsteen")
p.save(force_insert=True)
get_or_create(defaults=None,** kwargs)

防止在并行请求时创建重复对象,并作为boilerplatish代码的快捷方式。例如:

1
2
3
4
5
try:
obj = Person.objects.get(first_name='John', last_name='Lennon')
except Person.DoesNotExist:
obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
obj.save()

对于并发请求,可能会多次尝试保存具有相同参数的人员。为了避免这种竞争条件,上面的例子可以使用get_or_create()重写,如下所示:

1
2
3
4
5
6
7
obj, created = Person.objects.get_or_create(
first_name='John',
last_name='Lennon',
defaults={'birthday': date(1940, 10, 9)},
)

# 返回元组(object, created),其中object是检索或创建的对象,created是指定是否创建新对象的布尔值。
update_or_create(defaults=None, **kwargs)
1
2
3
4
5
6
7
8
9
10
11
defaults = {'first_name': 'Bob'}
try:
obj = Person.objects.get(first_name='John', last_name='Lennon')
for key, value in defaults.items():
setattr(obj, key, value)
obj.save()
except Person.DoesNotExist:
new_values = {'first_name': 'John', 'last_name': 'Lennon'}
new_values.update(defaults)
obj = Person(**new_values)
obj.save()
1
2
3
4
obj, created = Person.objects.update_or_create(
first_name='John', last_name='Lennon',
defaults={'first_name': 'Bob'},
)
bulk_create(objs, batch_size=None, ignore_conflicts=False)

将提供的对象列表高效地插入到数据库中(通常只有一个查询,无论有多少对象):

1
2
3
4
5
6
7
8
9
from itertools import islice

batch_size = 100
objs = (Entry(headline='Test %s' % i) for i in range(1000))
while True:
batch = list(islice(objs, batch_size))
if not batch:
break
Entry.objects.bulk_create(batch, batch_size)
bulk_update(objs, fields, batch_size=None)
1
2
3
4
5
6
7
>>> objs = [
... Entry.objects.create(headline='Entry 1'),
... Entry.objects.create(headline='Entry 2'),
... ]
>>> objs[0].headline = 'This is entry 1'
>>> objs[1].headline = 'This is entry 2'
>>> Entry.objects.bulk_update(objs, ['headline'])
count()
1
2
3
4
5
# Returns the total number of entries in the database.
Entry.objects.count()

# Returns the number of entries whose headline contains 'Lennon'
Entry.objects.filter(headline__contains='Lennon').count()
in_bulk(id_list=None, field_name=’pk’)
1
2
3
4
5
6
7
8
9
10
>>> Blog.objects.in_bulk([1])
{1: <Blog: Beatles Blog>}
>>> Blog.objects.in_bulk([1, 2])
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>}
>>> Blog.objects.in_bulk([])
{}
>>> Blog.objects.in_bulk()
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>, 3: <Blog: Django Weblog>}
>>> Blog.objects.in_bulk(['beatles_blog'], field_name='slug')
{'beatles_blog': <Blog: Beatles Blog>}
iterator(chunk_size=2000)
latest(*fields)

根据给定字段返回表中的最新对象。

1
Entry.objects.latest('pub_date')
1
Entry.objects.latest('pub_date', '-expire_date')
earliest(*fields)
first()
1
p = Article.objects.order_by('title', 'pub_date').first()

等价于

1
2
3
4
try:
p = Article.objects.order_by('title', 'pub_date')[0]
except IndexError:
p = None
last()
aggregate(args, *kwargs)
1
2
3
>>> from django.db.models import Count
>>> q = Blog.objects.aggregate(Count('entry'))
{'entry__count': 16}
1
2
>>> q = Blog.objects.aggregate(number_of_entries=Count('entry'))
{'number_of_entries': 16}
exists()
1
2
3
entry = Entry.objects.get(pk=123)
if some_queryset.filter(pk=entry.pk).exists():
print("Entry contained in queryset")

快于

1
2
if entry in some_queryset:
print("Entry contained in QuerySet")
update(**kwargs)
1
Entry.objects.filter(id=10).update(comments_on=False)

快于

1
2
3
e = Entry.objects.get(id=10)
e.comments_on = False
e.save()

update()未调用save()方法,若重写了save()方法,慎用之。

delete()
as_manager()
explain(format=None, **options)

返回查询集执行计划的字符串,其中详细说明数据库将如何执行查询,包括将使用的任何索引或连接。了解这些细节可以帮助您提高慢速查询的性能。

1
2
3
>>> print(Blog.objects.filter(title='My Blog').explain())
Seq Scan on blog (cost=0.00..35.50 rows=10 width=12)
Filter: (title = 'My Blog'::bpchar)

F表达式

  • 更新字段的值

示例1:

1
2
3
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed += 1
reporter.save()

示例2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from django.db.models import F

reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed = F('stories_filed') + 1
reporter.save()

# 若要重新访问reporter.stories_filed的值,需重新加载数据
# 方式一
reporter = Reporters.objects.get(name='Tintin')
print(reporter.stories_filed)

# 方式二
reporter.refresh_from_db()
print(reporter.stories_filed)

注:示例2的操作快于示例1,一个F()对象代表一个模型字段的值或注释列。使用它可以直接引用模型字段的值并执行数据库操作而不用把它们导入到python的内存中。Django使用F()对象生成一个描述数据库级别所需操作的SQL表达式。

  • F()函数结合update()方法使用
1
2
3
4
5
6
from django.db.models import F

reporter = Reporters.objects.filter(name='Tintin')
reporter.update(stories_field=F('stories_field') + 1)

# Reporters.objects.all().update(stories_field=F('stories_field') + 1)
  • 在过滤器(filter)中使用F()函数

要怎么做才能将模型字段值与同一模型中的另一字段做比较呢?

Django 提供了 F 表达式 可实现将模型字段值与同一模型中的另一字段做比较。 F() 的实例充当查询中的模型字段的引用。这些引用可在查询过滤器中用于在同一模型实例中比较两个不同的字段。

例,要查出所有评论数大于 pingbacks 的博客条目,我们构建了一个 F() 对象,指代 pingback 的数量,然后在查询中使用该 F() 对象:

1
2
>>> from django.db.models import F
>>> Entry.objects.filter(number_of_commets__gt=F("number_of_pingbacks"))

Django支持F()对象使用加、减、乘、除、取模和幂运算等算术操作,两个操作数可以是常数或F()对象

1
2
models.Test.objects.filter(input_price__gt=F("output_price")*2)
models.Test.objects.filter(input_price__gt=F("output_price")+F("output_price"))

Django 支持对 F() 对象进行加、减、乘、除、求余和次方,另一操作数既可以是常量,也可以是其它 F() 对象。要找到那些评论数两倍于 pingbacks 的博客条目,我们这样修改查询条件:

1
2
>>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks') * 2)
>>> Entry.objects.filter(rating__lt=F('number_of_comments') + F('number_of_pingbacks'))

也能用双下划线在 F() 对象中通过关联关系查询。带有双下划线的 F() 对象将引入访问关联对象所需的任何连接。例如,要检索出所有作者名与博客名相同的博客,这样修改查询条件:

1
>>> Entry.objects.filter(authors__name=F('blog__name'))

对于 date 和 date/time 字段,你可以加上或减去一个 timedelta 对象。以下会返回所有发布 3 天后被修改的条目:

1
2
>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))

F() 对象通过 .bitand(), .bitor(), .bitrightshift() 和 .bitleftshift() 支持位操作。例子:

1
>>> F('somefield').bitand(16)
  • annotate中使用 F()

F()可以使用算术运算组合不同字段在模型中创建动态字段。

1
company = Company.objects.annotate(chairs_needed=F('num_employees') - F('num_chairs'))

如果组合的是不同类型的字段,你需要告诉Django返回值是哪种字段类型。由于F()不直接支持output_field,所以需要使用ExpressionWrapper来封装表达式:

1
2
3
4
5
6
7
from django.db.models import DatetimeField, ExpressionWrapper, F

Ticket.objects.annotate(
expires=ExpressionWrapper(
F('active_at') + F('duration'), output_field=DateTimeField()
)
)

当引用关联字段(如ForeignKey)时,F()返回的主键值而不是一个模型实例:

1
2
3
4
5
>>> car = Company.objects.annotate(built_by=F('manufacturrer'))[0]
>>> car.manufacturer
<Manufacturer: Toyota>
>>> car.built_by
3
  • 使用F()对null值排序

使用F()和传递nulls_first或nulls_last参数给Expression.asc()或desc()来控制字段的null值的排序。默认情况下这个排序取决于你的数据库。
例如,要将未联系过(last_contacted为null)的公司排在已联系过的公司后面:

1
2
from django.db.models import F
Company.object.order_by(F('last_contacted').desc(nulls_last=True))
  • F()函数可以通过以下方式提供性能优势:

    • 直接在数据库中操作而不是python
    • 减少一些操作所需的数据库查询次数

Q表达式

主键 (pk) 查询快捷方式

出于方便的目的,Django 提供了一种 pk 查询快捷方式, pk 表示主键 “primary key”。

示例 Blog 模型中,主键是 id 字段,所以这 3 个语句是等效的:

1
2
3
>>> Blog.objects.get(id_exact=1)
>>> Blog.objects.get(id=1)
>>> Blog.objects.get(pk=1)

pk的使用并不仅限于 __exact 查询——任何的查询项都能接在 pk 后面,执行对模型主键的查询:

1
2
3
4
5
# Get blogs entries with id 1, 4 and 7
>>> Blog.objects.filter(pk__in=[1,4,7])

# Get all blog entries with id > 14
>>> Blog.objects.filter(pk__gt=14)

pk 查找也支持跨连接。例如,以下 3 个语句是等效的:

1
2
3
>>> Entry.objects.filter(blog__id__exact=3) # __exact is implied
>>> Entry.objects.filter(blog__id=3) # Explicit form
>>> Entry.objects.filter(blog__pk=3) # __pk implies __id__exact

在 LIKE 语句中转义百分号和下划线

等效于 LIKE SQL 语句的字段查询子句 (iexact, contains, icontains, startswith, istartswith, endswith 和 iendswith) 会将 LIKE 语句中有特殊用途的两个符号,即百分号和下划线自动转义。(在 LIKE 语句中,百分号匹配多个任意字符,而下划线匹配一个任意字符。)

这意味着事情都是表里如一的,不会出现抽象事故。例如,要检索所有包含百分号的条目,就像对待其它字符一样使用百分号:

1
>>> Entry.objects.filter(headline__contains='%') # SELECT ... WHERE headline LIKE '%\%%';

缓存和 QuerySet

每个 QuerySet 都带有缓存,尽量减少数据库访问。理解它是如何工作的能让你编写更高效的代码。
新创建的 QuerySet 缓存是空的。一旦要计算 QuerySet 的值,就会执行数据查询,随后,Django 就会将查询结果保存在 QuerySet 的缓存中,并返回这些显式请求的缓存(例如,下一个元素,若 QuerySet 正在被迭代)。后续针对 QuerySet 的计算会复用缓存结果。
牢记这种缓存行为,在你错误使用 QuerySet 时可能会被它咬一下。例如,以下会创建两个 QuerySet,计算它们,丢掉它们:

1
2
>>> print([e.headline for e in Entry.objects.all()])
>>> print([e.pub_date for e in Entry.objects.all()])

查询结果集并不总是缓存结果。当仅计算查询结果集的 部分 时,会校验缓存,若没有填充缓存,则后续查询返回的项目不会被缓存。特别地说,这意味着使用数组切片或索引的 限制查询结果集 不会填充缓存。
例如,重复的从某个查询结果集对象中取指定索引的对象会每次都查询数据库:

1
2
3
>>> queryset = Entry.objects.all()
>>> print(queryset[5]) # Queries the database
>>> print(queryset[5]) # Queries the database again

不过,若全部查询结果集已被检出,就会去检查缓存:

1
2
3
4
>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # Queries the database
>>> print(queryset[5]) # Uses cache
>>> print(queryset[5]) # Uses cache

这些动作会触发计算全部的查询结果集,并填充缓存的过程:

1
2
3
4
>>> [entry for entry in queryset]
>>> bool(queryset)
>>> entry in queryset
>>> list(queryset)

参考

jango中Q查询及Q()对象 F查询及F()对象用法
F() filter使用

model字段别名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from django.db.models import Q, F

from costasks.models import DataAddress, CosTasks

queryset = DataAddress.objects.filter(source_type=source)

# data: <QuerySet [{'id': UUID('f5a86b3a-5170-448d-b19f-20e9a64dfc0d'), 'address_name': 'sdfeageaa'}]>
data = queryset.annotate(address_name=F("dataaddress_name")).values("id", "address_name")

# 方式二
# data = queryset.values("id", address_name=F("dataaddress_name"))

# 上述查询语句等效于:
# SELECT `costasks_dataaddress`.`id`, `costasks_dataaddress`.`dataaddress_name` AS `address_name` FROM `costasks_dataaddress` ORDER BY `costasks_dataaddress`.`create_time` DESC'

# 查看SQL语句
data.query.__str__()

django命令

makemigrations

  • 第一次执行该操作,会在app下面创建migrations目录,并创建0001_inital.py文件,文件中记录了当前的建表、依赖等信息。

  • 下一次执行该操作,如果 model.py 有改动,会在migrations下生成以修改内容为名,类似0002_alter_permission_name_max_length.py的文件,文件中记录了你修改字段等信息,如果没有改动则提示:No changes detected

注:这些改动在此时都没有迁移到数据库。

migrate

  • migrate命令执行时Django会做4件事:
  1. 迁移判定,将你的项目中所有未迁移的变动文件进行迁移(django会去查询django_migrations表判断你是否有新的迁移变动),在完成接下来的迁移后,便会在该表中生成一条迁移记录。
  2. 迁移映射关系 django_contenttype表新增映射关系(app与模型关系)
  3. 迁移权限 auth_permission表新增权限
  4. 执行迁移,生成数据表,或变动

migrate –fake 只执行第一步,并生成迁移记录。
migrate –fake-initial 执行前三步,不实际变动数据库
migrate 全部依次执行所有步骤。
注:当django 环境变了时,原其他环境下的项目不能正常运行,需要进行修改。

sqlmigrate

  • 可以查看下migrations 0001会对应于什么样子的SQL命令
1
python manage.py sqlmigrate appname 0020_packappname

showmigrations

  • 查看当前项目所有的app及对应的已经生效的migration文件,[x]表示migrte通过,[]表示未通过
-------------本文结束感谢您的阅读-------------