Django终端打印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',
},
}
}
1 Setting配置:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 's3',
'USER': 'root',
'PASSWORD': '123456',
'HOST': '127.0.0.1',
'PORT': '3306',
}
}
2 默认python 使用的MysqlDB连接,Python3 支持支持pymysql 所有需要在app里面的__init__加上下面配置:
import pymysql
pymysql.install_as_MySQLdb()
3 models是用类的方式来配置管理数据库表的 需要继承models.Model这个类来管理数据库:
class ec2(models.Model):
name = models.CharField(max_length=32)
price = models.IntegerField()
pub = models.CharField(max_length=22)
4 数据库表建立好后需要让表写入到数据库
1 python manage.py makemigrations
2 python manage.py migrate
5 models 表里面需要在原来的表里面在新增一个字段字段提示:
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column) #现在提供一次性默认值(将在所有现有行上设置此列的空值)
2) Quit, and let me add a default in models.py 退出,让我添加一个默认的 models.py
Select an option: 1 #选择1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> "pool" #添加默认字段内容
6 ORM数据增加
有如下表:
class Test(models.Model):
name = models.CharField(max_length=32)
password = models.CharField(max_length=32)
add = models.CharField(max_length=32)
email = models.CharField(max_length=32)
def __str__(self):
return self.name
增的2种形式4种方法
方法1 models.Test.objects.create(name='ajx', password='test', add='beijing', email='123@163.com')
方法2 models.Test.objects.create(**{'name':'djaong','password':'null','add':'null','email':'null'}) #推荐这方法
方法3 arg = models.Test(name='gost', password='test', add='beijing', email='123@163.com')
arg.save()
方法4 arg = models.Test()
arg.name = 'gostadd'
arg.password = 'udi'
arg.add = 'shenzhen'
arg.email = 'null'
arg.save()
通过类的对象操作的话,一定要进行save()方法.
7 ORM更新数据
更新数据
更新id=2的 把password改为1111
models.logininfo.objects.filter(id=2).update(password = '1111')
全部改为1111
models.logininfo.objects.all().update(password = '1111')
8 ORM删除数据
删除所有的
models.logininfo.objects.all().delete()
删除 id =3 的
models.logininfo.objects.filter(id=3).delete()
9 ORM查询
filter(**kwargs): 它包含了与所给筛选条件相匹配的对象
all(): 查询所有结果
get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
-----下面的方法都是对查询的结果再进行处理:比如 objects.filter.values()--------
values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列
exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象
order_by(*field): 对查询结果排序
reverse(): 对查询结果反向排序
distinct(): 从返回结果中剔除重复纪录
values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
count(): 返回数据库中匹配查询(QuerySet)的对象数量。
first(): 返回第一条记录
last(): 返回最后一条记录
exists(): 判断queryset数据是不是为真,会走sql语句,但是把数据存在缓存
ec1 = ec2.objects.all()[:2]
ec3 = ec2.objects.all()[::2] #隔着获取,每隔2个获取
ec4 = ec2.objects.all()[::-1] #倒着隔着获取
ec5 = ec2.objects.first() #获取第一个
ec6 = ec2.objects.last() #获取最后一个
ec9 = ec2.objects.exclude(name="ok").values("pub") #排除name等于ok的数据 其他都显示
ec12 = ec2.objects.all().values("name").distinct() #找到name字段的值,进行去重
查询执行的语句
user_list = models.userinfo.objects.all()
print(user_list.query) #查询SQL执行的语句
#查询数据
query 查询当前sql语句
ret = models.username.objects.all().query
print(ret)
获取单条数据,不存在则报错(不建议)
models.username.objects.get(id=123) 这查询出来是一个对象
models.username.objects.filte(id=123)filter查询出来的是一个QuerySet类型 models.username.objects.filter(name='seven')[0]查询出来就是一个对象;
获取指定条件的数据
models.username.objects.filter(name='seven')
models.username.objects.exclude(name='seven') 查询不等于seven的数据
查询username表中的所有数据,查询出来的类型是QuerySet,可以用for循环,QuerySet可以支持for循环,拿数据
ret = models.username.objects.all()
print(ret)
for item in ret:
print(item.user, item.id)
查询username中的'id', 'user'字段,数据是以字典形式
ret = models.username.objects.all().values('id', 'user')
print(ret)
for item in ret:
print(item)
查询username中的'id', 'user'字段,数据是以列表形式,里面是元祖
ret = models.username.objects.all().values_list('id', 'user')
print(ret)
for item in ret:
print(item)
10 ORM了不起的双下划线(__)之单表条件查询
models.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") #like
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and
startswith,istartswith, endswith, iendswith,
11 ORM创建一对多
创建一对多(谁是多,ForeignKey就建立在谁的表里面)外面一定是建立到多的那边
class Publisher(models.Model):
Publisher_all = models.CharField(max_length=32)
def __str__(self):
return self.name
class Book(models.Model):
Book_name = models.CharField(max_length=32)
Publisher = models.ForeignKey("Publisher") #会生成一个Publisher_id字段
12 ORM 一对多的查询
一对多查询数据:进行filter跨表操作的时候 需要进行双下划线操作---记死了 (如果是个对象可以用操作比如obj = models.s3.obj.all() 这个查询多来是多条数据,包含在一个对象里面,for循环 所以可以用点来操作)
class Publisher(models.Model): #出版社表
PublisherName = models.CharField(max_length=32)
City = models.CharField(max_length=32)
def __str__(self):
return self.name
class Book(models.Model): #书籍表
Book_name = models.CharField(max_length=32)
ToPublisher = models.ForeignKey("Publisher")
正向查询 书是多,根据书查询年出版社
查询book中ID等于2的对象,根据obj对象查询这个书属于那个城市的出版社(obj.ToPublisher 这个一定是一个对象,这个对象是foreignkey表的对象内容,一条记录的对象)
obj = models.Book.objects.get(id=2)
print(obj.ToPublisher.City) #查询到城市
一对多 3种正向查询的方式:
class Classes(models.Model):
caption = models.CharField(max_length=32)
class Student(models.Model):
name = models.CharField(max_length=32)
cls = models.ForeignKey('Classes')
1 obj = models.Student.objects.all()
print(obj[0].cls.caption)
2 models.Student.objects.all().values('id','name','cls__caption')
3 models.Student.objects.all().values_list('id','name','cls__caption')
反向查询 出版社是单,根据出版社查询多个书籍
根据出版社查询所有的书籍名
obj = models.Publisher.objects.get(PublisherName='新华出版社')
print(obj.book_set.all().values('Book_name')[0]) 和 print(obj.book_set.values('Book_name')[0]) 相等 (book_set 这是django提供的一个反向查询方法 set加上表的名字进行关联,必须小写) Book_set(Book是表名)
双下划线主要用显示关联表的内容(推荐使用)
查询书籍等于'我的心情'这书记属于那个出版社(因为Publisher没使用到ForeignKey,所以直接可以使用book__Book_name) book是表名 Book_name字段名
obj = models.Publisher.objects.filter(book__Book_name='我的心情').values('PublisherName')[0]
或者根据book表查询书名,找到出版社
obj = models.Book.objects.filter(Book_name='我的心情').values('ToPublisher__PublisherName').distinct()[0]
obj = models.Publisher.objects.filter(book__Book_name='我的心情').values('PublisherName').distinct()[0] #distinct去除重复
正向查询,更具出版社查询书名,双下划线前面一定要用一对多那个字段ToPublisher,用Publisher报错,视频讲解有错误
obj = models.Book.objects.filter(ToPublisher__PublisherName='新华出版社').values('Book_name')
13 ORM一对多插入数据
方式1 models.Book.objects.create(**{'Book_name':'c++','Publisher_id':1})
方式2 models.Book.objects.create('Book_name'='c++','Publisher_id'=1)
方式3 obj = models.Publisher.objects.filter(id=3)[0] 或者 obj = models.Publisher.objects.get(id=2)
models.Book.objects.create(**{'Book_name':'c++','Publisher':obj})
#因为 Book表中的 ForeignKey生产的表叫Publisher_id 如果需要通过book表中的Publisher 字段插入的话,就需要先获取到Publisher表中的对象然后在插入数据
14 多对的查询
class book(models.Model):
name = models.CharField(max_length=32)
class author(models.Model):
name = models.CharField(max_length=32)
m = models.ManyToManyField('book')
def __str__(self):
return self.name
操作第3张表,只能间接获取,
操作表的三种方式;跟一对查找差不多
#正向
1 obj = models.author.objects.all()
for i in obj:
print(i.name,i.m.all().values('name'))
2 author_list = models.author.objects.values('id', 'name', 'm__name')
print(author_list)
#反向
3 book_list = models.book.objects.values('id','name','author__name')
print(book_list)
4 obj = models.book.objects.get(id=1)
print(obj.author_set.all())
增加相关操作
obj = models.author.objects.get(id=2) #获取的app aid等于2那个那个数据
obj.m.add(2,3,4,5)
obj.mtm.add(*[2,3,4,5])
清空 删除和更新相关操作
obj.m.clear() #清除author表中id=2的所有数据包含
obj.m.remove(1) 删除id=2 对应的第一本书
obj.m.set([3,5,7]) #更新操作 把aid=2的全部更新为3 5 7.
#反向操作增加 删除 更新同上
obj = models.book.objects.get(id=3)
obj.author_set.add(1,2,3)
15 ORM惰性机制:
所谓惰性机制:Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会马上执行sql,而是当调用QuerySet的时候才执行。
QuerySet特点:
<1> 可迭代的
<2> 可切片
QuerySet的高效使用:
<1>Django的queryset是惰性的
Django的queryset对应于数据库的若干记录(row),通过可选的查询来过滤。例如,下面的代码会得
到数据库中名字为‘Dave’的所有的人:person_set = Person.objects.filter(first_name="Dave")
上面的代码并没有运行任何的数据库查询。你可以使用person_set,给它加上一些过滤条件,或者将它传给某个函数,
这些操作都不会发送给数据库。这是对的,因为数据库查询是显著影响web应用性能的因素之一。
<2>要真正从数据库获得数据,你可以遍历queryset或者使用if queryset,总之你用到数据时就会执行sql.
为了验证这些,需要在settings里加入 LOGGING(验证方式)
obj=models.Book.objects.filter(id=3)
# for i in obj:
# print(i)
# if obj:
# print("ok")
<3>queryset是具有cache的
当你遍历queryset时,所有匹配的记录会从数据库获取,然后转换成Django的model。这被称为执行
(evaluation).这些model会保存在queryset内置的cache中,这样如果你再次遍历这个queryset,
你不需要重复运行通用的查询。
obj=models.Book.objects.filter(id=3)
# for i in obj:
# print(i)
## models.Book.objects.filter(id=3).update(title="GO")
## obj_new=models.Book.objects.filter(id=3)
# for i in obj:
# print(i) #LOGGING只会打印一次
<4>
简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,虽然你并不需要这些
数据!为了避免这个,可以用exists()方法来检查是否有数据:
obj = Book.objects.filter(id=4)
# exists()的检查可以避免数据放入queryset的cache。
if obj.exists():
print("hello world!")
<5>当queryset非常巨大时,cache会成为问题
处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统
进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法
来获取数据,处理完数据就将其丢弃。
objs = Book.objects.all().iterator()
# iterator()可以一次只从数据库获取少量数据,这样可以节省内存
for obj in objs:
print(obj.name)
#BUT,再次遍历没有打印,因为迭代器已经在上一次遍历(next)到最后一次了,没得遍历了
for obj in objs:
print(obj.name)
#当然,使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。所以使
#用iterator()的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询
总结:
queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。
使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能
会造成额外的数据库查询。
16 查看ORM执行的每条sql语句?? 在setting里面增加如下配置
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
17 ORM聚合查询(Avg Min Sun Max)
通过对QuerySet进行计算,返回一个聚合值的字典。aggregate()中每一个参数都指定一个包含在字典中的返回值。即在查询集上生成聚合。
from django.db.models import Avg,Min,Max,Sum
聚合查询用到aggregate
从整个查询集生成统计值。比如,你想要计算所有在售书的平均价钱。Django的查询语法提供了一种方式描述所有
图书的集合。
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}
aggregate()子句的参数描述了我们想要计算的聚合值,在这个例子中,是Book模型中price字段的平均值
aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的
标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定
一个名称,可以向聚合子句提供它:
>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}
如果你也想知道所有图书价格的最大值和最小值,可以这样查询:
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
print(models.Apple.objects.all().aggregate(Avg('price')))
print(models.Apple.objects.all().aggregate(Avg('money')))
print(models.Apple.objects.all().aggregate(Min('price')))
print(models.Apple.objects.all().aggregate(Min('money')))
print(models.Apple.objects.all().aggregate(Max('price')))
print(models.Apple.objects.all().aggregate(Max('money')))
print(models.Apple.objects.all().aggregate(Sum('price')))
print(models.Apple.objects.all().aggregate(Sum('money')))
18 ORM F操作
from django.db.models import Q,F
更新Book表中 ID等于3的中的pages字段中的pages数加100
F就如下操作:
def orm(self):
models.Book.objects.filter(id=3).update(pages=F('pages') + 100)
return HttpResponse("OK")
19 ORM Q操作
from django.db.models import Q,F
Q(Q(username=u)&Q(pwd=p))|~Q(Q(emial=u)&Q(pwd=p)) ~非操作
Q(Q(username=u)&Q(pwd=p))|~Q(Q(emial=u)&Q(pwd=p),name='Gosht') name='Gosht'必须放后面,不然报错
Q构造条件分为两种方式:
第1种:
models.UserInfo.objects.filter( Q(Q(username=u)&Q(pwd=p)) | Q(Q(emial=u)&Q(pwd=p)) )
分解:Q(Q(username=u)&Q(pwd=p))|Q(Q(emial=u)&Q(pwd=p))
第一个Q: Q(username=u) & Q(pwd=p) 之间and操作
第二个Q: Q(Q(emial=u) & Q(pwd=p)之间and操作
Q(Q(username=u)&Q(pwd=p)) | Q(Q(emial=u)&Q(pwd=p)) 构建一个大Q 让2个小Q之间or操作
比如一个登录页面允许帐号或者邮件登录2中方式
第2种:
把上面第一种Q(Q(username=u)&Q(pwd=p)) | Q(Q(emial=u)&Q(pwd=p)) 用第二中表达如下
con = Q() 创建一个大Q对象 相当于上面的红色的这个Q(Q(username=u)&Q(pwd=p)) | Q(Q(emial=u)&Q(pwd=p))
q1 = Q() 创建小Q对象 相当于上面的红色的这个 Q(username=u)&Q(pwd=p))
q1.connector = 'AND' 让小Q对象里面条件为and 相当于上面的红色的这个Q(username=u)&Q(pwd=p))
q1.children.append(('username', u)) 让小Q对象里面增加查询条件 相当于上面的红色的这个Q(username=u)&Q(pwd=p))
q1.children.append(('pwd', p)) 让小Q对象里面增加查询条件 相当于上面的红色的这个Q(username=u)&Q(pwd=p))
# Q(Q(username=u)&Q(pwd=p))
q2 = Q() 与上相同
q2.connector = 'AND'
q2.children.append(('email', e))
q2.children.append(('password', p))
# Q(Q(email=2)&Q(pwd=p))
con.add(q1, 'OR') #大Q然后增加小Q1对象
con.add(q2, 'OR') #大Q然后增加小Q2对象
最终构造了一个搜索条件:
Q(Q(username=u)&Q(pwd=p)) | Q(Q(emial=u)&Q(pwd=p))
放入models开始执行 models.UserInfo.objects.filter(con)
为什么要在 con.add(q1, 'OR') 加OR:
可能有这样的条件 id == 1 or q1 or q2 如果不是这样的条件上面 con.add(q1, 'OR') 不会报错
如何让一个字典做第二种方式搜索:
#vla = {'id': 1,'name': 'root'}
for k,v in val.items:
q1.children.append((k, v))
在创建一个q,并且是让connector 等于OR
q = Q()
q.connector = 'OR'
创建条件,(id=1) (id=3) (id=2)之间的关系就是上面创建的OR关系 也就是之间或关系
q.children.append((id,1))
q.children.append((id,3))
q.children.append((id,2))
20 ORM分组
1.和sql对比:
1.models.Employee Employee相当于sql中的from 后面的表名
2.annotate前面的values值相当于group by的字段
3.(a=Avg("salary"))里面如是跨表查询就需要使用双下划线,正查就字段__另一张表的字段,反查就另一张表明__字段
4.annotate后面的values 是select的字段
对应格式:select __ from ___ inner join ———— on ... group by ____
2.关键点:
1.queryset对象.annotate() annotate前面是queryset对象
2.annotate进行分组统计,按前面select的字段进行group by
3.annotate()返回值依然是queryset对象,增加了分组统计之后的键值对
补充:
1 stu = models.studen.objects.all() ----> 拿到是一个是querset数据类型,可以把这种数据类型理解为是一个列表 取到结果是[obj,obj,obj,obj,obj] #obj是一个对象.怎么获取obj的值? obj.name,obj.age
2 stu = models.studen.objects.all().values('id','name') ----> 拿到是一个也是是querset数据类型取到结果是[{'id':1,"name":'xxx'},{'id':2,"name":'zzz'}]
3 stu = models.studen.objects.all().values('id','name') ----> 拿到是一个也是是querset数据类型取到结果是[(1,'xxx'),(2,'zzz')]
跨表查询(一对多查询)
class school(models.Model):
'''校区表'''
schoolname = models.CharField(max_length=22)
class Student(models.Model):
'''学生表'''
username = models.CharField(max_length=33)
age = models.IntegerField()
gender = models.NullBooleanField()
cs = models.ForeignKey("Classes",on_delete=False) #cs 代表是Classes表的一个对象(title,fk(fk代表是school对象(n)))
class Classes(models.Model):
'''班级表'''
title = models.CharField(max_length=33)
#m = models.ManyToManyField("Teacher")
fk = models.ForeignKye("School",no_delete=False,related_name="sss")
1 查找python班所有的学生
models.Student.objects.filter(cs__title="pyrhon班")
#另外一种写法反向查询(有两次查询sql次数,推荐正向查找):
obj = modes.Classes.objects,fileter(title="python班").first() ===> queryset对象值 [obj.obj,obj] #现在找到班级
obj.student__set.all() ===> queryset对象值 [obj.obj,obj] #通过班级找到学生,student是学生表(小写);也就是说反向查询的时候,Classes表里面隐含了一个字段student__set,通过这个字段,可以进行数据操作(这个隐含的字段必须是其他表跟这个表(classes)ForeignKye字段关联,才能使用)总结:
正向查询:cs 这个ForeignKye字段查询
反向查询:小写的表名字+__set查询 (如果有related_name="sss",可以使用ssss__set后去数据)
2 查找python班所有的学生以及所在班级和姓名
stu = models.Student.objects.filter(cs__title="pyrhon班").values("username","cs__title")
取结果:
for row in stu:
print(row['username'],row['cs__fitle'])
3 查找python班所有的学生以及所在班级,姓名 和 校区
stu = models.Student.objects.filter(cs__title="pyrhon班").values("username","cs__title","cs__fk__schoolname")
跨表查询(多对多)
class Classes(models.Model):
'''班级表'''
title = models.CharField(max_length=33)
m = models.ManyToManyField("Teacher",related_name="sss") #m
class Teacher(models.Model):
'''老师表'''
name = models.CharField(max_length=33) #classes_set
获取到值如下:
obj = models.Classes.objects.all() #对象
get = models.Classes.objects.get(id=1) #对象
filter = models.Classes.objects.filter(id=2)[0] #对象
filter = models.Classes.objects.filter(id=2).first() #对象
filter = models.Classes.objects.filter(id=2) #queryset
#正向添加
filter.m.add(2) #只有对象才能add操作 也就是找到id=2的数据,然后在第三张表中添加第二个老师
filter.m.add(*[1,2])
#反向添加
filter = models.teacher.objects.filter(id=2).first()
filter.classes__set.add(2) #通过老师来添加班级
for 循环获取值:
obj = models.Classes.objects.all()
for i in obj:
print(i.id,i.title,i.m.all().values("name"))
for row in i.m.all():
print(row.name)