Django 入门之 ORM 增删改查,F Q 原生sql

添加表记录

普通字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 方式1
publish_obj=Publish(name="人民出版社",city="北京",email="renMin@163.com")
publish_obj.save() # 将数据保存到数据库

# 方式2
# 返回值publish_obj是添加的记录对象
publish_obj=Publish.objects.create(name="人民出版社",city="北京",email="renMin@163.com")

# 方式3
Publish.objects.create(**request.POST.dict())

# 批量插入
objs = [
models.DDD(name='r11'),
models.DDD(name='r22')
]
models.DDD.objects.bulk_create(objs, 10)

外键字段

1
2
3
4
5
6
# 方式1:
publish_obj=Publish.objects.get(nid=1)
Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=665,pageNum=334,publish=publish_obj)

# 方式2:
Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=665,pageNum=334,publish_id=1)

多对多字段

1
2
3
4
5
6
7
8
9
10
11
12
book_obj=Book.objects.create(title="追风筝的人",publishDate="2012-11-12",price=69,pageNum=314,publish_id=1)

author_yuan=Author.objects.create(name="yuan",age=23,authorDetail_id=1)
author_egon=Author.objects.create(name="egon",age=32,authorDetail_id=2)

# 方式1 add多个obj对象
# 将某个特定的 model对象添加到被关联对象集合中。 ======= book_obj.authors.add(*[])
book_obj.authors.add(author_egon,author_yuan)

# 方式2 直接create关联对象
# 创建并保存一个新对象,然后将这个对象加被关联对象的集合中,然后返回这个新对象。
book_obj.authors.create()

解除关系:

1
2
3
4
5
# 将某个特定的对象从被关联对象集合中去除。 ====== book_obj.authors.remove(*[])
book_obj.authors.remove()

# 清空被关联对象集合。
book_obj.authors.clear()

class RelatedManager

“关联管理器”是在一对多或者多对多的关联上下文中使用的管理器。

它存在于下面两种情况:

  1. 外键关系的反向查询,例如publish_obj.book_set
  2. 多对多关联关系,例如book_obj.authors和author_obj.book_set

简单来说就是当 点后面的对象 可能存在多个的时候就可以使用以下的方法

create() 创建一个新的对象,保存对象,并将它添加到关联对象集之中,返回新创建的对象。

1
2
import datetime
models.Author.objects.first().book_set.create(title="番茄物语", publish_date=datetime.date.today())

add() 把指定的model对象添加到关联对象集中。

1
2
3
4
5
6
7
8
9
# 添加对象
author_objs = models.Author.objects.filter(id__lt=3)
models.Book.objects.first().authors.add(*author_objs)

# 添加id
models.Book.objects.first().authors.add(*[1, 2])

注:在上面的例子中,对于ForeignKey关系,e.save()由关联管理器调用,执行更新操作。
然而,在多对多关系中使用add()并不会调用任何 save()方法,而是由QuerySet.bulk_create()创建关系。

set() 更新model对象的关联对象。先清空再添加

1
2
book_obj = models.Book.objects.first()
book_obj.authors.set([2, 3])

remove() 从关联对象集中移除执行的model对象

1
2
book_obj = models.Book.objects.first()
book_obj.authors.remove(3) # 可以是主键值也可以是具体对象

clear() 从关联对象集中移除一切对象。

1
2
book_obj = models.Book.objects.first()
book_obj.authors.clear()

注意:对于ForeignKey对象,clear()和remove()方法仅在null=True时存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# ForeignKey字段 没 设置null=True时,没有clear()和remove()方法,运行会报错:
class Book(models.Model):
title = models.CharField(max_length=32)
publisher = models.ForeignKey(to=Publisher)

models.Publisher.objects.first().book_set.clear()
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'RelatedManager' object has no attribute 'clear'

# 当ForeignKey字段设置null=True时,此时就有clear()和remove()方法:
class Book(models.Model):
name = models.CharField(max_length=32)
publisher = models.ForeignKey(to=Class, null=True)

