Django之Orm的各种操作

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Django之Orm 的各种操作
1.⼀般操作
***必知必会13条***
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> all(): 查询所有结果
<2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象
models.Customer.objects.filter(id=1) >>> <QuerySet [<Customer: Costomer1>]> 不存在返回⼀个空的queryset,不会报错
models.Customer.objects.filter(**{"id":1,..}) 参数也可传字典形式
---也可传Q对象,后⾯Q对象介绍有
<3> get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有⼀个,如果符合筛选条件的对象超过⼀个或者没有都会抛出错误。

models.Customer.objects.get(id=1) >>> <Customer: Costomer1>
<4> exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象
models.Customer.objects.exclude(id=1)
<5> values(*field): 返回⼀个ValueQuerySet——⼀个特殊的QuerySet,运⾏后得到的并不是⼀系列model的实例化对象,⽽是⼀个可迭代的字典序列
models.Customer.objects.values("name","telephone_number")
>>> <QuerySet [{'name': 'Costomer1', 'telephone_number': '182********'}, {'name':
'Costomer2', 'telephone_number': '183********'}]>
<6> values_list(*field): 它与values()⾮常相似,它返回的是⼀个元组序列,values返回的是⼀个字典序列
models.Customer.objects.values_list("name","telephone_number")
>>> <QuerySet [('Costomer1', '182********'), ('Costomer2', '183********')]>
<7> order_by(*field): 对查询结果排序
models.Customer.objects.all().order_by("id")
>>> <QuerySet [<Customer: Costomer1>, <Customer: Costomer2>, <Customer: Costomer3>
<8> reverse(): 对查询结果反向排序,请注意reverse()通常只能在具有已定义顺序的QuerySet上调⽤(在model类的Meta中指定ordering或调⽤order_by()⽅法)。

models.Customer.objects.all().order_by("id").reverse()
>>> <QuerySet [<Customer: Costomer17>, <Customer: Costomer16>, <Customer: Costomer15>
<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()
1234567891011121314models.Tb1.objects.filter (id__lt =10, id__gt =1) # 获取id ⼤于1 且 ⼩于10的值
models.Tb1.objects.filter (id__in =[11, 22, 33]) # 获取id 等于11、22、33的数据
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]) # id 范围是1到3的,等价于SQL 的bettwen and
类似的还有:startswith ,istartswith, endswith, iendswith
date 字段还可以:
models.Class.objects.filter (first_day__year =2017)
2.ForeignKey 操作
正向查找
对象查找(跨表)
语法: 对象.关联字段.字段
⽰例:
123book_obj = models.Book.objects.first() # 第⼀本书对象
print (book_obj.publisher) # 得到这本书关联的出版社对象
print (book_) # 得到出版社对象的名称
字段查找(跨表)
语法: 关联字段__字段
⽰例:
1print (models.Book.objects.values_list("publisher__name"
)) ---"publisher__name" 外键名_字段名
反向操作语法:obj.表名_set
⽰例:
123publisher_obj = models.Publisher.objects.first() # 找到第⼀个出版社对象
books = publisher_obj.book_set.all () # 找到第⼀个出版社出版的所有书
titles = books.values_list("title") # 找到第⼀个出版社出版的所有书的书名
语法: 表名__字段
⽰例:
1titles =models.Publisher.objects.values_list("book__title") ----"book__title":表名_字段名
3.ManyToManyField操作
class RelatedManager
"关联管理器"是在⼀对多或者多对多的关联上下⽂中使⽤的管理器。

它存在于下⾯两种情况:
1. 外键关系的反向查询
2. 多对多关联关系
简单来说就是当点后⾯的对象可能存在多个的时候就可以使⽤以下的⽅法。

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

>>> import datetime
>>> models.Author.objects.first().book_set.create(title="番茄物语", publish_date=datetime.date.today())
add()
把指定的model对象添加到关联对象集中。

添加对象
>>> 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])
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时存在。

举个例⼦:
ForeignKey字段没设置null=True时,
class Book(models.Model):
title = models.CharField(max_length=32)
publisher = models.ForeignKey(to=Publisher)
没有clear()和remove()⽅法:
>>> 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时,
class Book(models.Model):
name = models.CharField(max_length=32)
publisher = models.ForeignKey(to=Class, null=True)
此时就有clear()和remove()⽅法:
>>> models.Publisher.objects.first().book_set.clear()
注意:
1. 对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。

换句话说,在关联的任何⼀端,都不需要再调⽤save()⽅法。

4.聚合查询和分组查询
聚合
aggregate()是QuerySet 的⼀个终⽌⼦句,意思是说,它返回⼀个包含⼀些键值对的字典。

键的名称是聚合值的标识符,值是计算出来的聚合值。

键的名称是按照字段和聚合函数的名称⾃动⽣成出来的。

⽤到的内置函数:
from django.db.models import Avg, Sum, Max, Min, Count
⽰例:
>>> 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')}
分组
我们在这⾥先复习⼀下SQL语句的分组。

假设现在有⼀张公司职员表:
我们使⽤原⽣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 ,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")
⽰例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')}]>
5.F查询和Q查询
F查询
在上⾯所有的例⼦中,我们构造的过滤器都只是将字段值与某个常量做⽐较。

