单独的py文件测试ORM操做须要配置的参数python
import os if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day58.settings") import django django.setup() from app01 import models # 这一句话必须在这下面导入
""" 查看orm内部sql语句的方法有哪些 1.若是是queryset对象 那么能够点query直接查看该queryset的内部sql语句 2.在django项目的配置文件中 配置一下参数便可实现全部的orm在查询的时候自动打印对应的sql语句 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } } """
当一张表已经建立出来以后 后续还想添加字段,能够有两种方式mysql
删除字段 直接在models.py中注释该字段 而后从新执行两条命令便可
注意: 执行完以后 表中该字段所对应的全部的数据所有删除
而且通常状况下 基本是不会用到真正意义上的删除sql
查:数据库
增:django
删:
models.User.objects.filter(条件).delete()并发
改:
models.User.objects.filter(条件).update()app
__gt 大于 __lt 小于 __gte 大于等于 __lte 小于等于 __in 在某范围内(包含两头) __range 是否在设定的几个选择中
案例:测试
"""神奇的双下滑查询""" # 查询价格大于200的书籍 # res = models.Book.objects.filter(price__gt=200) # print(res) # 查询价格小于200的书籍 # res = models.Book.objects.filter(price__lt=200) # print(res) # 查询价格大于等于200.22的书籍 # res = models.Book.objects.filter(price__gte=200.22) # print(res) # 查询价格小于等于200.22的书籍 # res = models.Book.objects.filter(price__lte=200.22) # print(res) # 查询价格要么是200,要么是300,要么是666.66 # res = models.Book.objects.filter(price__in=[200,300,666.66]) # print(res) # 查询价格在200到800之间的 # res = models.Book.objects.filter(price__range=(200,800)) # 两边都包含 # print(res) # 查询书籍名字中包含p的 """原生sql语句 模糊匹配 like % _ """ # res = models.Book.objects.filter(title__contains='p') # 仅仅只能拿小写p # res = models.Book.objects.filter(title__icontains='p') # 忽略大小写 # print(res) # 查询书籍是以三开头的 # res = models.Book.objects.filter(title__startswith='三') # res1 = models.Book.objects.filter(title__endswith='p') # print(res) # print(res1) # 查询出版日期是2017的年(******) res = models.Book.objects.filter(create_time__year='2017') print(res)
我的小总结: 熟练利用 __ ,熟练使用 __ 的相关方法便可fetch
add() # 添加
set() # 修改
remove() # 不能接收可迭代对象
clear() # 清空 不用传参
# 一对多的字段的增删改查 # 增 # publish_id传数字 # models.Book.objects.create(title='三国演义',price=189.99,publish_id=1) # publish直接传出版社对象 # publish_obj = models.Publish.objects.filter(pk=2).first() # models.Book.objects.create(title='红楼梦',price=999.99,publish=publish_obj) # 改 # 传数字的 # models.Book.objects.filter(pk=1).update(publish_id=3) # 传对象的 # publish_obj = models.Publish.objects.filter(pk=2).first() # models.Book.objects.filter(pk=1).update(publish=publish_obj) # 查 # 删 # models.Publish.objects.filter(pk=2).delete() # 默认都是级联更新 级联删除
我的小总结:优化
正反向跨表就完事了,利用好双下滑线,上面例子没用,有些注意点就是 关于创建关联时候单表 __id对应的自动建表时候出来的字段, 给它赋对应数字id能够创建关系,要么就是 外键= 对象方式
# 多对多字段的增删改查 # 增 # 要给主键为1的书籍添加两个做者 # book_obj = models.Book.objects.filter(pk=1).first() # print(book_obj.authors) # 对象点击多对多虚拟字段 会直接跨到多对多的第三张表 # book_obj.authors.add(1) # book_obj.authors.add(2,3) # author_obj = models.Author.objects.filter(pk=1).first() # author_obj1 = models.Author.objects.filter(pk=2).first() # author_obj2 = models.Author.objects.filter(pk=3).first() # book_obj.authors.add(author_obj) # book_obj.authors.add(author_obj1,author_obj2) """ add() 是给书籍添加做者 括号内既能够传数字也能够传对象 而且支持一次性传多个 逗号隔开便可 """ # 改 # 将主键为1的书籍对象 做者修改成2,3 # book_obj = models.Book.objects.filter(pk=1).first() # book_obj.authors.set([2,]) # book_obj.authors.set([2,3]) # author_obj = models.Author.objects.filter(pk=1).first() # author_obj1 = models.Author.objects.filter(pk=2).first() # author_obj2 = models.Author.objects.filter(pk=3).first() # book_obj.authors.set([author_obj,]) # book_obj.authors.set([author_obj, author_obj1, author_obj2]) """ set()括号内 须要传一个可迭代对象 可迭代对象中 能够是多个数字组合 也能够是多个对象组合 可是不要混着用!!! """ # 删 # book_obj = models.Book.objects.filter(pk=1).first() # # book_obj.authors.remove(3) # book_obj.authors.remove(1,2) # author_obj = models.Author.objects.filter(pk=1).first() # author_obj1 = models.Author.objects.filter(pk=2).first() # author_obj2 = models.Author.objects.filter(pk=3).first() # book_obj.authors.remove(author_obj) # book_obj.authors.remove(author_obj1,author_obj2) """ remove()括号内既能够传数字 也能够传对象 而且支持传对个 逗号隔开便可 """ # 将某本书跟做者的关系所有清空 # book_obj = models.Book.objects.filter(pk=1).first() # book_obj.authors.clear() # 清空当前书籍与做者的全部关系 """ add() set() remove() 上面三个都支持传数字 或者对象 而且能够传多个 可是set须要传可迭代对象 clear() clear括号内不须要传任何参数 """
我的小总结:
利用好外键跨表,正反向跨表就完事了,利用好双下滑线,上面例子没用,有些注意点 ==>
add,set对象和id都不能混着用
能够经过id或对象添加关联,支持 逗号(,) 隔开传多个
set()括号内 须要传一个可迭代对象 ,可迭代对象中 能够是多个数字组合
remove()括号内既能够传数字 也能够传对象 ,而且支持传多个 逗号隔开便可
clear括号内不须要传任何参数
换句话说 只有set须要可迭代对象
""" 正向与反向的概念 # 一对一 # 正向:author---关联字段在author表里--->authordetail 按字段 # 反向:authordetail---关联字段在author表里--->author 按表名小写 # 一对多 # 正向:book---关联字段在book表里--->publish 按字段 # 反向:publish---关联字段在book表里--->book 按表名小写_set.all() 由于一个出版社对应着多个图书 # 多对多 # 正向:book---关联字段在book表里--->author 按字段 # 反向:author---关联字段在book表里--->book 按表名小写_set.all() 由于一个做者对应着多个图书 正向查询按外键字段 反向查询按表名小写 """ """基于对象的跨表查询(子查询:将一张表的查询结果当作另一个查询语句的条件)""" """ 强调:在书写orm语句的时候 跟写sql语句同样 不要尝试着 一次性写完 应该作到写一点看一点再一点 """ # 1.查询书籍id是1 的出版社名称 # book_obj = models.Book.objects.filter(pk=1).first() # print(book_obj.publish.name) # print(book_obj.publish.addr) # 2.查询书籍id是2 的做者姓名 # book_obj = models.Book.objects.filter(pk=2).first() # print(book_obj.authors) # app01.Author.None # print(book_obj.authors.all()) # res = book_obj.authors.all() # for r in res: # print(r.name) # 3.查询做者是jason的家庭住址 # author_obj = models.Author.objects.filter(name='jason').first() # print(author_obj.author_detail.addr) # 4.查询出版社是东方出版社出版的书籍 # publish_obj = models.Publish.objects.filter(name='东方出版社').first() # # print(publish_obj.book_set) # app01.Book.None # print(publish_obj.book_set.all()) # 5.查询做者是jason的写过的全部的书籍 # author_obj = models.Author.objects.filter(name='jason').first() # print(author_obj.book_set) # app01.Book.None # print(author_obj.book_set.all()) # 6.查询电话号码是130的做者姓名 # author_detail_obj = models.AuthorDetail.objects.filter(phone=130).first() # print(author_detail_obj.author.name) # print(author_detail_obj.author.age) """ 当你反向查询的结果是多个的时候 就须要加_set 不然直接代表小写便可 """ # 7.查询书籍id为1 的做者的电话号码 # book_obj = models.Book.objects.filter(pk=1).first() # author_list = book_obj.authors.all() # for author_obj in author_list: # print(author_obj.author_detail.phone) """基于双下划綫的跨表查询(连表操做) left join inner join right join union """ # 正向 # 1.查询jason做者的手机号 # res = models.Author.objects.filter(name='jason').values('author_detail__phone','author_detail__addr') # print(res) # res1 = models.AuthorDetail.objects.filter(author__name='jason').values('phone') # print(res1) # 查询jason这个做者的年龄和手机号 # 正向 # res = models.Author.objects.filter(name='jason').values('age','author_detail__phone') # print(res) # 反向 # res1 = models.AuthorDetail.objects.filter(author__name='jason').values('phone','author__age') # print(res1) # 查询手机号是130的做者年龄 # 正向 # res = models.AuthorDetail.objects.filter(phone=130).values('author__age') # print(res) # # 反向 # res1 = models.Author.objects.filter(author_detail__phone=130).values('age') # print(res1) # 查询书籍id是1 的做者的电话号码 # res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone') # res1 = models.Book.objects.filter(pk=1).values('外键字段1__外键字段2__外键字段3__普通字段') # print(res) """只要表里面有外键字段 你就能够无限制跨多张表""" # 1.查询出版社为北方出版社的全部图书的名字和价格 # res = models.Publish.objects.filter(name='北方出版社').values('book__title','book__price') # print(res) # 2.查询北方出版社出版的价格大于19的书 # res = models.Book.objects.filter(price__gt=19,publish__name='北方出版社').values('title','publish__name') # print(res)
from django.db.models import Max, Min, Count, Avg, Sum
是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。
# 1.统计全部书的总价格 # from django.db.models import Max,Min,Count,Avg,Sum # # # res = models.Book.objects.aggregate(Sum('price')) # res1 = models.Book.objects.aggregate(Avg('price')) # res2 = models.Book.objects.aggregate(Count('price')) # res3 = models.Book.objects.aggregate(Max('price')) # res4 = models.Book.objects.aggregate(Min('price')) # res5 = models.Book.objects.aggregate(Max('price'),Min('price'),Count('pk'),Avg('price'),Sum('price')) # print(res5) # print(res) # print(res1) # print(res2)
我的口水总结:
思考方式==> aggregate以后 对应的字符串就是搜索条件,已经获取相关的对应条件全部对象的对应条件数值后,对它进行 其余的方法,好比 (Count,Max等等) 这时候能够 = 方式赋值, 当前指向 改位置的变量 , .values() 里面能够作为字符串方式呈现, 做为搜索结果的一个 key
运用场景联想==>
针对全部时候考虑
聚合, 把分散的结合汇总起来统计 , 好比 书不少, 价格档次不一样, 这时候aggregate 根据不一样的书得到对应全部的档次(这边是全考虑,不考虑任何分组), 而后能够针对这全部档次的数据能够进一步进行方法操做
from django.db.models import Max, Min, Count, Avg, Sum
# 统计每一本书的做者个数 from django.db.models import Max, Min, Count, Avg, Sum # res = models.Book.objects.annotate(author_num = Count('authors')).values('author_num','title') # print(res) # 统计出每一个出版社卖的最便宜的书的价格 # res = models.Publish.objects.annotate(mmp = Min('book__price')).values('name','mmp') # print(res) # 统计不止一个做者的图书 # res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1) # print(res) """ 只要是queryset对象 就能够无限制的调用queryset对象的方法!!! 最最经常使用的就是对一个已经filter过滤完的数据 再进行更细化的筛选 """ # 查询各个做者出的书的总价格 # res = models.Author.objects.annotate(sp=Sum('book__price')).values('name','sp') # print(res) #F查询的本质就是从数据库中获取某个字段的值 # 查询库存数大于卖出数的书籍 """以前查询等号后面的条件都是咱们认为输入的 如今变成了须要从数据库中获取数据放在等号后面 """
我的口水总结:
思考方式==> annotate以后 对应的字符串就是搜索条件,而后搜索条件获取对应条件的对象而后对他们进行分组,以后能够对它们进行 其余的方法,好比 (Count,Max等等) 这时候能够 = 方式赋值, 当前指向 该位置的变量 , .values() 里面能够作为字符串方式呈现, 做为搜索结果的一个 key
运用场景联想==>
针对各个状况考虑
分组, 把分散的结合汇总起来 , 好比 书不少, 价格档次不一样, 这时候annotate根据不一样的价格分红对应的档次, 每一个档次对应多个对象, 而后在每一个分组对对应的数据能够进一步进行操做, 汇总
当搜索条件后面的参数不为固定值时候想到F或Q查询
F 能够帮咱们取到表中某个字段对应的值来看成个人筛选条件
from django.db.models import F
经典案例:
从数据库中获取字段对应的数据
列子如: 库存数大于卖出数
# res = models.Book.objects.filter(kucun__gt=F('maichu')) # print(res) # 将书籍库存数所有增长1000 # models.Book.objects.update(kucun=F('kucun')+1000) # 把全部书名后面加上'新款' # from django.db.models.functions import Concat # from django.db.models import Value # # ret3 = models.Book.objects.update(title=Concat(F('title'), Value('新款'))) # models.Book.objects.update(title = F('title')+'新款') # 不能这么写
我的总结:
当搜索条件后面的参数不为固定值时候想到F,须要字符串拼接时候,用 Concat, Value, 不过须要导入,直接弄 会出问题,全部字段都为数据单一
Q 实现取反和或
filter() 等方法中逗号隔开的条件是与的关系。 若是你须要执行更复杂的查询(例如OR语句),你可使用Q对象。
咱们能够组合& 和| 操做符以及使用括号进行分组来编写任意复杂的Q 对象。
同时,Q 对象可使用~ 操做符取反,这容许组合正常的查询和取反(NOT) 查询。
from django.db.models import Q
经典案例:
# 查询书籍名称是三国演义或者价格是444.44 # res = models.Book.objects.filter(title='三国演义',price=444.44) # filter只支持and关系 # res1 = models.Book.objects.filter(Q(title='三国演义'),Q(price=444)) # 若是用逗号 那么仍是and关系 # res2 = models.Book.objects.filter(Q(title='三国演义')|Q(price=444)) # res3 = models.Book.objects.filter(~Q(title='三国演义')|Q(price=444)) # print(res2) # Q高级用法 q = Q() q.connector = 'or' # 修改查询条件的关系 默认是and q.children.append(('title__contains','三国演义')) # 往列表中添加筛选条件 q.children.append(('price__gt',444)) # 往列表中添加筛选条件 res = models.Book.objects.filter(q) # filter支持你直接传q对象 可是默认仍是and关系 print(res)
我的口水总结:
或的时候和取反时候考虑, 对应 ~ , |
能够用Q() 建立对象方法实现对象q q.children为一个列表,给当前列表添加2参数的元组,做为对应的搜索条件, 默认为 and 关系 , 设置 q的 connector = "or" 就可使之称为 或关系
ACID 原子性 都为最小的不可分割的单位,要么成功要么失败 一致性 从一个一致性状态到另外一个一致性状态 隔离性 各个数据操做之间是隔离的, 并发操做并不会影响到其余的数据的操做 持久性 当提交以后数据的状态就永久肯定恒定,不会由于回退或取消而发送变化
with transaction.atomic():
在里面进行相关操做,本质就是一个锁
from django.db import transaction # 哈哈哈, 也是从 django.db导入哩 with transaction.atomic(): """数据库操做 在该代码块中书写的操做 同属于一个事务 """ models.Book.objects.create() models.Publish.objects.create() # 添加书籍和出版社 就是同一个事务 要么一块儿成功要么一块儿失败 print('出了 代码块 事务就结束')
跟数据相关, ORM操做的相关方法, 通常都是从 from django.db import 导入
orm内全部的语句操做都是 惰性查询:
只会在你真正须要数据的时候才会走数据库,若是你单单只写orm语句时不会走数据库的
这样设计的好处 在于 减轻数据库的压力
案例:
# res = models.Book.objects.all() # print(res) # res = models.Book.objects.values('title') # # print(res) # for r in res: # print(r.title) # res = models.Book.objects.only('title') # # print(res) # for r in res: # # print(r.title) # 只走一次数据库查询 # print(r.price) # 当你点击一个不是only括号内指定的字段的时候 不会报错 而是会频繁的走数据库查询 # res1 = models.Book.objects.defer('title') # defer与only是相反的 # for r in res1: # defer会将不是括号内的全部的字段信息 所有查询出来封装对象中 # # 一旦你点击了括号内的字段 那么会频繁的走数据库查询 # print(r.price)
defer 与 only 是 相反的
# select_related帮你直接连表操做 查询数据 括号内只能放外键字段 # res = models.Book.objects.all().select_related('publish') # for r in res: # print(r.publish.name) # res = models.Book.objects.all().select_related('publish__xxx__yyy__ttt') # print(res) # res = models.Book.objects.all() """ select_related:会将括号内外键字段所关联的那张表 直接所有拿过来(能够一次性拿多张表)跟当前表拼接操做 从而下降你跨表查询 数据库的压力 注意select_related括号只能放外键字段(一对一和一对多) res = models.Book.objects.all().select_related('外键字段1__外键字段2__外键字段3__外键字段4') """ # prefetch_related 不主动连表 res = models.Book.objects.prefetch_related('publish') """ 不主动连表操做(可是内部给你的感受像是连表操做了) 而是将book表中的publish所有拿出来 在取publish表中将id对应的全部的数据取出 res = models.Book.objects.prefetch_related('publish') 括号内有几个外键字段 就会走几回数据库查询操做 """ for r in res: print(r.publish.name)
我的口水总结:
select_related:
会将括号内外键字段所关联的那张表 直接所有拿过来(能够一次性拿多张表)跟当前表拼接操做
从而下降你跨表查询 数据库的压力
select_related括号只能放外键字段(一对一和一对多)
prefetch_related:
不主动连表操做(可是内部给你的感受像是连表操做了) 而是将book表中的publish所有拿出来 在取publish表中将id对应的全部的数据取出
括号内有几个外键字段 就会走几回数据库查询操做
class MyChar(models.Field): def __init__(self,max_length,*args,**kwargs): self.max_length = max_length super().__init__(max_length=max_length,*args,**kwargs) def db_type(self, connection): return 'char(%s)'%self.max_length
django2注意点:
外键字段 当你在使用django2.X版本的时候 在创建外键关系时(*****) 须要你手动添加几个关键点参数 models.cascade db_constraints