models.Publisher.objects.first().book_set.clear()

注意:对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。

查询操作

看专业的官网文档,做专业的程序员!

必知必会13条

1
2
3
4
5
6
7
8
9
10
11
12
13
<1> all():                 查询所有结果
<2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象
<3> get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
<4> exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象
<5> values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列
<6> values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
<7> order_by(*field): 对查询结果排序
<8> reverse(): 对查询结果反向排序,请注意reverse()通常只能在具有已定义顺序的QuerySet上调用(在model类的Meta中指定ordering或调用order_by()方法)。
<9> distinct(): 从返回结果中剔除重复纪录(如果你查询跨越多个表,可能在计算QuerySet时得到重复的结果。此时可以使用distinct(),注意只有在PostgreSQL中支持按字段去重。)
<10> count(): 返回数据库中匹配查询(QuerySet)的对象数量。
<11> first(): 返回第一条记录
<12> last(): 返回最后一条记录
<13> exists(): 如果QuerySet包含数据,就返回True,否则返回False

返回QuerySet对象的方法有

  • all()
  • filter()
  • exclude()
  • order_by()
  • reverse()
  • distinct()

特殊的QuerySet

  • values() 返回一个可迭代的字典序列
  • values_list() 返回一个可迭代的元祖序列

返回具体对象的

  • get()
  • first()
  • last()

返回布尔值的方法有:

  • exists()

返回数字的方法有

  • count()

单表查询之神奇的双下划线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1且小于10的值(gt大于,gte大于等于,lt小于,lte小于等于)

models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于112233的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in

models.Tb1.objects.filter(name__contains="ven") # 获取name字段包含"ven"的
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感

models.Tb1.objects.filter(id__range=[1, 3]) # range范围,等价于SQL的bettwen and

# 类似的还有:startswith,istartswith, endswith, iendswith

Entry.objects.get(title__regex=r'^(An?|The) +') # regex正则匹配
Entry.objects.get(title__iregex=r'^(an?|the) +') # iregex正则匹配,不区分大小写

# date字段还可以:
models.Class.objects.filter(first_day__date=datetime.date(2005, 1, 1))
models.Class.objects.filter(first_day__year=2017)
models.Class.objects.filter(first_day__mouse=2)
models.Class.objects.filter(first_day__week=2)
models.Class.objects.filter(first_day__date=2)
models.Class.objects.filter(first_day__hour=2)
models.Class.objects.filter(first_day__minute_gte=2)
models.Class.objects.filter(first_day__second_gte=2)

基于对象的跨表查询

一对多查询(Publish 与 Book),同多对多查询

1
2
3
4
5
6
7
8
9
10
11
# 正向查询(按字段,如:publish):
# 查询nid=1的书籍的出版社所在的城市
book_obj=Book.objects.get(nid=1)
print(book_obj.publish.city) # book_obj.publish 是nid=1的书籍对象关联的出版社对象

# 反向查询(按表名_set,如:book_set):
# 查询 人民出版社出版过的所有书籍
publish=Publish.objects.get(name="人民出版社")
book_list=publish.book_set.all() # 与人民出版社关联的所有书籍对象集合
for book_obj in book_list:
print(book_obj.title)

一对一查询(Author 与 AuthorDetail)

1
2
3
4
5
6
7
8
9
10
# 正向查询(按字段,如:authorDetail):
# 查询egon作者的手机号
author_egon=Author.objects.get(name="egon")
print(author_egon.authorDetail.telephone)

# 反向查询(按表名,如:author):
# 查询所有住址在北京的作者的姓名
authorDetail_list=AuthorDetail.objects.filter(addr="beijing")
for obj in authorDetail_list:
print(obj.author.name)

多对多查询 (Author 与 Book),同一对多查询

