ORM全集

 

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)

 

相关文章

注:所有源代码均实测运行过。所有源代码均已上传CSDN,请有...
继承APIView和ViewSetMixin;作用也与APIView基本类似,提供...
一、Django介绍Python下有许多款不同的 Web 框架。Django是重...
本文从nginx快速掌握到使用,gunicorn快速掌握到使用,实现小...
uniapp微信小程序订阅消息发送服务通知
Django终端打印SQL语句 1 Setting配置: 2 默认python 使用的...