Django模型层ORM学习笔记
首先测试文件需要做一些准备工作,我使用的是test.py,需要在manage.py文件夹中拷贝以下内容
from django.test import TestCase
# Create your tests here.
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day58.settings")
import django
django.setup()
# 以上语句固定写法,接下来写导入models模块的语句
from app01 import models
# 从这开始正常写ORM操作数据库的语句即可
查看ORM操作数据库内部走的mysql语句
当我们操作数据库拿回来的是QuerySet对象是,我们可以通过QuerySet对象.query查看内部的mysql语句,那么返回的不是QuerySet对象时该怎么做呢?只需要在settings.py中找个地方将下面语句放着即可(运用日志)。
ORM单表查询,双下划线查询
为了更好的了解ORM对数据库的操作,我们选择连接mysql数据库,毕竟sqlite3还是有点小瑕疵的。
ORM有很多常用字段,其中字段DateField的参数有auto_now跟auto_now_add,如果我们配置了这两个参数中的一个,创建数据对象时DateField字段会自动创建,无需我们传值。配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库,之后就不在变化。配置上auto_now=True,每次更新数据记录的时候会更新该字段。友情提示,DateFIeld和DateTImeField可以直接接收datetime对象。
ForeignKey有to_field参数,可以指定你想跟一张表的什么字段建立外键,不写默认是id
models.py中创建表:
from django.db import models
# Create your models here.
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8, decimal_places=2)
publish_time = models.DateField(auto_now_add=True)
publish = models.ForeignKey(to='Publish')
authors = models.ManyToManyField(to='Author')
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
email = models.EmailField()
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
author_detail = models.OneToOneField(to='AuthorDetail')
class AuthorDetail(models.Model):
phone = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
单表查询:
1新增数据
# 第一种:有返回值,并且就是当前被创建的数据对象
modles.Publish.objects.create(name='',addr='',email='')
# 第二种:先实例化产生对象,然后调用save方法保存
book_obj = models.Publish(name='',addr='',email='')
book_obj.save()
2修改数据
# 基于数据对象
user_obj = models.User.objects.filter(name='jason').first()
user_obj.age = 17
user_obj.save()
# 基于queryset对象
models.User.objects.filter(name='kevin').update(age=66)
删除数据
# 基于数据对象
# user_obj = models.User.objects.filter(name='owen').first()
# user_obj.delete()
# 基于queryset对象
models.User.objects.filter(name='egon').delete()
查询数据
之前常用的查询是all查所有及filter条件查询,其中filter条件查询有多个条件时,各条件是and的关系。filter查询传多个参数时,各参数间是and的关系:
models.User.objects.filter(name='egon', age=18) # name='egon' and 'age'=18
其实查询数据能用到的方法一共有13个(均是QuerySet对象的方法),最好都记住。然后QuerySet对象能够无限制的点QuerySet方法:
在过滤得到的QuerySet对象的基础上再进行过滤,依次类推
models.User.objects.filter(过滤条件1).filter(过滤条件2).order_by(排序依据的字段名).reverse()
# reverse方法使用的前提是QuerySet对象被排序过,order_by是排序(排序依据的字段名前加-号即是反向排序)
<1> all(): #查询所有结果
<2> filter(**kwargs): #它包含了与所给筛选条件相匹配的对象
<3> get(**kwargs): #返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
<4> exclude(**kwargs): #它包含了与所给筛选条件不匹配的对象
<5> order_by(*field): #对查询结果排序('-id')/('price')
<6> reverse(): #对查询结果反向排序 >>>前面要先有排序才能反向
<7> count(): #返回数据库中匹配查询(QuerySet)的对象数量。
<8> first(): #返回第一条记录
<9> last(): #返回最后一条记录
<10> exists(): #如果QuerySet包含数据,就返回True,否则返回False
<11> values(*field): #返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列
<12> values_list(*field): #它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
<13> distinct(): #从返回结果中剔除重复纪录
着重记住values方法(普通QuerySet对象:<QuerySet [<Author: Author object>]>):
在上述13种QuerySet方法中,有些方法执行完返回的不一定是QuerySet对象,可能是其他对象。
# 返回QuerySet对象的方法
all()、filter()、exclude()、order_by()、reverse()、distinct()
# 特殊的QuerySet
values() #返回一个可迭代的字典序列
values_list() #返回一个可迭代的元祖序列
# 返回具体数据对象的
get()、first()、last()
# 返回布尔值的方法
exists() #判断QuerySet对象是否为空,空返回False,否则返回True
# 返回数字的方法
count() #返回当前QuerySet对象中的数据对象个数
双下划线查询
上述的查询中,filter的查询条件都是name=‘值’,age=值之类的,可是我们查询时条件肯定会有age>18,age<=18等情况。这个时候就需要用的双下划线查询。
大于 小于 大于等于 小于等于
filter(price__gt='90') # 大于 great than
filter(price__lt='90') # 小于 less than
filter(price__gte='90') # 大于等于 great than equal
filter(price__lte='90') # 小于等与 less than equal
存在于某几个条件中
filter(age__in=['11','22','33'])
在某个范围内
filter(age__range=[50,90])
模糊查询
filter(title__contains='p') # 区分大小写
filter(title__icontains='P') # 不区分大小写
以什么开头,以什么结尾
# 查询名字以j开头的用户
res = models.User.objects.filter(name__startswith='j')
print(res)
# **查询名字以n结尾的用户**
res = models.User.objects.filter(name__endswith='n')
print(res)
按年查询(针对DateField和针对DateTImeField)
filter(create_time__year='2017')
多表操作
一对多记录增删改查
新增(主键用pk传比较好,比较稳)
# 直接写id
models.Book.objects.create(title='红楼梦',price=66.66,publish_id=1)
# 传数据对象
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='三国演义',price=199.99,publish=publish_obj)
修改
# Queryset修改
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)
# 对象修改
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.publish_id = 3 # 点表中真实存在的字段名
book_obj.save()
publish_obj = models.Publish.objects.filter(pk=2).first()
book_obj.publish = publish_obj # 点orm中字段名 传该字段对应的表的数据对象
book_obj.save()
删除
# 使用QuerySet对象删除
models.Book.objects.filter(pk=1).delete()
models.Publish.objects.filter(pk=1).delete()
# 使用数据对象删除
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.delete()
多对多记录增删改查
添加(add)
# 拿到id=3的书籍数据对象
book_obj = models.Book.objects.filter(pk=3).first()
# 数据对象.authors可以直接跳到多对多那张表里
# add传值传作者id,可以传多个
book_obj.authors.add(1)
book_obj.authors.add(2,3)
# add传值支持传对象,而且可以传多个
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=3).first()
book_obj.authors.add(author_obj)
book_obj.authors.add(author_obj,author_obj1)
修改(set)
# 拿到id=3的书籍数据对象
book_obj = models.Book.objects.filter(pk=3).first()
# 数据对象.authors可以直接跳到多对多那张表里
# set传值传作者id,可以传多个
book_obj.authors.set((1,))
book_obj.authors.set((1,2,3))
# set传值支持传对象,而且可以传多个
author_list = models.Author.objects.all()
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.set(author_list)
删除(remove)
# 拿到id=3的书籍数据对象
book_obj = models.Book.objects.filter(pk=3).first()
# 数据对象.authors可以直接跳到多对多那张表里
# remove传值支持传值,而且可以传多个
book_obj.authors.remove(1)
book_obj.authors.remove(2,3)
# remove传值支持传对象,而且可以传多个
author_obj = models.Author.objects.all().first()
author_list = models.Author.objects.all()
book_obj.authors.remove(author_obj)
book_obj.authors.remove(*author_list) #需要将Queryset对象打散
清空(clear)
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.clear()
**
正向反向查询
**
# 一对一
# 正向:author---关联字段在author表里--->authordetail 按字段
# 反向:authordetail---关联字段在author表里--->author 按表名小写
# 查询jason作者的手机号 正向查询
# 查询地址是 :山东 的作者名字 反向查询
# 一对多
# 正向:book---关联字段在book表里--->publish 按字段
# 反向:publish---关联字段在book表里--->book 按表名小写_set.all() 因为一个出版社对应着多个图书
# 多对多
# 正向:book---关联字段在book表里--->author 按字段
# 反向:author---关联字段在book表里--->book 按表名小写_set.all() 因为一个作者对应着多个图书
# 连续跨表
# 查询图书是三国演义的作者的手机号,先查书,再正向查到作者,在正向查手机号
# 总结:基于对象的查询都是子查询,这里可以用django配置文件自动打印sql语句的配置做演示
基于对象的表查询
正向
# 查询书籍是三国演义的出版社邮箱
book_obj = models.Book.objects.filter(title='三国演义').first()
print(book_obj.publish.email)
# 查询书籍是水浒传的作者的姓名
book_obj = models.Book.objects.filter(title='水浒传').first()
print(book_obj.authors) # app01.Author.None
print(book_obj.authors.all())
# 查询作者为jason电话号码
user_obj = models.Author.objects.filter(name='jason').first()
print(user_obj.authordetail.phone)
反向
# 查询出版社是东方出版社出版的书籍 一对多字段的反向查询
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
print(publish_obj.book_set) # app01.Book.None
print(publish_obj.book_set.all())
# 查询作者jason写过的所有的书 多对多字段的反向查询
author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.book_set) # app01.Book.None
print(author_obj.book_set.all())
# 查询作者电话号码是110的作者姓名 一对一字段的反向查询
authordetail_obj = models.AuthorDetail.objects.filter(phone=110).first()
print(authordetail_obj.author.name)
基于双下划线的查询
正向示例
# 查询书籍为三国演义的出版社地址
res = models.Book.objects.filter(title='三国演义').values('publish__addr','title')
print(res)
# 查询书籍为水浒传的作者的姓名
res = models.Book.objects.filter(title='水浒传').values("authors__name",'title')
print(res)
# 查询作者为jason的家乡
res = models.Author.objects.filter(name='jason').values('authordetail__addr')
print(res)
反向示例
# 查询南方出版社出版的书名
res = models.Publish.objects.filter(name='南方出版社').values('book__title')
print(res)
# 查询电话号码为120的作者姓名
res = models.AuthorDetail.objects.filter(phone=120).values('author__name')
print(res)
# 查询作者为jason的写的书的名字
res = models.Author.objects.filter(name='jason').values('book__title')
print(res)
# 查询书籍为三国演义的作者的电话号码
res = models.Book.objects.filter(title='三国演义').values('authors__authordetail__phone')
print(res)
反向示例
练习:
# 查询jason作者的手机号
# 正向
res = models.Author.objects.filter(name='jason').values('authordetail__phone')
print(res)
# 反向
res = models.AuthorDetail.objects.filter(author__name='jason').values('phone')
print(res)
# 查询出版社为东方出版社的所有图书的名字和价格
# 正向
res = models.Publish.objects.filter(name='东方出版社').values('book__title','book__price')
# print(res)
反向
# res = models.Book.objects.filter(publish__name='东方出版社').values('title','price')
print(res)
# 查询东方出版社出版的价格大于400的书
# 正向
res = models.Publish.objects.filter(name="东方出版社",book__price__gt=400).values('book__title','book__price')
print(res)
# 反向
res = models.Book.objects.filter(price__gt=400,publish__name='东方出版社').values('title','price')
print(res)
在查询的时候先把orm查询语句写出来,再看用到的条件是否在当前表内,在就直接获取,不在就按照正向按字段反向按表名小写来查即可。切忌一口吃成胖子。
聚合查询(aggregate)
需要先导入模块:
from django.db.models import Max,Min,Count,Sum,Avg
示例
# 查询所有书籍的作者个数
res = models.Book.objects.filter(pk=3).aggregate(count_num=Count('authors'))
print(res)
# 查询所有出版社出版的书的平均价格
res = models.Publish.objects.aggregate(avg_price=Avg('book__price'))
print(res) # 4498.636
# 统计东方出版社出版的书籍的个数
res = models.Publish.objects.filter(name='东方出版社').aggregate(count_num=Count('book__id'))
print(res)
分组查询(annotate)
# 统计每个出版社出版的书的平均价格
res = models.Publish.objects.annotate(avg_price=Avg('book__price')).values('name','avg_price')
print(res)
# 统计每一本书的作者个数
res = models.Book.objects.annotate(count_num=Count('authors')).values('title','count_num')
print(res)
# 统计出每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
print(res)
# 查询每个作者出的书的总价格
res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price')
print(res)
分组查询例子