1
2
3
4
5
6
7
8
9
10
11
12
13
# 正向查询(按字段,如:authors):
# 金瓶眉所有作者的名字以及手机号
book_obj=Book.objects.filter(title="金瓶眉").first()
authors=book_obj.authors.all()
for author_obj in authors:
print(author_obj.name,author_obj.authorDetail.telephone)

# 反向查询(按表名_set,如:book_set):
# 查询egon出过的所有书籍的名字
author_obj=Author.objects.get(name="egon")
book_list=author_obj.book_set.all() #与egon作者相关的所有书籍
for book_obj in book_list:
print(book_obj.title)

注意:你可以通过在 ForeignKey() 和ManyToManyField的定义中设置 related_name 的值来覆写 FOO_set 的名称。例如,如果 Article model 中做一下更改: publish = ForeignKey(Blog, related_name=’bookList’),那么接下来就会如我们看到这般:

1
2
3
# 查询 人民出版社出版过的所有书籍
publish=Publish.objects.get(name="人民出版社")
book_list=publish.bookList.all() # 与人民出版社关联的所有书籍对象集合

基于双下划线的跨表查询

Django 还提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认 SQL JOIN 联系。要做跨关系查询,就使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的 model 为止。

关键点:正向查询按字段,反向查询按表名小写。通常用在 filter 和 value

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
# 练习1:  查询人民出版社出版过的所有书籍的名字与价格(一对多)
# 正向查询 按字段:publish
queryResult=Book.objects
.filter(publish__name="人民出版社")
.values_list("title","price")
# 反向查询 按表名小写:book
queryResult=Publish.objects
.filter(name="人民出版社")
.values_list("book__title","book__price")


# 练习2: 查询egon出过的所有书籍的名字(多对多)
# 正向查询 按字段:authors:
queryResult=Book.objects
.filter(authors__name="yuan")
.values_list("title")
# 反向查询 按表名小写:book
queryResult=Author.objects
.filter(name="yuan")
.values_list("book__title","book__price")


# 练习3: 查询人民出版社出版过的所有书籍的名字以及作者的姓名
# 正向查询 按字段
queryResult=Book.objects
.filter(publish__name="人民出版社")
.values_list("title","authors__name")
# 反向查询 按表名小写
queryResult=Publish.objects
.filter(name="人民出版社")
.values_list("book__title","book__authors__age","book__authors__name")


# 练习4: 手机号以151开头的作者出版过的所有书籍名称以及出版社名称
queryResult=Book.objects
.filter(authors__authorDetail__telephone__regex="151")
.values_list("title","publish__name")

注意:反向查询时,如果定义了related_name ,则用related_name替换表名,例如: publish = ForeignKey(Blog, related_name=’bookList’):

1
2
3
4
5
# 练习1: 查询人民出版社出版过的所有书籍的名字与价格(一对多)
# 反向查询 不再按表名:book,而是related_name:bookList
queryResult=Publish.objects
.filter(name="人民出版社")
.values_list("bookList__title","bookList__price")

聚合查询和分组查询

聚合 aggregate(*args, **kwargs)

aggregate() 是 QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。

键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。

1
2
3
4
5
6
7
8
9
10
# 用到的内置函数
from django.db.models import Avg, Sum, Max, Min, Count
models.Book.objects.all().aggregate(Avg("price")) # {'price__avg': 13.233333}

# 如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
models.Book.objects.aggregate(average_price=Avg('price')) # {'average_price': 13.233333}

# 如果你希望生成多个聚合,可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以:
models.Book.objects.all().aggregate(Avg("price"), Max("price"), Min("price"))
# {'price__avg': 13.233333, 'price__max': Decimal('19.90'), 'price__min': Decimal('9.90')}

分组 annotate

我们在这里先复习一下SQL语句的分组。假设现在有一张公司职员表:

1
2
3
4
5
6
# 我们使用原生SQL语句,按照部分分组求平均工资:
select dept,AVG(salary) from employee group by dept;

# ORM查询:
from django.db.models import Avg
models.Employee.objects.values("dept").annotate(avg=Avg("salary")).values(dept, "avg")