如果我们要对两个字段的值做⽐较,那该怎么做呢?
Django 提供 F() 来做这样的⽐较。

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

⽰例1:
查询评论数⼤于收藏数的书籍
from django.db.models import F
models.Book.objects.filter(commnet_num__gt=F('keep_num'))
Django ⽀持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。

models.Book.objects.filter(commnet_num__lt=F('keep_num')*2)
修改操作也可以使⽤F函数,⽐如将每⼀本书的价格提⾼30元
models.Book.objects.all().update(price=F("price")+30)
引申:
如果要修改char字段咋办?
如:把所有书名后⾯加上(第⼀版)
>>> 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:
查询作者名是⼩仙⼥或⼩魔⼥的
models.Book.objects.filter(Q(authors__name="⼩仙⼥")|Q(authors__name="⼩魔⼥"))
你可以组合& 和| 操作符以及使⽤括号进⾏分组来编写任意复杂的Q 对象。

同时,Q 对象可以使⽤~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。

⽰例:查询作者名字是⼩仙⼥并且不是2018年出版的书的书名。

>>> models.Book.objects.filter(Q(author__name="⼩仙⼥") & ~Q(publish_date__year=2018)).values_list("title")
<QuerySet [('番茄物语',)]>
查询函数可以混合使⽤Q 对象和关键字参数。

所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在⼀起。

但是,如果出现Q 对象,它必须位于所有关键字参数的前⾯。

例如:查询出版年份是2017或2018,书名中带物语的所有书。

>>> models.Book.objects.filter(Q(publish_date__year=2018) | Q(publish_date__year=2017), title__icontains="物语")
<QuerySet [<Book: 番茄物语>, <Book: ⾹蕉物语>, <Book: 橘⼦物语>]>
6.事务
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))
7.其他鲜为⼈知的操作
Django ORM执⾏原⽣SQL
1# extra
2# 在QuerySet的基础上继续执⾏⼦语句
3# extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None) 4
5# select和select_params是⼀组,where和params是⼀组,tables⽤来设置from哪个表
6# Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) 7# Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
8# Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
9# Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid']) 10
11举个例⼦:
erInfo.objects.extra(
13 select={'newid':'select count(1) from app01_usertype where id>%s'},
14 select_params=[1,],
15 where = ['age>%s'],
16 params=[18,],
17 order_by=['-age'],
18 tables=['app01_usertype']
19 )
20"""
21 select
22 app01_userinfo.id,
23 (select count(1) from app01_usertype where id>1) as newid
24 from app01_userinfo,app01_usertype
25 where
26 app01_userinfo.age > 18
27 order by
28 app01_userinfo.age desc
29"""
30
31
32# 执⾏原⽣SQL
33# 更⾼灵活度的⽅式执⾏原⽣SQL语句
34# from django.db import connection, connections
35# cursor = connection.cursor() # cursor = connections['default'].cursor()
36# cursor.execute("""SELECT * from auth_user where id = %s""", [1])
37# row = cursor.fetchone()
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 26def all(self)
# 获取所有的数据对象
def filter(self, *args, **kwargs)
# 条件查询
# 条件可以是:参数,字典,Q
def get_字段_display()
#表存在字段gender=Models.Customer.InteherField(verbosename="性别",choices=[(1="男"),(2,"⼥")] 想直接查询出某条数据的性别:
models.Customer.Objects.all().first().gender ==> "1"
models.Customer.Objects.all().first().get_gender_display() ==>"男"
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()来进⾏优化。

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
2.prefetch_related()的优化⽅式是分别查询每个表,然后⽤Python处理他们之间的关系。

def annotate(self, *args, **kwargs)
# ⽤于实现聚合group by查询
from django.db.models import Count, Avg, Max, Min, Sum
v =erInfo.objects.values('u_id').annotate(uid=Count('u_id'))
# SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id
v =erInfo.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 =
erInfo.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去重
erInfo.objects.values('nid').distinct()
# select distinct nid from userinfo
注:只有在PostgreSQL中才能使⽤distinct进⾏去重
def order_by(self, *field_names)
# ⽤于排序
erInfo.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):
# 倒序
erInfo.objects.all().order_by('-nid').reverse()
# 注:如果存在order_by,reverse则是倒序,如果多个排序则⼀⼀倒序
def defer(self, *fields):
erInfo.objects.defer('username','id')

erInfo.objects.filter(...).defer('username','id')
#映射中排除某列数据
def only(self, *fields):
#仅取某个表中的数据
erInfo.objects.only('username','id')

erInfo.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
erInfo.objects.raw('select * from userinfo')
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
# 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
erInfo.objects.raw('select id as nid from 其他表')
# 为原⽣SQL设置参数
erInfo.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)
# 指定数据库
erInfo.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 =erInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid')) ===> {'k': 3, 'n': 4}
def count(self):
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 196 197 198 199 200 201 202 203 204 205 206 207
# 获取个数
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 =erInfo.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 =erInfo.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):
# 是否有结果
8.Django终端打印SQL语句
在Django项⽬的settings.py⽂件中,在最后复制粘贴如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14LOGGING ={
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
15 16 17
},
} }
即为你的Django项⽬配置上⼀个名为django.db.backends的logger实例即可查看翻译后的SQL语句。

9.在Python脚本中调⽤Django环境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)。

相关文档
最新文档