【一】前期数据准备
"""
django自带的sqlite3数据库 功能很少 并且针对日期类型不精确
1.数据库正向迁移命令(将类操作映射到表中)
python3 manage.py makemigrations
python3 manage.py migrate
2.数据库反向迁移命令(将表映射成类)--》编程orm的代码
python3 manage.py inspectdb
"""
需求
我们只想操作orm 不想使用网络请求
需要有专门的测试环境
1.自己搭建,那个文件要用但是你又不想运行django,就在该文件上面复制以下代码--》随后就可以写orm的代码
import os
if __name__ == "__main__":
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoProject1.settings')
import django
django.setup()
from app01.models import user//不许换位置
res = user.objects.all()
print(res)
2.pycharm提供
python console
【二】all()和filter()方法
1.res = user.objects.all()查询所有数据 QuerySet可以看成是列表套对象//<QuerySet [<user: user object (1)>, <user: user object (2)>]>
2.res=user.objects.filter()括号里不写筛选条件相当于all
res=user.objects.filter(id="1")筛出id=1的数据,可以写pk自动定位到主键字段
res=user.objects.filter(id="1")[0]支持索引但不推荐,索引不存在会报错
res=user.objects.filter(id="1").first()选择第一条数据,不存在返回none
res=user.objects.filter(id="1",name="llh").first()//支持写多个筛选条件,默认是and的关系
res=user.objects.filter().filter()//只要是QuerySet对象就可以继续.对象方法
res=user.objects.filter(id="1").last()//选择筛出的最后一条数据
【三】orm关键字
1.values()指定字段---》可以看成是结果集对字段的筛选
res=user.objects.filter(user_id=1).values("name")
//<QuerySet [{'name': 'llh'}]>列表套字典
2.values_list()
res=user.objects.filter(user_id=1).values_list("name")//<QuerySet [('llh',)]>列表套元组
3.distict()数据中包含主键不可能去重,一定要是一模一样的数据才可以去重
res=user.objects.filter(user_id=1).values("name").distict()
4.order_by默认升序,支持多个字段
res=user.objects.order_by(id,age)//升序id比不出来就比age
res=user.objects.order_by(-id)//降序
5.exclude不包含
res=user.objects.exclude(name="llh")
6.reverse() 只有在order_by以后有用
res=user.objects.order_by(-id).reverse()
7.count()对结果集计数
res=user.objects.count()
8.exists() 判断有没有结果集
res=user.objects.filter(user_id=1).exists()//有就true,无false
9.get() 直接获取数据对象 不推荐使用,条件不存在直接报错
res=user.objects.get()
【四】双下划线查询
1. __gt大于
__lt小于
__gte大于等于
__lte小于等于
查询年龄大于20的用户
# res = models.User.objects.filter(age__gt=20)
2.__in或的关系,意味着某属性在不在该列表
查询年龄是18、22、25的用户
# res = models.User.objects.filter(age__in=[18, 22, 25])
3.__range针对范围查找
查询年龄在18-25之间的用户
es = models.User.objects.filter(age__range=[18,25])
4.__contains区分大小写
查询姓名中包含j的用户
res =models.User.objects.filter(age__contains='j')
__icontains忽略大小写
5.其他方法补充
"""
__startswith
__endswith
__regex
"""
6.日期相关操作
查询五月的数据__month
res =models.User.objects.filter(tome__month=5)
查22年数据
res =models.User.objects.filter(tome__year=2022)
【五】orm创建外键关系
1.关系种类:1对1,1对多,多对多
2.字段位置:1对多建在多的
多对多放在第三张表上
一对一建在任意一方都可以,推荐建在查询频率搞得表中
3.orm创建表关系
"""
图书表
出版社表
作者表
作者详情表
关系判断
书与出版社
一本书不能对应多个出版社
一个出版社可以对应多本书
书与作者
一本书可以对应多个作者
一个作者可以对应多本书
作者与作者详情
一个作者对应1个作者详情
一个作者详情对应1个作者
"""
models.ForeignKey(to=publish)
# 一对多关系 ---->ForeignKey,默认关联的就是主键了,可以不写pk
# 多对多关系 ---->ManyToManyField,会自动创建第三张表
# 一对一关系 ---->OneToOneField
class Book(Model):
"""图书表"""
ps:三个关键字里面的参数
to用于指定跟哪张表有关系 自动关联主键
to_field\to_fields 也可以自己指定关联字段
"""
ManyToManyField不会在表中创建实际的字段 而是告诉django orm自动创建第三张关系表
ForeignKey、OneToOneField会在字段的后面自动添加_id后缀 如果你在定义模型类的时候自己添加了该后缀那么迁移的时候还会再次添加_id_id 所以不要自己加_id后缀
"""
# 书跟作者进行关联
# 第一种方式 : 调用第三张表进行关联
# 如果使用 的是Django提供的 ManyToManyField 自动创建的第三张表
# 模型表中是没有这个第三张表的 需要跨表建立联系
# 如果是你自己创建的第三章模型表你就可以调用第三张表
# 现在的场景是基于Django自动创建的第三张表
# 【0】获取到书籍对象
book_obj = Book.objects.get(pk=3)
author_obj = Author.objects.get(pk=1)
# 【1】添加多对多外键关系 add
# 给当前书籍添加一个作者
# 获取到当前书籍对象 书记对象在跨到 作者表身上 加上作者的id
# book_obj.author.add(1)
# book_obj.author.add(author_obj)
# 多个外键关系直接放ID即可
# book_obj.author.add(2,3)
# 【2】删除多对多外键关系 remove
# 获取到当前书籍对象 书记对象在跨到 作者表身上 加上作者的id
# book_obj.author.remove(author_obj)
# 【3】修改外键关系
# (1)方式一 借用 set 方法 弊端就是会清空其他的外键关系
# 使用 关键字 set 的时候 参数是一个列表
# 清空当前对象的所有对对多外键关系 再重新关联新的外键关系
# book_obj.author.set([2])
# book_obj.author.set([author_obj])
# (2)方式二 先用 remove 移除外键关系 再用 add 添加外键关系
【六】外键字段的操作
外键是建立外键表的字段=被建表的对象
# 一对多、一对一外键字段操作
(1)增
publish_id=1 author_detail_id=1
publish=publish_obj author_detail=detail_obj
eg:
外键的增加 - 直接写实际字段
models.Book.objects.create(title="三国演义", price=1369.25, publish_id=1)
外键的增加 - 虚拟字段
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title="红楼梦", price=1569.25, publish=publish_obj)
(2)改
直接写实际字段
models.Book.objects.filter(pk=1).update(publish_id=2)
虚拟字段
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.filter(pk=1).update(publish=publish_obj
(3)删除
models.Publish.objects.filter(pk=1).delete()
# 多对多字段操作
1.第三张关系表创建数据
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.add()
括号内可以放主键值也可以放数据对象 并且都支持多个
2.第三张关系表修改数据
book_obj.authors.set()
括号内必须是一个可迭代对象 元素同样支持主键值或者数据对象
3.第三张关系表删除数据
book_obj.authors.remove()
括号内可以放主键值也可以放数据对象 并且都支持多个
4.第三张关系表清空指定数据
book_obj.authors.clear()
括号内无需传值 直接清空当前表在第三张关系表中的绑定记录
【七】多表查询
"""
MySQL多表查询思路
子查询---》正反向我没看出来是子查询啊
将SQL语句用括号括起来当做条件使用
连表操作
inner join\left join\right join\union
django orm本质还是使用的上述两种方法
子查询>>>:基于对象的跨表查询
连表操作>>>:基于双下划线的跨表查询
"""
1.正向概念
当前数据对象是否存在外键对象
eg:书籍查出版社---》正向
书查作者----》正向
作者查作者详情---》正向
出版社查书---》外键字段不在出版社表--》反向
"""
查询口诀
正向查询按外键字段名
反向查询按表名小写_set
"""
【八】基于对象的跨表查询
"""基于对象的跨表查询本质就是子查询即分步操作即可"""
正向
1.查询数据分析书籍对应的出版社
# 先获取书籍对象
book_obj = models.Book.objects.filter(title='数据分析').first()
# 再使用跨表查询
res = book_obj.publish
print(res) # 出版社对象:北方出版社
2.查询python全栈开发对应的作者
#获取图书对象
book_obj=Book.objects.filter(title='python全栈开发').first()
# 再使用跨表查询
res = book_obj.author.all()#因为书和作者是1对多的关系
反向
1.查询东方出版社出版的书籍
publish_obj=publish.objects.filter(name="东方出版社").first()
res=publish_obj.book_set.all()#有多个的情况就要all()
2.查询jason编写的书籍
作者表没外键,在第三张表上
author_obj = models.Author.objects.filter(name='jason').first()
res = author_obj.book_set.all()
【九】基于双下划线的跨表查询
"""基于双下划线的跨表查询的结果也可以是完整的数据对象"""
'''手上有条件所在的表直接点最终的目标数据对应的表'''
1.查询数据分析书籍对应的出版社
res = models.Book.objects.filter(title='数据分析').values('publish__name', 'publish__addr') # <QuerySet [{'publish__name': '北方出版社', 'publish__addr': '北京'}]>
2.查询python全栈开发对应的作者姓名和年龄
res = models.Book.objects.filter(title='python全栈开发').values('authors__name','authors__age')# <QuerySet [{'authors__name': 'jason', 'authors__age': 18}, {'authors__name': 'jerry', 'authors__age': 29}]>
3.查询作者jason的手机号和地址
res=models.Author.objects.filter(name='jason').values('author_detail__phone','author_detail__addr')# <QuerySet [{'author_detail__phone': 110, 'author_detail__addr': '芜湖'}]>
反向
1.查询东方出版社出版的书籍名称和价格
res = models.Publish.objects.filter(name='东方出版社').values('book__title','book__price')# <QuerySet [{'book__title': 'linux云计算', 'book__price': Decimal('24888.44')}, {'book__title': '聊斋志异', 'book__price': Decimal('16987.22')}]>
【十】双下线查询扩展
"""基于双下划线的跨表查询的结果也可以是完整的数据对象"""
'''手上有条件所在的表要求不能直接models点 直接点最终的目标数据对应的表'''
反向来,但是这种是直接有外键关联的
1.查询数据分析书籍对应的出版社名称
反向表名小写
res = models.Publish.objects.filter(Book__title='数据分析').values('name')
对象.values出来了
2.查询python全栈开发对应的作者姓名和年龄
作者查书,反向表名小写
res = models.Author.objects.filter(book__title='python全栈开发').values('name','age')
这种通过三个表来连接-->可以一直__下去
3.查询全栈开发对应的作者和手机号-->连续跨了两张表
res = models.Author.objects.filter(title='python全栈开发').values('author__author_detail__phone')
或
res = models.Author_Detail.objects.filter(author__book__title='python全栈开发').values('phone')
【十一】查看SQL内部语句
1.获取到结果对象以后queryset
res.query
2.固定配置到setting
适用面更广 只要执行了orm操作 都会打印内部SQL语句
【十二】聚合查询
"""
MySQL聚合函数:max\min\sum\count\avg
"""
"""
django使用聚合函数要导入模块
from django.db.models import Max, Min, Sum, Avg, Count
"""
eg:res = models.Book.objects.aggregate(Max('price'))
'''没有分组也可以使用聚合函数 默认整体就是一组'''
【十三】分组查询
"""MySQL分组操作:group by
ORM执行分组操作 如果报错 可能需要去修改sql_mode 移除only_full_group_by"""
1.统计每一本书的作者个数
from django.db.models import Count
res=models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
2.统计不止一个作者的图书
res=models.Book.objects.annotate(author_num=Count('authors__pk')).filter(author_num__gt=1).values('title', 'author_num')
3.统计每个出版社卖的最便宜的书的价格
反向表名小写
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
4.统计每个作者出的书的总价格
res = models.Author.objects.annotate(book_sum_price=Sum('book__price')).values('name','book_sum_price')
"""上述操作都是以表为单位做分组 如果想要以表中的某个字段分组如何操作"""
models.Author.objects.values('age').annotate()
5.统计每个出版社主键值对应的书籍个数
from django.db.models import Count
res = models.Book.objects.values('publish_id').annotate(book_num=Count('pk')).values('publish_id','book_num')
print(res)
【十四】F查询
"""
当表中已经有数据的情况下 添加额外的字段 需要指定默认值或者可以为null
方式1
IntegerField(verbose_name='销量',default=1000)
方式2
IntegerField(verbose_name='销量',null=True)
方式3
在迁移命令提示中直接给默认值
"""
当要比较的是数据库两个字段的值就无法比较,可以用F查询
1.查询库存大于销量的书籍
from django.db.models import F
res=models.Book.objects.filter(kucun__gt= F('maichu'))
2.将所有书的价格提升1000块
res=models.Book.objects.update(F("price")+1000)
3.将所有书的名称后面加上_爆款后缀(针对字符)
res = models.Book.objects.update(title=F('title') + '_爆款') # 不可以
res=models.Book.objects.update(name=Concat(F('title'), Value('爆款')))
【十五】Q查询
"""筛选条件是或的关系而且是不同的字段,可以用Q查询"""
1.查询价格大于20000或者卖出大于1000的书籍
与
res=models.Book.objects.filter(Q(price__gt=20000), Q(maichu__gt=1000))# 逗号是and关系
或
res=models.Book.objects.filter(Q(price__gt=20000) | Q(maichu__gt=1000)) # 管道符是or关系
非
res=models.Book.objects.filter(~Q(price__gt=20000)) # ~是not操作
'''
Q对象进阶用法
filter(price=100)
filter('price'=100)
当我们需要编写一个搜索功能 并且条件是由用户指定 这个时候左边的数据就是一个字符串
'''
q_obj = Q()得到Q对象
q_obj.connector = 'or' # 默认是and 可以改为or,给连接调条件
q_obj.children.append(('price__gt',20000))增加查询条件,也可以一起加
q_obj.children.append(('maichu__gt',1000))
res = models.Book.objects.filter(q_obj)将q对象添加到筛选条件中
print(res.query)
【十五】ORM查询优化(面试常问)
"""
在IT行业 针对数据库 需要尽量做 能不'麻烦'它就不'麻烦'它
"""
# 1.orm查询默认都是惰性查询(能不消耗数据库资源就不消耗)
光编写orm语句并不会直接指向SQL语句 只有后续的代码用到了才会执行
# 2.orm查询默认自带分页功能(尽量减轻单次查询数据的压力)
(1)only与defer
#res=models.Book.objects.values('title','price')
'''需求:单个结果还是以对象的形式展示 可以直接通过句点符操作'''
# for i in res:
# print(i.get('title'))
res = models.Book.objects.only('title', 'price')
for obj in res:
print(obj.title)
print(obj.price)
print(obj.publish_time)
"""
only会产生对象结果集 对象点括号内出现的字段不会再走数据库查询
但是如果点击了括号内没有的字段也可以获取到数据 但是每次都会走数据库查询
"""
res = models.Book.objects.defer('title','price')
for obj in res:
# print(obj.title)
# print(obj.price)
print(obj.publish_time)
"""
defer与only刚好相反 对象点括号内出现的字段会走数据库查询
如果点击了括号内没有的字段也可以获取到数据 每次都不会走数据库查询
"""
(2)select_related和prefetch_related
"""
主要用于多表查询
"""
# res = models.Book.objects.select_related('publish')
# for obj in res:
# print(obj.title)
# print(obj.publish.name)
# print(obj.publish.addr)
"""
字段少用
select_related括号内只能传一对一和一对多字段 不能传多对多字段
效果是内部直接连接表(inner join) 然后将连接之后的大表中所有的数据全部封装到数据对象中
后续对象通过正反向查询跨表 内部不会再走数据库查询
"""
res = models.Book.objects.prefetch_related('publish')
for obj in res:
print(obj.title)
print(obj.publish.name)
print(obj.publish.addr)
"""
字段多用,一点一点查
将多次查询之后的结果封装到数据对象中 后续对象通过正反向查询跨表 内部不会再走数据库查询
"""
【十六】常见字段类型
AutoField()
int auto_increment
CharField()
必须提供max_length参数 对应的数据库中是varchar类型
IntergerField()
int
DecimalField()
decimal
DateField()
date auto_now只要save了就可以改 auto_now_add只会在第一次的时候有,也就是可以写注册时间
DateTimeField()
datetime auto_now auto_now_add
BigIntergerField()
bigint
BooleanField()
传布尔值 存0和1
TextField()
存储大段文本
FileField()
传文件自动保存到指定位置并存文件路径
EmailField()
本质还是varchar类型
# 自定义字段类型
class MyCharField(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
【十七】重要参数
blank=True django后台添加数据可以为空
primary_key
max_length
verbose_name-->别名
null
default
max_digits数字的最大总位数
decimal_places指定数字中小数点后的位数
unique
db_index
auto_now
auto_now_add
choices(重要)
用于可以被列举完全的数据
eg:性别 学历 工作经验 工作状态
class User(models.Model):
username = models.CharField(max_length=32)
password = models.IntegerField()
gender_choice = (
(1,'男性'),
(2,'女性'),
(3,'变性')
)
gender = models.IntegerField(choices=gender_choice)
user_obj.get_gender_display()
# 有对应关系就拿 没有还是本身
to
to_field
db_constraint
ps:外键字段中可能还会遇到related_name参数
"""
外键字段中使用related_name参数可以修改正向查询的字段名
"""
【十八】事务操作
MySQL事务:四大特性(ACID)
原子性
一致性
独立性
持久性
# 【1】开始事务方式一 : 全局配置
# (1)在settings.py 文件中配置参数
'''
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': "django_day08",
"HOST": "127.0.0.1",
"PORT": 3306,
"USER": "root",
"PASSWORD": "1314521",
"CHARSET": "utf8mb4",
# 配置这个参数以后每一次的请求进来 都会在视图上开启事务
# 如果你的请求顺利 正常响应 你的事务就会提交
# 如果遇到报错程序崩溃 ,事务就被取消了
"ATOMIC_REQUESTS": True,
}
}
# 想要取消局部的 事务验证
from django.db import transaction
# 在局部的函数上面加上装饰器 non_atomic_requests
@transaction.non_atomic_requests(using=指定数据库的)
def index(request):
'''
# 【2】方式二:局部开启 事务
# (1)给视图函数加装饰器
'''
# 第一步 引入 Django内置的事务模块
from django.db import transaction
# 第二步 在视图函数上面增加装饰器
@transaction.atomic
def index(request):
'''
'''
# 第一步 引入 Django内置的事务模块
from django.db import transaction
from django.views import View
class LoginView(View):
# 第二步 在视图函数上面增加装饰器
@transaction.atomic
def get(self, request):
'''
# (2)自定义事务点
'''
def login(request):
# 开启事务
with transaction.atomic():
# (1)记录事务起点
sid = transaction.savepoint()
try:
# (2)执行你的代码
print("执行代码 ....")
# (3)确认没问题就会自动提交事务
except Exception as e:
# 进行事务的回滚
transaction.savepoint_rollback(sid)
'''
【十九】ORM执行原生SQL(很少用)
# 方式1
from django.db import connection, connections
cursor = connection.cursor()
cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
cursor.fetchone()
# 方式2
models.UserInfo.objects.extra(
select={'newid':'select count(1) from app01_usertype where id>%s'},
select_params=[1,],
where = ['age>%s'],
params=[18,],
order_by=['-age'],
tables=['app01_usertype']
)
【二十】多对多三种创建方式
# 全自动(常见)
orm自动创建第三张表 但是无法扩展第三张表的字段
authors = models.ManyToManyField(to='Author')
# 全手动(使用频率最低)
优势在于第三张表完全自定义扩展性高 劣势在于无法使用外键方法和正反向
class Book(models.Model):
title = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
class Book2Author(models.Model):
book_id = models.ForeignKey(to='Book')
author_id = models.ForeignKey(to='Author')
# 半自动(常见)
正反向还可以使用 并且第三张表可以扩展 唯一的缺陷是不能用
add\set\remove\clear四个方法
class Book(models.Model):
title = models.CharField(max_length=32)
authors = models.ManyToManyField(
to='Author',
through='Book2Author', # 指定表
through_fields=('book','author') # 指定字段
)
class Author(models.Model):
name = models.CharField(max_length=32)
'''多对多建在任意一方都可以 如果建在作者表 字段顺序互换即可'''
books = models.ManyToManyField(
to='Author',
through='Book2Author', # 指定表
through_fields=('author','book') # 指定字段
)
class Book2Author(models.Model):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')