连表查询的分组:

1
2
3
4
5
6
# SQL查询:
select dept.name,AVG(salary) from employee inner join dept on (employee.dept_id=dept.id) group by dept.id;

# ORM查询:
from django.db.models import Avg
models.Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg")

更多示例:

只要记住,前面的value的字段,就是要分组的字段,默认是id

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
# 示例1:统计每一本书的作者个数
book_list = models.Book.objects.all().annotate(author_num=Count("author"))
for obj in book_list:
print(obj.author_num)
2
1
1

# 示例2:统计出每个出版社买的最便宜的书的价格
publisher_list = models.Publisher.objects.annotate(min_price=Min("book__price"))
for obj in publisher_list:
print(obj.min_price)
9.90
19.90

# 方法二:
models.Book.objects.values("publisher__name").annotate(min_price=Min("price"))
# <QuerySet [{'publisher__name': '沙河出版社', 'min_price': Decimal('9.90')},
{'publisher__name': '人民出版社', 'min_price': Decimal('19.90')}]>


# 示例3:统计不止一个作者的图书
models.Book.objects.annotate(author_num=Count("author")).filter(author_num__gt=1)
# <QuerySet [<Book: 番茄物语>]>

# 示例4:根据一本图书作者数量的多少对查询集 QuerySet进行排序
models.Book.objects.annotate(author_num=Count("author")).order_by("author_num")
# <QuerySet [<Book: 香蕉物语>, <Book: 橘子物语>, <Book: 番茄物语>]>

# 示例5:查询各个作者出的书的总价格
models.Author.objects.annotate(sum_price=Sum("book__price")).values("name", "sum_price")
# <QuerySet [{'name': '小精灵', 'sum_price': Decimal('9.90')},
{'name': '小仙女', 'sum_price': Decimal('29.80')},
{'name': '小魔女', 'sum_price': Decimal('9.90')}]>

F查询和Q查询

F查询

在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?

F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。

1
2
3
4
5
6
# 示例1:查询评论数大于收藏数的书籍
from django.db.models import F
models.Book.objects.filter(commnet_num__gt=F('keep_num')) # keep_num是book的字段

Django 支持 F() 对象之间 以及 F() 对象和常数之间的加减乘除和取模的操作。
models.Book.objects.filter(commnet_num__lt=F('keep_num')*2)

修改操作也可以使用F函数

1
2
# 比如将每一本书的价格提高30元
models.Book.objects.all().update(price=F("price")+30)

引申:如果要修改char字段咋办?如:把所有书名后面加上(第一版)

1
2
3
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.all().update(title=Concat(F("title"), Value("("), Value("第一版"), Value(")")))

Q查询

filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR语句),你可以使用Q对象。

1
2
3
# 示例1:查询作者名是小仙女或小魔女的
from django.db.models import Q
models.Book.objects.filter(Q(authors__name="小仙女")|Q(authors__name="小魔女"))

你可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。

1
2
3
# 示例:查询作者名字是小仙女并且不是2018年出版的书的书名。
models.Book.objects.filter(Q(author__name="小仙女") & ~Q(publish_date__year=2018)).values_list("title")
<QuerySet [('番茄物语',)]>

查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将”AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。

1
2
3
4
# 例如:查询出版年份是2017或2018,书名中带物语的所有书。
models.Book.objects.filter(Q(publish_date__year=2018)|Q(publish_date__year=2017),title__icontains="物语")
# <QuerySet [<Book: 番茄物语>, <Book: 香蕉物语>, <Book: 橘子物语>]>
# | 是或操作,后面的title__icontains是且操作

Q还有一种写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 方式二:
from django.db.models import Q
con = Q()
q1 = Q()
q1.connector = 'OR'
q1.children.append(('id', 1))
q1.children.append(('id__gt', 10))
q1.children.append(('id', 9))
q2 = Q()
q2.connector = 'OR'
q2.children.append(('c1', 1))
q2.children.append(('c1', 10))
q2.children.append(('c1__gt', 9))
con.add(q1, 'AND')
con.add(q2, 'AND')

