Django ORM相关操作
在Python脚本中调用Django环境
很多情况下,在做测试的时候需要在Python脚本进行,此时若想成功运行需要导入Django环境配置,否则运行不成功。
import os
if __name__ == '__main__':
# 导入配置环境,记不住就去manage.py中找
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ormday69.settings")
# 启动Django
import django
django.setup()
# 要进行的操作
from app01 import models
.....
Django终端打印SQL语句
在运行相应的操作后会在终端打印相应的SQL语句
在Django项目的settings.py文件中,在最后复制粘贴如下代码:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
一般操作
基本的语法是:models.类名.objects.函数名()
必知必会13个
all()
- 功能:
查询表中的所有结果 - 使用方式:
models.Person.objects.all()
- 返回内容:
QuerySet
get()
- 功能:
返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。 - 使用方式:
models.Person.objects.get(id=1)
- 返回内容:
Object
filter(**kwargs)
-
功能:
返回所给筛选条件相匹配的对象,返回的是个类似于列表一样的QuerySet对象。如果没有找到不会报错,会返回一个空的QuerySet对象,及时只有一个返回值照样返回QuerySet,想要取到某一个值可以使用索引。 -
使用方式:
ret = models.Person.objects.filter() ret = models.Person.objects.filter() ret[0]
-
返回内容:
QuerySet
注意比较get方法和filter方法的不同之处
exclude(**kwargs)
- 功能:
与所给筛选条件不匹配的对象 - 使用方式:
ret = models.Person.objects.exclude(id=1)
- 返回内容:
QuerySet
values(*field)
- 功能:
返回一个可迭代的字典序列,每一个都是 {"字段":值} 的形式
参数为数据表的字段,如果没有字段则返回所有字段 - 使用方式:
ret = models.Person.objects.values() - 返回内容:
特殊的QuerySet
values_list(*field)
- 功能:
它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 - 使用方式:
ret = models.Person.objects.values_list() - 返回内容:
特殊的QuerySet
order_by(*field)
- 功能:
对查询结果排序-
有参数,按照参数字段排序
-
没有参数,类在元数据中设置了默认排序方式
按照元数据的默认排序方式,元数据设置方式如下:class Person(models.Model): name = models.CharField(max_length=32) age = models.IntegerField(default=18) birthday = models.DateField(auto_now_add=True) def __str__(self): return "<Object:Person>{}".format(self.name) class Meta: ordering=("birthday", )
-
没有参数,且没有设置元数据
按照原有顺序输出,不排序
-
- 使用方式:
ret = models.Person.objects.order_by()
- 返回内容:
QuerySet
reverse()
- 功能:
对查询结果反向排序,请注意reverse()通常只能在具有已定义顺序的QuerySet上调用(在model类的Meta中指定ordering或调用order_by()方法)。 - 使用方式:
ret = models.Person.objects.reverse()
- 返回内容:
QuerySet
count()
- 功能:
返回数据库中匹配查询(QuerySet)的对象数量。 - 使用方式:
ret = models.Person.objects.count()
- 返回内容:
数字
first()
- 功能:
返回第一条记录 - 使用方式:
ret = models.Person.objects.first()
- 返回内容:
具体的对象
last()
- 功能:
返回最后一条记录 - 使用方式:
ret = models.Person.objects.last()
- 返回内容:
具体的对象
exists()
- 功能:
如果QuerySet包含数据,就返回True,否则返回False - 使用方式:
ret = models.Person.objects.exists()
- 返回内容:
布尔值
distinct()
-
功能:
从返回结果中剔除重复纪录(如果你查询跨越多个表,可能在计算QuerySet时得到重复的结果。此时可以使用distinct(),注意只有在PostgreSQL中支持按字段去重。) -
使用方式:
-
返回内容:
单表查询之神奇的双下划线
只能用在前面提到的filter函数中使用,方式为:字段+双下划线+=+值
__lt __gt
models.Person.objects.filter(id__gt=2, id__lt=5)
# 获取 id 大于 2 且小于 5 的值
__in
models.Person.objects.filter(id__in=[1, 2, 3])
# 获取 id 等于 11、22、33 的数据
models.Person.objects.exclude(id__in=[1, 2, 3])
# 获取 id 不等于 11、22、33 的数据
__contains
models.Person.objects.filter(name__contains="ban")
# 获取 name 字段包含 "ban" 的
models.Person.objects.filter(name__icontains="ban")
# icontains 对大小写不敏感
__range
models.Person.objects.filter(id__range=(1, 3))
# id 范围是 1 到 3 的,等价于 SQL 的 bettwen and
date字段有:__year, __month, __day
models.Person.objects.filter(birthday__day=6)
# 表示 birthday 字段中日为 6
其他关于字符串的还有
startswith,istartswith, endswith, iendswith
ForeignKey操作
正向查找
-
对象查找(跨表)
语法:对象.关联字段.字段
示例:
book_obj = models.Book.objects.first() # 第一本书对象 print(book_obj.publisher) # 得到这本书关联的出版社对象 print(book_obj.publisher.name) # 得到出版社对象的名称
-
字段查找(跨表)
根据双下划线跨表查询,双下划线就代表跨了一张表
语法:关联字段__字段
示例:
print(models.Book.objects.values_list("publisher__name"))
反向查询
-
对象查找
语法:obj.表名_set
示例:
publisher_obj = models.Publisher.objects.first() # 找到第一个出版社对象 books = publisher_obj.book_set.all() # 找到第一个出版社出版的所有书 titles = books.values_list("title") # 找到第一个出版社出版的所有书的书名
-
字段查找
语法:表名__字段
示例:
titles = models.Publisher.objects.values_list("book__title")
注意:
ManyToManyField
class RelatedManager
"关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。
它存在于下面两种情况:
- 外键关系的反向查询
- 多对多关联关系
简单来说就是当 点后面的对象 可能存在多个的时候就可以使用以下的方法。
方法
create()
创建一个新的对象,保存对象,并将它添加到关联对象集之中,返回新创建的对象。
# step1:在book表中创建了一本新书,step2:在作者和书的关系表中添加关联记录
author_obj.books.create(title="跟金老板学开坦克", publisher_id=2)
add()
把指定的model对象添加到关联对象集中。
添加对象
book_obj = models.Book.objects.get(id=7)
author_obj.books.add(book_obj)
添加对象列表
book_objs = models.Book.objects.filter(id__gt=9)
author_obj.books.add(*book_objs) # 要把列表打散再传进去
添加id
author_obj.books.add(12)
set()
更新model对象的关联对象。
book_obj = models.Book.objects.first()
book_obj.authors.set([2, 3])
remove()
从关联对象集中移除执行的model对象
book_obj = models.Book.objects.first()
book_obj.authors.remove(3)
clear()
从关联对象集中移除一切对象。
book_obj = models.Book.objects.first()
book_obj.authors.clear()
注意:
- 对于ForeignKey对象,clear()和remove()方法仅在null=True时存在。
- 对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法
聚合查询和分组查询
聚合
aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。
用到的内置函数:
from django.db.models import Avg, Sum, Max, Min, Count
键的名称是聚合值的标识符,值是计算出来的聚合值。
1. 键的名称是按照字段和聚合函数的名称自动生成出来的。[字段名__函数名]
models.Book.objects.all().aggregate(Avg("price"))
键的名称:price__avg
2. 如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
models.Book.objects.all().aggregate(avg_price=Avg("price"))
键的名称:avg_price
3. 如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。
如果你也想知道所有图书价格的最大值和最小值,可以这样查询:
models.Book.objects.all().aggregate(Avg("price"), Max("price"), Min("price"))
分组
假设现在有一张公司职员表:
按照部分分组求平均工资
- 原生SQL语句
select dept,AVG(salary) from employee group by dept;
- ORM
from django.db.models import Avg Employee.objects.values("dept").annotate(avg=Avg("salary").values(dept, "avg")
连表查询的分组
- 原生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")
更多示例
- 查询每一本书的作者个数
ret = models.Book.objects.all().annotate(author_num=Count("author"))
print(ret.author_num) # ret是列表,不能对其使用.属性
for book in ret:
print("书名:{},作者数量:{}".format(book.title, book.author_num))
- 查询作者数量大于一的书
ret=models.Book.objects.all().annotate(author_num=Count("author")).filter(author_num__gt=1)
print(ret[0].title)
- 查询各个作者出的书的总价格
ret=models.Author.objects.all().annotate(price_num=Sum("books__price")).values_list("name", "price_num")
ret=models.Author.objects.all().annotate(price_num=Sum("books__price")).values("name", "price_num")
print(ret.values_list("id", "name", "price_num"))
print(ret)
for i in ret:
print(i.get("name"), i.get("price_num"))
F查询和Q查询
F查询
Django 提供 F() 对两个字段的值做比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
示例
- 查询库存数大于卖出数的所有书(两个字段作比较)
from django.db.models import F
ret = models.Book.objects.filter(kucun__gt=F("maichu"))
for book in ret:
print(book.title, book.kucun, book.maichu)
- 刷单 把每一本书的卖出数都乘以3
# 单个对象操作用save
# obj = models.Book.objects.first()
# obj.maichu = 100 * 3
# obj.save()
# 具体的对象没有 update(), QuerySet 对象才有 update() 方法
models.Book.objects.update(maichu=(F("maichu")+1)*3)
- 给每一本书的书名后面加上 第一版
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title=Concat(F("title"), Value("第一版")))
Q查询
filter() 等方法中的关键字参数查询都是一起进行 “AND” 的。 如果你需要执行更复杂的查询(例如 OR 语句),你可以使用 Q 对象。
from django.db.models import Q
# 查询 卖出数大于1000,并且 价格小于100的所有书
ret = models.Book.objects.filter(maichu__gt=1000, price__lt=100)
print(ret)
# 查询 卖出数大于1000,或者 价格小于100的所有书
ret = models.Book.objects.filter(Q(maichu__gt=1000) | Q(price__lt=100))
可以组合 & 和 | 操作符以及使用括号进行分组来编写任意复杂的 Q 对象。同时,Q 对象可以使用 ~ 操作符取反,这允许组合正常的查询和取反 (NOT) 查询。
查询函数可以混合使用 Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或 Q 对象)都将 "AND" 在一起。但是,如果出现 Q 对象,它必须位于所有关键字参数的前面。
# Q查询和字段查询同时存在时, 字段查询要放在Q查询的后面
ret = models.Book.objects.filter(Q(maichu__gt=1000) | Q(price__lt=100), title__contains="金老板")
print(ret)
事务
原子性操作: 中间一旦某个环节出了问题,则整个操作都将取消,包括出错环节之前已经执行过的环节。
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))