Django-ORM操作
一、orm概述
1 orm:对象关系映射(跟语言无关)
数据库中的表 ----》对应程序的一个类
数据库中的一行数据----》对应程序中的一个对象
2 python中常见orm框架
-django的orm框架
-sqlachemy的orm框架
3 java:(扩展),java中写web项目
ssh框架 :spring+struts(有漏洞)+hibernate(orm框架)(很早的年代)
ssm框架:spring+springmvc+mybatis(orm框架,可以写原生sql)
springboot:sb框架 ,tomcat内置进去了
springcloud:微服务
4 orm能干的事
-创建表(不能创建数据库,手动创建数据库)
-增加删除表内字段
-增删查改数据
二、orm简单使用
1.配置文件
虽然使用orm对应与许多的数据库,但是因为我们一般是使用mysql数据库,所以需要在settings中配置,不然默认使用sqlite。若使用sqlite跳过这一步直接从第二步开始。
1.
#settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'day62',
'HOST': '127.0.0.1',
'PORT': 3306,
'USER': 'root',
'PASSWORD':'123'
# 下面两个暂时不用加,也不用管
'ATOMIC_REQUEST': True,
'OPTIONS': {
"init_command": "SET storage_engine=MyISAM",
}
}
}
2.django默认情况链接mysql,用的驱动是mysqldb模块,python 3.x以后,这个模块用不了了,咱们用的全都是pymysql,需要做个替换
# app的__init__.py 中写以下代码,其实随便在哪写,但是规范是在这写
# 因为对于django项目,在运行的时候,所有文件的代码都会运行,所以无所谓在哪写,公司里可能会换地方写
import pymysql
pymysql.install_as_MySQLdb()
2.利用orm创建表
# 第一步在models中写要给类
class Book(models.Model):
# 如果不写id,会默认一个id,并且自增
# primary_key=True 表示该字段是主键,一个表中只能有一个主键
id = models.AutoField(primary_key=True)
# varchar类型,长度,
# 字段是否可以为空:null=True,可以为空
# 默认值:default='未知书名',如果没传,默认是它
# 设置索引:db_index=True 表示该字段是辅助索引
# 是否唯一:unique=True 表示唯一
name=models.CharField(max_length=32,null=True,default='未知书名',db_index=True,unique=True)
# float类型
# max_digits 最大长度是5 4567.5
# decimal_places=2 小数点后两位 23.56 999.99
price=models.DecimalField(max_digits=5,decimal_places=2)
# DateTimeField年月日时分秒
# auto_now=True 新增,默认使用当前时间
# auto_now_add=True 修改,设置当前时间,这两者只能设置一种
publish_date=models.DateTimeField(auto_now=True)
'''
auto_now无论是你添加还是修改对象,时间为你添加或者修改的时间。
auto_now_add为添加时的时间,更新对象时不会有变动。
'''
publish=models.CharField(max_length=32)
# 第二步,把表创建出来(执行两个命令)
-python3 manage.py makemigrations # 这条命令会在migrations创建一条记录,数据库变更记录
-python3 manage.py migrate # 把更改同步到数据库
3.orm单表增加
from app01 import models
# 注册功能
def register(request):
if request.method == 'GET':
return render(request, 'register.html')
elif request.method == 'POST':
# 获取数据
res = request.POST
name = res.get('name')
password = res.get('password')
gender = res.get('gender')
province = res.get('province')
# 通过orm存到数据库
# 方式一
user=models.UserInfo(name=name,password=password,gender=gender,province=province)
user.save()
# 方式二
user=models.UserInfo.objects.create(name=name,password=password,gender=gender,province=province)
# 注册完成直接跳转到用户列表页面
return redirect('/userlist')
4.orm单表查询
# 查看目前注册的用户
def user_list(request):
# 通过orm查询所有用户,返回列表,内部有一个一个的user对象 [user1,user2,user3]
userlist=models.UserInfo.objects.all()
return render(request, 'userlist.html',context={'userlist':userlist})
def select_user(request):
# 查询名字叫xxx的人(是个列表:QuerySet)
res = models.UserInfo.objects.filter(name='xxx')
res = models.UserInfo.objects.filter(name='xxx')[0]
res = models.UserInfo.objects.filter(name='xxx').first()
# 利用get方法查询名字叫xxx的人(UserInfo),如果没有或者由多个,都报错
# 查询结果必须有且仅有一个才正常,否则报错
res=models.Book.objects.get(name='xxx')
三、常用和非常用字段和参数概览
1.常用字段
# 最为常用
IntegerField 整数
AutoField
BooleanField
CharField
DateField
DateTimeField
DecimalField
FileField 上传文件,本质是varchar
ImageField 图片,本质是varchar,继承了FileField
TextField 存大文本
EmailField 本质是varchar
# 详细
AutoField(Field)
- int自增列,必须填入参数 primary_key=True
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models
class UserInfo(models.Model):
# 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32)
class Group(models.Model):
# 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647
BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
自定义无符号整数字段
class UnsignedIntegerField(models.IntegerField):
def db_type(self, connection):
return 'integer UNSIGNED'
PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',
BooleanField(Field)
- 布尔值类型
NullBooleanField(Field):
- 可以为空的布尔值
CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度
TextField(Field)
- 文本类型
EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制
IPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
GenericIPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
- 参数:
protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要protocol="both"
URLField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证 URL
SlugField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
CommaSeparatedIntegerField(CharField)
- 字符串类型,格式必须为逗号分割的数字
UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
FilePathField(Field)
- 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
- 参数:
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
DateTimeField(DateField)
- 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD
TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]
DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
FloatField(Field)
- 浮点型
DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度
BinaryField(Field)
- 二进制类型
2.常用参数
(1)null
如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.
(2)blank
如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。
(3)default
字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用。
(4)primary_key
如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,
否则没必要设置任何一个字段的primary_key=True。
(5)unique
如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的
(6)choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。如果设置了choices,默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices中的选项。
3.元信息
class UserInfo(models.Model):
nid = models.AutoField(primary_key=True)
username = models.CharField(max_length=32)
class Meta:
# 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
db_table = "table_name"
# 联合索引
index_together = [
("pub_date", "deadline"),
]
# 联合唯一索引
unique_together = (("driver", "restaurant"),)
# admin中显示的表名称
verbose_name = '用户信息表'
# 只写上面一行的话verbose_name会加s,即用户信息表s,因为在外国s代表复数,如果要完全改过来写下面这句
verbose_name_plural = '用户信息表'
# 一般这两句是连用的,但是真正起效果的是下面那句
四、打印原生sql
有时候为了我们方便排查需要打印出原生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',
},
}
}
五、查询表记录API
<1> all(): 查询所有结果
<2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象
<3> get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
<4> exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象,即排除
<5> order_by(*field): 对查询结果排序,如果要对某个字段逆排序,可以在字段前加-号('-id')
<6> reverse(): 对查询结果反向排序,相当于上面加-号
<7> count(): 返回数据库中匹配查询(QuerySet)的对象数量。count操作是在数据库的时候就统计好的,而不是拿到结果再用python统计
<8> first(): 返回第一条记录,底层相当于asc,limit1
<9> last(): 返回最后一条记录,底层相当于desc,limit1
<10> exists(): 如果QuerySet包含数据,就返回True,否则返回False
<11> values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列
<12> values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
<13> distinct(): 从返回结果中剔除重复纪录
六、基于双下划线的模糊查询
# 1 价格在[100,200,300]这个范围内
Book.objects.filter(price__in=[100,200,300])
# 2 大于,小于,大于等于,小于等于
Book.objects.filter(price__gt=100)
Book.objects.filter(price__lt=100)
Book.objects.filter(price__gte=100)
Book.objects.filter(price__lte=100)
# 3 范围
Book.objects.filter(price__range=[100,200])
# 4 包含
Book.objects.filter(title__contains="python")
# 5 忽略大小写包含
Book.objects.filter(title__icontains="python")
# 6 以xx开头
Book.objects.filter(title__startswith="py")
# 7 时间类型,年份是2012年的
Book.objects.filter(pub_date__year=2012)
七、删除表记录
# 第一种:queryset的delete方法
res=models.Book.objects.all().delete()
print(res)
# 第二种:对象自己的delete方法
book = models.Book.objects.all().filter(name='金梅').first()
print(type(book))
res=book.delete()
八、修改表记录
# 第一种:queryset的update方法
res=models.Book.objects.filter(publish='东京').update(name='金梅1')
print(res)
# 第二种:对象自己的
book = models.Book.objects.filter(name='xxx').last()
book.name='asdfasd'
book.save()
九、python脚本中调用django环境
可以让我们不需要打开浏览器输入数据,方便测试
# 在脚本中调用djagno服务
import os
if __name__ == '__main__':
#1 引入django配置文件
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day67.settings')
# 2 让djagno启动
import django
django.setup()
# 3 使用表模型
from app01 import models
models.Book.objects.create(name='测试书籍',publish='xx出版社')
十、多表操作
1.多表模型创建注意点
多表因为要创建外键或者对应关系,所以和单表的创建有些许不同
1 图书表:book,作者表:author,作者详情表:authordetail,出版社表:publish,(第三张中间表)
2 作者跟作者详情:是一对一,关联字段写在哪一方都可以
3 图书跟出版社:是一对多,一对多关系一旦确立,关联字段写在多的一方
4 图书和作者:是多对多,多对多的关系需要建立第三张表(可以自动生成)
5 models.py中把关系建立出来
from django.db import models
### django版本:目前使用1.11.x或者12.0.x,使用django3会出问题
class Publish(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
phone = models.CharField(max_length=64)
email = models.EmailField()
class Book(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=6, decimal_places=2)
publish_date = models.DateTimeField(auto_now_add=True)
read_num=models.IntegerField(default=0)
commit_num=models.IntegerField(default=0)
# to='Publish'跟Publish表做关联(ForeignKey,一对多)
# to_field='id'跟哪个字段做关联
# publish=models.CharField(max_length=32)
# publish=models.ForeignKey(to='Publish',to_field='id')
# publish = models.ForeignKey(to='Publish') # 不写的话,默认跟主键做关联
publish = models.ForeignKey(to=Publish)
# 自动创建出第三张表(这句话会自动创建第三张表)
# authors在数据库中不存在该字段,没有to_field
# 默认情况:第三张表有id字段,还有Book表的id和Author表的id字段
authors=models.ManyToManyField(to='Author')
class Author(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.SmallIntegerField()
# 一对一的本质是 ForeignKey+unique
author_detail=models.OneToOneField(to='AuthorDetail',to_field='id')
# author_detail=models.ForeignKey(to='AuthorDetail',to_field='id',unique=True)
class AuthorDetail(models.Model):
id = models.AutoField(primary_key=True)
sex = models.SmallIntegerField()
addr = models.CharField(max_length=64)
phone = models.BigIntegerField()
6 同步到mysql数据库
-配置文件
-pymysql.install_as_mysqldb()
-g的mysqlclient
-两条命令
7 2.x版本的django
-外键字段必须加参数:on_delete
-1.x版本不需要,默认就是级联删除
-假设,
删除出版社,该出版社出版的所有图书也都删除,on_delete=models.CASCADE
删除出版社,该出版社出版的图书不删除,设置为空on_delete=models.SET_NULL,null=True
删除出版社,该出版社出版的图书不删除,设置为默认on_delete=models.SET_DEFAULT,default=0
2.一对多添加记录
当创建了外键关系之后,我们在插入表格的时候,被关联的表格需要先有数据,然后才能在多关系的表格里加数据,如下的book表,publish字段需要和publish表的id字段关联。
publish=models.Publish.objects.create(name='北京出版社',addr='北京',phone='0536-12345678',email='邮箱地址')
# 新增图书三种方式
book=models.Book.objects.create(name='金梅',price='23.45',publish=publish)# publish=对象
book=models.Book.objects.create(name='西游记',price='23.55',publish_id=1)# publish_id=数字
book=models.Book.objects.create(name='西游记',price='23.55',publish_id=publish.id)# publish_id=数字
# 总结:
1 email可以不传email,本质就是varchar(admin中会判断)
2 新增图书:
-publish=publish
-publish_id=publish.id
3 写在表模型中的publish字段,到数据库中会变成publish_id(ForeignKey)
4 查到book对象以后
-book.publish 对象
-book.publish_id id号,数字
3.多对多添加记录,修改,删除
自动创建的表,表模型就拿不到,就如同我们在book类里创建的authors。book.authors代指表模型
# 多对多,作者和书
# 给西游记这本书新增两个作者lqz和egon
# 去到西游记这本书
book=models.Book.objects.get(name='西游记')
# 代指中间表book.authors
lqz=models.Author.objects.get(id=2)
egon=models.Author.objects.get(id=3)
# 新增作者需要传id,有以下三种方式
book.authors.add(2,3) # 新增作者,通过id新增
book.authors.add(lqz,egon) # 新增作者,通过对象新增
book.authors.add(2,egon) # 新增作者,通过对象新增
# 西游记删除一个作者
book = models.Book.objects.get(name='西游记')
book.authors.remove(2)
egon = models.Author.objects.get(id=3)
book.authors.remove(egon)
# clear 清空所有作者
book = models.Book.objects.get(name='西游记')
book.authors.clear()
# set先清空,再add,前提是不存在的作者,即如果这个作者原来在表里,又使用set,原存在的数据不会被删除,然后把不存在的数据加进去
book.authors.set([4,])
# add ,remove,set clear
十一、多表操作之查询
跨表查询有两种方式
-基于对象的跨表查询:相当于我们原生sql子查询
-基于双下划线的跨表查询:相当于我们原生sql的关联查询,连表查询(就是left join等)
1.基于对象的跨表查询
基于对象的跨表查询,先查对象,通过对象再去查另一个对象(正向:字段名,反向:表名小写/表名小写_set.all())
正向查询:最后查出来的对象有我们需要的属性,那么直接,对象.字段名
反向查询:最后查出来的对象没有我们需要的属性,而是与我们想要的字段有关联(比如外键关系),那么我们可以,对象.类名小写.属性,获取单个数据,对象.类名小写_set.all(),获取多个数据返回的列表。
技巧:熟练后,不需要管什么正向反向,能点出属性就直接点出来,不能的话就点类名小写,再看数据是一条还是多条。
一对多
# 查询主键为1的书籍的出版社所在的城市
# 先查主键为1的书,下面有两种方式
book=models.Book.objects.get(id=1) # 第一次查询
book=models.Book.objects.filter(id=1).first() # 第一次查询
publish=book.publish # 第二次查询,内部又执行了一次查询,根据publish_id查询publish
print(publish.addr)
# 北京出版社出版的所有书籍,反向
publish=models.Publish.objects.get(name='北京出版社') # 第一次查询了出版社
books=publish.book_set.all() # 表名小写_set,第二次,根据出版社id,查询所有书
print(books)
# 地址为山东的作者写的所有书
author_detail=models.AuthorDetail.objects.get(addr='山东')
author=author_detail.author
books=author.book_set.all()
print(books[0].name)
# 正向查询:book表内有publish字段 直接对象.字段名
# 反向查询:publish表内没有book字段,出版社对象.Book小写_set.all()或者Book直接小写。
一对一
# 查询所有住址在山东的作者的姓名,反向
# 反向查询:author_detail没有author字段,需要使用author_detail.表名小写
author_detail=models.AuthorDetail.objects.filter(addr__contains='山东').first()
print(author_detail.author.name)
# 查询egon作者的地址,正向
author=models.Author.objects.get(name='egon')
print(author.author_detail.addr)
多对多
# 金梅所有作者的名字以及手机号,正向
book=models.Book.objects.get(name='金梅')
authors=book.authors.all()
for author in authors:
print(author.name)
print(author.author_detail.phone)
# 查询egon出过的所有书籍的名字,反向
egon=models.Author.objects.get(name='egon')
books=egon.book_set.all()
for book in books:
print(book.name)
2.基于双下划线的跨表查询
一对多
# 格式 models.类型.objects.filter,values/values_list(写 __ 跨表)
# 查询北京出版社出版过的所有书籍的名字与价格(一对多)
res=models.Publish.objects.filter(name='北京出版社').values('book__name','book__price')
print(res)
res=models.Book.objects.filter(publish__name='北京出版社').values('name','price')
print(res)
多对多
# 查询egon出过的所有书籍的名字,价格(多对多)
# 反向
res=models.Author.objects.filter(name='egon').values('book__name','book__price')
print(res)
# 正向
res=models.Book.objects.filter(authors__name='egon').values('name','price')
print(res)
# 查询egon的手机号
res=models.Author.objects.filter(name='egon').values('author_detail__phone')
print(res)
res=models.AuthorDetail.objects.filter(author__name='egon').values('phone')
print(res)
3.进阶连续跨表查询
# 查询北京出版社出版过的所有书籍的名字以及作者的姓名
res=models.Publish.objects.filter(name='北京出版社').values('book__name','book__authors__name')
print(res)
res=models.Book.objects.filter(publish__name='北京出版社').values('name','authors__name')
print(res)
res=models.Author.objects.filter(book__publish__name='北京出版社').values('book__name','name')
print(res)
# 手机号以189开头的作者出版过的所有书籍名称以及出版社名称
res=models.AuthorDetail.objects.filter(phone__startswith='189').values('author__book__name','author__book__publish__name')
print(res)
res = models.Author.objects.filter(author_detail__phone__startswith='189').values('book__name','book__publish__name')
print(res)
十二、聚合查询
聚合查询包括:Count,Avg,Min,Max,Sum,和我们原生sql中的聚合函数是一样的。同时使用也相同,我们在使用了聚合函数之后,只能拿到聚合的字段。
aggregate()
是QuerySet
的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
from django.db.models import Avg,Max,Min,Count,Sum
# 要用aggregate药包裹聚合函数,当aggregate结束,已经不是queryset对象了,我们也可以在aggrepate里用as起别名
# 1 计算所有图书的平均价格
book=models.Book.objects.all().aggregate(Avg('price'))
book=models.Book.objects.all().aggregate(avg=Avg('price'))
# 2 计算总图书数
book = models.Book.objects.all().aggregate(count=Count('id'))
# 3 计算最低价格的图书
book = models.Book.objects.all().aggregate(min_price=Min('price'))
# 4 计算最大价格图书
book = models.Book.objects.all().aggregate(max_price=Max('price'))
# 5 多个查询
Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
十三、分组查询
分组查询,即原生sql里的group by。我们经常是分组和聚合函数搭配使用,如果是单独使用聚合函数,那么就是用aggregate()包括聚合函数,如果要和分组搭配使用,就需要annotate()包裹。
annotate的返回值是querySet,如果不想遍历对象,可以用上value_list:
queryResult= Publish.objects.annotate(MinPrice=Min("book__price")).values_list("name","MinPrice")
print(queryResult)
'''
使用说明:
annotate() 内写聚合函数
values在前表示group by的字段
values在后表示取某几个字段
filter在前表示where
filter在后表示having
pk 代指主键
如果没有指定group by的字段,默认就用基表(Publish)主键字段作为group by的字段
查询每个作者的名字,以及出版过书籍的最高价格(建议使用分组的表作为基表)
如果不用分组的表作为基表,数据不完整可能会出现问题
'''
from django.db.models import Avg, Count, Max, Min
# 示例一:查询每一个出版社id,以及出书平均价格
select publish_id,avg(price) from app01_book group by publish_id;
ret=models.Book.objects.values('publish_id').annotate(avg=Avg('price')).values('publish_id','avg')
print(ret)
# 查询出版社id大于1的出版社id,以及出书平均价格
select publish_id,avg(price) from app01_book where publish_id>1 group by publish_id;
ret=models.Book.objects.values('publish_id').filter(publish_id__gt=1).annotate(avg=Avg('price')).values('publish_id','avg')
print(ret)
# 查询出版社id大于1的出版社id,以及出书平均价格大于30的
select publish_id,avg(price)as aaa from app01_book where publish_id>1 group by publish_id HAVING aaa>30;
ret = models.Book.objects.values('publish_id').filter(publish_id__gt=1).annotate(avg=Avg('price')).filter(avg__gt=30).values('publish_id', 'avg')
print(ret)
# 查询每一个出版社出版的书籍个数
# pk 代指主键
ret=models.Publish.objects.values('pk').annotate(count=Count('book__id')).values('name','count')
print(ret)
# 如果没有指定group by的字段,默认就用基表(Publish)主键字段作为group by的字段.即上面可缩写为
ret=models.Publish.objects.annotate(count=Count('book__id')).values('name','count')
print(ret)
# 另一种方式实现
ret=models.Book.objects.values('publish').annotate(count=Count('id')).values('publish__name','count')
print(ret)
# 查询每个作者的名字,以及出版过书籍的最高价格(建议使用分组的表作为基表)
# 如果不用分组的表作为基表,数据不完整可能会出现问题
ret=models.Author.objects.values('pk').annotate(max=Max('book__price')).values('name','max')
ret = models.Author.objects.annotate(max=Max('book__price')).values('name', 'max')
ret= models.Book.objects.values('authors__id').annotate(max=Max('price')).values('authors__name','max')
print(ret)
# 查询每一个书籍的名称,以及对应的作者个数
ret=models.Book.objects.values('pk').annotate(count=Count('authors__id')).values('name','count')
ret=models.Book.objects.annotate(count=Count('authors__id')).values('name','count')
ret=models.Author.objects.values('book__id').annotate(count=Count('id')).values('book__name','count')
print(ret)
#统计不止一个作者的图书
ret=models.Book.objects.values('pk').annotate(count=Count('authors__id')).filter(count__gt=1).values('name','count')
ret = models.Author.objects.values('book__id').annotate(count=Count('id')).filter(count__gt=1).values('book__name', 'count')
print(ret)
# 统计价格数大于10元,作者的图书
ret = models.Book.objects.values('pk').filter(price__gt=10).annotate(count=Count('authors__id')).values('name', 'count')
print(ret)
#统计价格数大于10元,作者个数大于1的图书
ret = models.Book.objects.values('pk').filter(price__gt=10).annotate(count=Count('authors__id')).filter(count__gt=1).values('name', 'count')
print(ret)
ret = models.Book.objects.filter(price__gt=10).annotate(count=Count('authors__id')).filter(count__gt=1).values('name', 'count')
print(ret)
十四、F和Q查询
F查询:取出数据库的某个字段的值,因为
Q查询:制造"与或非"的条件
# F查询:取出数据库的某个字段的值
from django.db.models import F
# 把read_num都加1
ret=models.Book.objects.all().update(read_num=F('read_num')+1)
print(ret)
#查询评论数大于阅读数的书籍
ret=models.Book.objects.all().filter(commit_num__gt=F('read_num'))
for i in ret:
print(i.name)
# 查询评论数大于阅读数2倍的书籍
ret=models.Book.objects.filter(commit_num__gt=F('read_num')*2)
print(ret)
# Q查询:制造"与或非"的条件:& | ~
from django.db.models import Q
# 查询名字叫egon或者价格大于100的书
ret=models.Book.objects.filter(Q(name='egon') | Q(price__gt=100))
# 查询名字叫egon并且价格大于100的书,并且的用法有两种方式,一般用第二种,和之前的写法一样
ret=models.Book.objects.filter(Q(name='egon') & Q(price__gt=100))
ret=models.Book.objects.filter(name='egon',price__gt=100)
# 查询名字不为egon的书
ret = models.Book.objects.filter(~Q(name='egon'))
print(ret)
# Q可以嵌套,嵌套就可以使用很多条件的判断
ret = models.Book.objects.filter((Q(name='egon') & Q(price__lt=100)) | Q(id__lt=3))
print(ret)
十五、原生sql
# 原生sql(有些sql用orm写不出来,因为水平不到位哈哈)
# 第一种:用的比较少
from django.db import connection
cursor = connection.cursor()
cursor.execute("""SELECT * from app01_book where id = %s""", [1])
row = cursor.fetchone()
row = cursor.fetchall()
print(row)
# 第二种,用的多
books=models.Book.objects.raw('select * from app01_book where id >3')
print(books)#RawQuerySet对象
for book in books:
print(book.name)
books=models.Book.objects.raw('select * from app01_publish')
for book in books:
print(book.__dict__)
print(book.name)
print(book.addr)
print(book.email)
print(book.price)
authors = models.Author.objects.raw('SELECT app01_author.id,app01_author. NAME,app01_authordetail.sex FROM app01_author JOIN app01_authordetail ON app01_author.author_detail_id = app01_authordetail.id WHERE app01_authordetail.sex = 1')
for author in authors:
print(author.name)
print(author.__dict__)
十六、defer和only
# defer和only(查询优化相关)
# only保持是book对象,但是只能使用only指定的字段
books = models.Book.objects.all().only('name')
print(books[0].name)
print(books[0].price) # 能出来,但是要再去数据库查一遍
books = models.Book.objects.all().only('name')
print(books[0].__dict__)
books = models.Book.objects.all().defer('name','price')
print(books[0].__dict__)
十七、事务(请求,装饰器,局部)
# 事物:ACID,事物的隔离级别,锁, 行级锁,表级锁
# djanog orm中使用事物:原子性操作,要么都成功,要么都失败
# 例子:新增一个作者详情,新增一个作者,这两者应该同时成功,不可能有作者详情而没有作者,反之亦然
# 事物的三个粒度
# 1 局部使用(常用)
from django.db import transaction
with transaction.atomic(): # 都在事物中,要么都成功,要么都失败
author_detail = models.AuthorDetail.objects.create(addr='xxx', phone='123', sex=1)
# raise Exception('抛了异常')
author = models.Author.objects.create(name='llqz', age=19, author_detail=author_detail)
# 2 在一个视图中使用,视图函数装饰器,这一个视图函数都在一个事物中(偶尔使用)
@transaction.atomic
def index(request):
return HttpResponse('ok')
# 3 整个http请求,在事物中,在setting.py中配置(基本不用)
DATABASES = {
'default': {
...
'PORT': 3306,
'ATOMIC_REQUEST': True,
}
}
'ATOMIC_REQUEST': True,
# 设置为True统一个http请求对应的所有sql都放在一个事务中执行,百分之九十九不会用这个,因为把整个http请求放进事务太消耗资源,且很多情况下,对于一个http请求我们需要有不同的操作
补充
1.时区和国际化问题
setting.py中
1 后台管理汉语问题
LANGUAGE_CODE = 'zh-hans' # 管理后台看到的就是中文
2 时区问题(使用东八区)
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False
2.django admin
0 管理后台是django提供的可以快速对表进行增删查改操作
1 创建一个后台管理账号
python3 manage.py createsuperuser
输入用户名
输入邮箱(可以不填,敲回车)
输入密码
确认密码
# 超级用户创建出来了,可以登录管理后台了
2 admin中表中一行一行的数据显示我们定制的样子
重写模型类的__str__方法
3 创建完账号后,需要将表注册到后台中
在app下的admin.py中写
from app01 import models
# 把book表注册,管理后台就能看到了
admin.site.register(models.Book)
4 更改时区,这样django后台就变成了中文
3.blank参数作用
1 需要把book表注册到admin中
在app下的admin.py中写
from app01 import models
# 把book表注册一些,管理后台就能看到了
admin.site.register(models.Book)
2 可以快速的对book表进行增删查改操作
4.看一看这篇博客
https://www.cnblogs.com/nokiaguy/p/13803370.html
5.安装模块相关
pip3 install django
# 本质是去https://pypi.python.org/simple,搜这个模块,会根据你的平台下载在一个安装包(windows平台是whl),下载完,再安装
# pip安装失败的情况
# 我们可以绕过它,有了whl文件以后,自己装
# https://www.lfd.uci.edu/~gohlke/pythonlibs/#opencv
pip3 install django.whl
# 官方库没有上传到pypi,官方也不给制作whl文件
#如何安装 包 (setup.py)
到达安装目录,setup.py所在的目录
python setup.py build
python setup.py install
# 配置清华源,豆瓣源,本质是
豆瓣源会把pypi,包拉到自己的服务器上,以后你再下,去它的服务器上下,所以速度快
# 你自己写的包,如何上传到pypi上给别人使用?
6.外键关系要不要建立
1 关联字段与外键约束没有必然的联系(建管理字段是为了进行查询,建约束是为了不出现脏数据)
2 默认情况,关联关系建好以后,外键约束就自然建立了
3 实际工作中,外键约束一般不建(影响效率),都是人为约束(代码约束)
-db_constraint=False
4 表模型和数据库表的对应,不要直接修改表(这是可以的,但是不建议),要修改表模型,同步到表中
练习
1.链式调用
queryset就是链式调用的使用),实现一个可以支持链式调用的类
class MyClass:
def func1(self):
print('func1')
return self
def func2(self):
print('func2')
return self
obj = MyClass()
obj.func1().func2()
(重点)2.利用orm的多表查询完成如下功能
基于对象,基于双下滑下)
# 查找所有书名里包含红楼的书
res = models.BookInfo.objects.filter(name__contains='红楼')
# 查找出版日期是2017年的书
res = models.BookInfo.objects.filter(publish_time__year=2017)
# 查找出版日期是2017年的书名
books = models.BookInfo.objects.filter(publish_time__year=2017)
for book in books:
print(book.name)
# 查找价格大于10元的书
res = models.BookInfo.objects.filter(price__gt=10)
# 查找价格大于10元的书名和价格
res = models.BookInfo.objects.filter(price__gt=10)
for i in res:
print(i.name,i.price)
# 查找在北京的出版社
res = models.Publish.objects.filter(addr='北京')
# 查找名字以沙河开头的出版社
res = models.Publish.objects.filter(name__startswith='沙河')
# 查找作者名字里面带“小”字的作者
res = models.Author.objects.filter(name__contains='小')
# 查找年龄大于30岁的作者
res = models.Author.objects.filter(age__gt=30)
# 查找手机号是155开头的作者
res = models.AuthorDetail.objects.filter(phone__startswith='155')
res1 = res.author_set.all()
# 查找手机号是155开头的作者的姓名和年龄
res = models.AuthorDetail.objects.filter(phone__startswith='155')
res1 = res.author_set.all()
for i in res1:
print(i.name,i.age)
# 查找书名是“红楼梦”的书的出版社
res = models.BookInfo.objects.filter(name='红楼梦')
res1 = res.publish
# 查找书名是“红楼梦”的书的出版社所在的城市
res = models.BookInfo.objects.filter(name='红楼梦')
res1 = res.publish.addr
res2 = res1.addr
# 查找书名是“红楼梦”的书的出版社的名称
res = models.BookInfo.objects.filter(name='红楼梦')
res1 = res.publish
res2 = res1.name
# 查找书名是“红楼梦”的书的所有作者
res = models.BookInfo.objects.filter(name='红楼梦')
res1 = res.author_set.all()
# 查找书名是“红楼梦”的书的作者的年龄
res = models.BookInfo.objects.filter(name='红楼梦')
res1 = res.author_set.all()
for i in res1:
print(i.age)
# 查找书名是“红楼梦”的书的作者的手机号码
res = models.BookInfo.objects.filter(name='红楼梦')
res1 = res.author_set.all()
res2 = res1.authordetail.phone
# 查找书名是“红楼梦”的书的作者的地址
res = models.BookInfo.objects.filter(name='红楼梦')
res1 = res.author_set.all()
res2 = res1.authordetail.addr
# 查找书名是“红楼梦”的书的作者的邮箱
res = models.BookInfo.objects.filter(name='红楼梦')
res1 = res.author_set.all()
res2 = res1.authordetail.email
3.完成如下功能
1 查询老男孩出版社出版过的价格大于200的书籍
res = models.BookInfo.objects.filter(price__gt = 200).first()
2 查询2017年8月出版的所有以py开头的书籍名称
res = models.BookInfo.objects.filter(publish_time__year = 2017).filter(name__startswith = 'py')
3 查询价格为50,100或者150的所有书籍名称及其出版社名称
4 查询价格在100到200之间的所有书籍名称及其价格
5 查询所有人民出版社出版的书籍的价格(从高到低排序,去重)
4.整理
查询api--14个方法中,哪些返回值是queryset对象,哪些不是queryset对象,是什么?
5.登录功能,连接mysql
前端页面略
路由
url(r'^login', views2.login),
url(r'^index', views2.index),
url(r'^test', views.test),
视图函数
def index(request):
return render(request, 'index2.html')
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
name = request.POST.get('name')
password = request.POST.get('password')
# 创建一个数据库链接
conn = pymysql.connect(host='127.0.0.1', user='root', password='123', database='userinfo', port=3306, )
# 拿到一个游标
cursor = conn.cursor()
# 执行sql
cursor.execute('select * from user where name=%s and password=%s ', (name, password))
# 获取结果
ret = cursor.fetchone()
print(ret)
if ret:
return redirect('/index')
else:
return HttpResponse('用户名或密码错误')
6.(重点)完成如下查询
import os
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE','django6.settings')
import django
django.setup()
from app01 import models
from django.db.models import Count, Max, Min,Avg,Sum,F,Q
# 1统计每一本书作者个数
ret = models.Book.objects.values("id").annotate(count = Count('authors__id')).values("name","count")
print(ret)
# 2统计每一个出版社的最便宜的书
ret = models.Publish.objects.annotate(min_price = Min('book__name')).values("name",'min_price')
print(ret)
# 3统计每一本以py开头的书籍的作者个数:
ret = models.Book.objects.filter(name__startswith = 'book').annotate(count=Count('authors__id')).values("name", 'count')
print(ret)
# 4作者数量大于2的图书名字和价格
ret = models.Book.objects.annotate(count = Count('authors__id')).filter(count__gt = 2).values('name','price')
print(ret)
# 5根据一本图书作者数量的多少对查询集QuerySet进行排序:
ret = models.Book.objects.annotate(count=Count('authors__id')).values('name','count').order_by('count')
print(ret)
# 6查询各个作者出的书的总价格
ret = models.Author.objects.annotate(max_price=Max('book__price')).values('name','max_price')
print(ret)
# 7查询每个出版社的名称和书籍个数
ret = models.Publish.objects.annotate(count=Count('book__id')).values('name', 'count')
print(ret)
# 8查询所有书籍的平均价格
ret = models.Book.objects.aggregate(avg_price=Avg('price'))
print(ret)
# 9统计图书平均价格和最大价格
ret = models.Book.objects.aggregate(avg_price=Avg('price'),max_price = Max('price'))
print(ret)
# 10统计图书总个数和平均价格
ret = models.Book.objects.aggregate(count_price=Count('pk'),avg_price=Avg('price')).
print(ret)
# 11查询评论数小于等于阅读数2倍的书籍
ret = models.Book.objects.filter(commit_num__lte=F('read_num')*2)
for book in ret:
print(book.name)
# 12将每一本书的价格提高30元
ret = models.Book.objects.all().update(price = F("price")+30)
print(ret)
# 13将id大于3的每本书价格提高5元
ret = models.Book.objects.filter(id__gt=3).update(price = F("price")+5)
print(ret)
# 14查询名字不是西游,并且价格大于150的书
ret = models.Book.objects.filter(~Q(name='西游') & Q(price__gt = 150))
for book in ret:
print(book.name)
# 15查询名字叫西游并且价格大于150或者id小于等于5的图书
ret = models.Book.objects.filter(Q(name='西游') & Q(price__gt = 150) | Q(id__lte=5))
for book in ret:
print(book.name)
# 16查询名字叫西游或者出版社为北京出版社的图书
ret = models.Book.objects.filter(Q(name='西游') | Q(publish__name = '北京出版社'))
for book in ret:
print(book.name)