models.Tb1.objects.filter(con)

修改表记录

注意:

<1> 第二种方式修改不能用get的原因是:update是QuerySet对象的方法,get返回的是一个model对象,它没有update方法,而filter返回的是一个QuerySet对象(filter里面的条件可能有多个条件符合,比如name=’alvin’,可能有两个name=’alvin’的行数据)。

<2>在“插入和更新数据”小节中,我们有提到模型的save()方法,这个方法会更新一行里的所有列。 而某些情况下,我们只需要更新行里的某几列。

此外,update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录update()方法会返回一个整型数值,表示受影响的记录条数。

注意,这里因为update返回的是一个整形,所以没法用query属性;对于每次创建一个对象,想显示对应的raw sql,需要在settings加上日志记录部分

删除表记录

queryset 和 modelobject 都有delete方法,它运行时立即删除对象而不返回任何值。

你也可以一次性删除多个对象。每个 QuerySet 都有一个 delete() 方法,它一次性删除 QuerySet 中所有的对象。

例如,下面的代码将删除 pub_date 是2005年的 Entry 对象:

1
Entry.objects.filter(pub_date__year=2005).delete()

要牢记这一点:无论在什么情况下,QuerySet 中的 delete() 方法都只使用一条 SQL 语句一次性删除所有对象,而并不是分别删除每个对象。如果你想使用在 model 中自定义的 delete() 方法,就要自行调用每个对象的delete 方法。(例如,遍历 QuerySet,在每个对象上调用 delete()方法),而不是使用 QuerySet 中的 delete()方法。

在 Django 删除对象时,会模仿 SQL 约束 ON DELETE CASCADE 的行为,换句话说,删除一个对象时也会删除与它相关联的外键对象。例如:

1
2
3
b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()

要注意的是: delete() 方法是 QuerySet 上的方法,但并不适用于 Manager 本身。这是一种保护机制,是为了避免意外地调用 Entry.objects.delete() 方法导致 所有的 记录被误删除。如果你确认要删除所有的对象,那么你必须显式地调用:

Entry.objects.all().delete()

如果不想级联删除,可以设置为:

1
pubHouse = models.ForeignKey(to='Publisher', on_delete=models.SET_NULL, blank=True, null=True)

锁和事务

select_for_update(nowait=False, skip_locked=False)

返回一个锁住行直到事务结束的查询集,如果数据库支持,它将生成一个 SELECT … FOR UPDATE 语句。

举个例子:

1
entries = Entry.objects.select_for_update().filter(author=request.user)

所有匹配的行将被锁定,直到事务结束。这意味着可以通过锁防止数据被其它事务修改。

一般情况下如果其他事务锁定了相关行,那么本查询将被阻塞,直到锁被释放。 如果这不想要使查询阻塞的话,使用select_for_update(nowait=True)。 如果其它事务持有冲突的锁, 那么查询将引发 DatabaseError 异常。你也可以使用select_for_update(skip_locked=True)忽略锁定的行。 nowait和skip_locked是互斥的,同时设置会导致ValueError。

目前,postgresql,oracle和mysql数据库后端支持select_for_update()。 但是,MySQL不支持nowait和skip_locked参数。

使用不支持这些选项的数据库后端(如MySQL)将nowait=True或skip_locked=True转换为select_for_update()将导致抛出DatabaseError异常,这可以防止代码意外终止。

事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import os

if __name__ == '__main__':
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
import django
django.setup()

import datetime
from app01 import models

try:
from django.db import transaction
with transaction.atomic():
new_publisher = models.Publisher.objects.create(name="火星出版社")
models.Book.objects.create(title="橘子物语", publish_date=datetime.date.today(), publisher_id=10) # 指定一个不存在的出版社id
except Exception as e:
print(str(e))

其他鲜为人知的操作(有个印象即可)

Django ORM执行原生SQL

在模型查询API不够用的情况下,我们还可以使用原始的SQL语句进行查询。Django 提供两种方法使用原始SQL进行查询:

  • 一种是使用raw()方法,进行原始SQL查询并返回模型实例;
  • 另一种是完全避开模型层,直接执行自定义的SQL语句。

执行原生查询

raw()管理器方法用于原始的SQL查询,并返回模型的实例:

注意:raw()语法查询必须包含主键。

这个方法执行原始的SQL查询,并返回一个django.db.models.query.RawQuerySet 实例。 这个RawQuerySet 实例可以像一般的QuerySet那样,通过迭代来提供对象实例。

1
2
3
4
5
6
7
8
class Person(models.Model):
first_name = models.CharField(...)
last_name = models.CharField(...)
birth_date = models.DateField(...)

# 可以像下面这样执行原生SQL语句
for p in Person.objects.raw('SELECT * FROM myapp_person'):
print(p)

raw()查询可以查询其他表的数据。但是查询到的数据要和模型表的定义的字段一一对应

1
2
3
ret = models.Student.objects.raw('select id, tname as hehe from app02_teacher')
for i in ret:
print(i.id, i.hehe)

raw()方法自动将查询字段映射到模型字段。还可以通过translations参数指定一个把查询的字段名和ORM对象实例的字段名互相对应的字典

1
2
3
4
d = {'tname': 'haha'}
ret = models.Student.objects.raw('select * from app02_teacher', translations=d)
for i in ret:
print(i.id, i.sname, i.haha)

原生SQL还可以使用参数,注意不要自己使用字符串格式化拼接SQL语句,防止SQL注入!参数 params

1
2
3
4
d = {'tname': 'haha'}
ret = models.Student.objects.raw('select * from app02_teacher where id > %s', translations=d, params=[1,])
for i in ret:
print(i.id, i.sname, i.haha)

直接执行自定义SQL

有时候raw()方法并不十分好用,很多情况下我们不需要将查询结果映射成模型,或者我们需要执行DELETE、 INSERT以及UPDATE操作。在这些情况下,我们可以直接访问数据库,完全避开模型层。

我们可以直接从django提供的接口中获取数据库连接,然后像使用pymysql模块一样操作数据库。

1
2
3
4
from django.db import connection, connections
cursor = connection.cursor() # cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
ret = cursor.fetchone()

QuerySet方法大全

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
def all(self)
# 获取所有的数据对象

def filter(self, *args, **kwargs)
# 条件查询,过滤,条件可以是:参数,字典,Q

def exclude(self, *args, **kwargs)
# 条件查询,除了,条件可以是:参数,字典,Q

def select_related(self, *fields)
性能相关:表之间进行join连表操作,一次性获取关联的数据。
总结:
1. select_related主要针一对一和多对一关系进行优化。
2. select_related使用SQL的JOIN语句进行优化,通过减少SQL查询的次数来进行优化、提高性能。

def prefetch_related(self, *lookups)
性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。
总结:
1. 对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化。
2. prefetch_related()的优化方式是分别查询每个表,然后用Python处理他们之间的关系。

def annotate(self, *args, **kwargs)
# 用于实现聚合group by查询
from django.db.models import Count, Avg, Max, Min, Sum

v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))
# SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id

v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)
# SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)
# SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

def distinct(self, *field_names)
# 用于distinct去重
models.UserInfo.objects.values('nid').distinct()
# select distinct nid from userinfo

注:只有在PostgreSQL中才能使用distinct进行去重

def order_by(self, *field_names)
# 用于排序
models.UserInfo.objects.all().order_by('-id','age')

def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
# 构造额外的查询条件或者映射,如:子查询

Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

def reverse(self):
# 倒序
models.UserInfo.objects.all().order_by('-nid').reverse()
# 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序


def defer(self, *fields):
models.UserInfo.objects.defer('username','id')

models.UserInfo.objects.filter(...).defer('username','id')
#映射中排除某列数据

def only(self, *fields):
# 仅取某个表中的数据
models.UserInfo.objects.only('username','id')
# 或
models.UserInfo.objects.filter(...).only('username','id')

def using(self, alias):
指定使用的数据库,参数为别名(setting中的设置)


##################################################
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
##################################################
def raw(self, raw_query, params=None, translations=None, using=None):
# 执行原生SQL
models.UserInfo.objects.raw('select * from userinfo')

# 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
models.UserInfo.objects.raw('select id as nid from 其他表')

# 为原生SQL设置参数
models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])

# 将获取的到列名转换为指定列名
name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)

# 指定数据库
models.UserInfo.objects.raw('select * from userinfo', using="default")

################### 原生SQL ###################
from django.db import connection, connections
cursor = connection.cursor() # cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
row = cursor.fetchone() # fetchall()/fetchmany(..)


def values(self, *fields):
# 获取每行数据为字典格式

def values_list(self, *fields, **kwargs):
# 获取每行数据为元祖

def dates(self, field_name, kind, order='ASC'):
# 根据时间进行某一部分进行去重查找并截取指定内容
# kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
# order只能是:"ASC" "DESC"
# 并获取转换后的时间
- year : 年-01-01
- month: 年-月-01
- day : 年-月-日

models.DatePlus.objects.dates('ctime','day','DESC')

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’)
"""

def none(self):
# 空QuerySet对象


####################################
# METHODS THAT DO DATABASE QUERIES #
####################################

def aggregate(self, *args, **kwargs):
# 聚合函数,获取字典类型聚合结果
from django.db.models import Count, Avg, Max, Min, Sum
result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))
===> {'k': 3, 'n': 4}

def count(self):
# 获取个数

def get(self, *args, **kwargs):
# 获取单个对象

def create(self, **kwargs):
# 创建对象

def bulk_create(self, objs, batch_size=None):
# 批量插入
# batch_size表示一次插入的个数
objs = [
models.DDD(name='r11'),
models.DDD(name='r22')
]
models.DDD.objects.bulk_create(objs, 10)

def get_or_create(self, defaults=None, **kwargs):
# 如果存在,则获取,否则,创建
# defaults 指定创建时,其他字段的值
obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})

def update_or_create(self, defaults=None, **kwargs):
# 如果存在,则更新,否则,创建
# defaults 指定创建时或更新时的其他字段
obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})

def first(self):
# 获取第一个

def last(self):
# 获取最后一个

def in_bulk(self, id_list=None):
# 根据主键ID进行查找
id_list = [11,21,31]
models.DDD.objects.in_bulk(id_list)

def delete(self):
# 删除

def update(self, **kwargs):
# 更新

def exists(self):
# 是否有结果

Django终端打印SQL语句

在Django项目的settings.py文件中,在最后复制粘贴如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}

即为你的Django项目配置上一个名为_django.db.backends_的logger实例即可查看翻译后的SQL语句。

在Python脚本中调用Django环境

1
2
3
4
5
6
7
8
9
10
11
import os

if __name__ == '__main__':
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
import django
django.setup()

from app01 import models

books = models.Book.objects.all()
print(books)

外键

没外键的话,没办法联表查询。

Django ORM有硬性规定,不论是已有表做ORM映射还是用ORM创建的表,都必须满足以下条件

外键字段必须以 xxx_id 的格式命名

另外,如表之间确实有关联,请务必使用ForeignKey,请勿道听途说不使用外键。

不使用外键是指数据库层面的外键约束,而Django ORM所使用的是查询引擎的查询逻辑。所以要关闭数据库层面的外键约束,只需要使用db_constraint参数设置为False即可。但建议再按照业务需求,设置上索引和联合索引即可。


Django 入门之 ORM 增删改查,F Q 原生sql
https://flepeng.github.io/021-Python-32-框架-Django-Django-入门之-ORM-增删改查,F-Q-原生sql/
作者
Lepeng
发布于
2021年8月8日
许可协议