ORM
ORM
(1)什么是ORM
- ORM是一种将对象与关系型数据库之间的映射的技术,主要实现了以下三个方面的功能:
- 数据库中的表映射为Python中的类
- 数据库中的字段映射为Python中的属性
- 数据库中的记录映射为Python中的实例
- ORM的主要优点是可以减少开发人员编写重复的SQL语句的时间和工作量,并且可以减少由于SQL语句的调整和更改所带来的错误。
(2)定义模型表
- 在
models.py
文件中定义模型表
from django.db import models
class User(models.Model):
username = models.CharField(max_length=20)
password = models.CharField(max_length=20)
age = models.IntegerField()
register_time = models.DateField() # 年月日
register_time1 = models.DateTimeField() # 年月日时分秒
(1)允许为空
class User(models.Model):
name = models.CharField(max_length=50, null=True) # 允许name字段为空
(2)指定默认值
class MyModel(models.Model):
age = models.IntegerField(default=18) # 默认年龄为18岁
(3)DateField两个重要参数
auto_now
每次操作数据的时候 该字段会自动将当前时间更新auto_now_add
在创建数据的时候会自动将当前时间记录下来 之后只要不人为的修改 数据会一直保存
from django.db import models
class User(models.Model):
register_time = models.DateField(auto_now = True ) # 年月日
register_time1 = models.DateTimeField(auto_now_add = True ) # 年月日时分秒
(4)外键
- 一对多
ForeignKey
FK = models.ForeignKey(to='表名',on_delete=models.CASCADE)
- 多对多
ManyToManyField
MTM = models.ManyToManyField(to='表名')
- 一对一
OneToOneField
OTO = models.OneToOneField(to='表名',on_delete=models.CASCADE)
(5)总结
- 以下是一些常用的语法
- 字段类型:
CharField
: 字符串字段- (max_length = )
TextField
: 文本字段IntegerField
: 整数字段FloatField
: 浮点数字段DecimalField
: 十进制数字段- (max_digits= , decimal_places= )
BooleanField
: 布尔字段DateField
: 日期字段DateTimeField
: 日期时间字段TimeField
: 时间字段EmailField
: 邮箱字段FileField
: 文件上传字段ImageField
: 图片上传字段ForeignKey
: 外键字段ManyToManyField
: 多对多关系字段OneToOneField
: 一对一关系字段
- 字段选项:
verbose_name
: 显示字段的名称help_text
:显示字段的说明on_delete
:级联更新级联删除- (on_delete=models.CASCADE)
max_length
: 字符串字段的最大长度default
: 默认值null
: 数据库中字段是否可以为NULLauto_now
: 每次保存对象时自动更新为当前时间auto_now_add
: 创建对象时自动设置为当前时间
(4)数据库迁移
python manage.py makemigrations
python manage.py migrate
(5)ORM数据操作 单表查询
- QuerySet对象索引取值或者循环取值
- object对象可以用.取值
(1)增加 create
模型表名.objects.create(字段名=字段值)
User.objects.create(
name="heart",
age=18,
email="ssrheart@outlook.com"
)
(2)查询 all
filter
get
- 获取到当前表中的全部数据
all()
模型表名.objects.all()
# <QuerySet [<Book: Book object (1)>, <Book: Book object (3)>, <Book: Book object (4)>, <Book: Book object (5)>]>
- 根据指定条件筛选数据
filter
get
# 方式一
模型表名.objects.get(筛选字段名=筛选字段值)
# Book object (1) 取出来是对象 可以.取值
# 方式二
模型表名.objects.filter(筛选字段名=筛选字段值)
# <QuerySet [<Book: Book object (1)>]> 取出来是QuerySet对象 不能.取值 需要取值的话要在后面.first()
# 获取到第一个数据
模型表名.objects.filter(筛选字段名=筛选字段值).first()
# Book object (1) 取出来是对象 可以.取值
- 去除指定条件的数据
exclude
模型表名.objects.exclude(筛选字段名=筛选字段值)
# 获取表中所有数据 QuerySet对象
user = User.objects.all()
# 根据特定条件筛选数据(方式一)
res = User.objects.get(name="heart")
# 根据特定条件筛选数据(方式二)
res = User.objects.filter(name="heart").first()
# 去除满足指定条件的数据
# 所有数据内过滤掉年龄等于18岁的记录
exclude_res = User.objects.exclude(age=18)
# <QuerySet [<Author: Author object (2)>, <Author: Author object (3)>, <Author: Author object (4)>]>
-
获取到一个字段的数据
values
-
返回的数据格式为列表套字典 本质上是一个
QuerySet
对象,而不是真的列表
user_obj = models.User.objects.values("name")
# <QuerySet [{'name': 'heart'}, {'name': 'god'}]> 列表套字典
- 获取到多个字段的数据
values_list
列表套元组
user_obj = models.User.objects.values_list("name","age")
print(user_obj)
# <QuerySet [('heart', 18), ('god', 20)]> 列表套元组
- 去重
distinct
user_obj = models.User.objects.values('name', 'age').distinct()
print(user_obj)
# <QuerySet [{'name': 'heart', 'age': 18}, {'name': 'god', 'age': 20}]>
- 排序
- 默认升序
user_obj = models.User.objects.order_by('age')
- 降序
user_obj = models.User.objects.order_by('-age')
- 反转
user_obj = models.User.objects.order_by('age').reverse()
- 统计个数
user_obj = models.User.objects.count()
- 是否存在
user_obj = models.User.objects.filter(name="heart").exists()
(3)更改 update
- 方式一:先查询直接修改
模型表名.objects.filter(筛选字段名=筛选字段值).update(修改字段名=修改字段值)
User.objects.filter(age=18).update(age=24)
- 方式二:先查询后修改
obj = 模型表名.objects.get(筛选字段名=筛选字段值)
obj.修改字段名=修改字段值
obj.save()
# 更新已有记录的值,可先通过查询获取对象实例后调用 .save() 方法
res = User.objects.get(name="张三")
res.age = 26
res.save()
(4)删除 delete
- 方式一:直接delete()
模型表名.objects.filter(筛选字段名=筛选字段值).delete()
User.objects.filter(username=username).delete()
- 方式二:删除需要先查询再删除
res = User.objects.get(筛选字段名=筛选字段值)
res.delete()
(6)测试脚本&查看内部sql语句
from django.test import TestCase
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day07.settings")
import django
django.setup()
# 在下面书写我们需要测试的代码
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
(7)必知必会十三条
# 以下方法的前提均为 models.模型表名.objects 之后的方法
# 【0】新增数据
.create(字段名=字段值)
# 【1】查询全部数据
.all()
# 【2】带有筛选条件的过滤
.filter(筛选字段名=筛选字段值)
# 【3】获取筛选结果的第一条数据
.filter(筛选字段名=筛选字段值).first()
# 【4】获取筛选结果的最后一条数据
.filter(筛选字段名=筛选字段值).last()
# 【4.1】获取筛选后的结果进行修改
.filter(筛选字段名=筛选字段值).update(字段名=字段值)
# 【4.2】获取筛选后的结果进行删除
.filter(筛选字段名=筛选字段值).delete()
# 【5】根据指定条件获取数据对象,条件不存在或者数据大于2则会报错
.get(筛选字段名=筛选字段值)
# 【6】获取单个字段名所对应的数据
.values(字段名)
# 【7】获取多个字段名所对应的数据
.value_list(字段名1,字段名2)
# 【8】去重,拿到指定字段的数据后,对筛选出的数据进行去重
.values(字段名1,字段名2).distinct()
# 【9】排序
# 正序
.order_by(字段名)
# 倒序
.order_by(-字段名)
# 【10】反转的已经经过排序过的数据
.order_by(字段名).reverse()
# 【11】统计当前数据对象的个数
.count()
# 【12】排除指定条件的数据
.exclude(字段名=字段值)
# 【13】判断符合当前条件的数据是否存在
.filter(筛选字段名=筛选字段值).exists()
# 补充:查看当前ORM语句的SQL查询语句
# 注意可以使用此方法的必须是 QuerySet 对象
.query
(8)双下划线查询
(1)条件大于__gt
- 年龄大于35岁的数据
res = models.User.objects.filter(age__gt=35)
print(res)
(2)条件小于__lt
- 年龄小于35岁的数据
res = models.User.objects.filter(age__lt=35)
print(res)
(3)条件大于等于__gte
- 年龄大于等于35岁的数据
res = models.User.objects.filter(age__gte=35)
print(res)
(4)条件小于等于__lte
- 年龄小于等于35岁的数据
res = models.User.objects.filter(age__lte=35)
print(res)
(5)或条件__in
- 年龄是18或者32或者40
res = models.User.objects.filter(age__in=(18, 32, 40))
print(res)
(6)两个条件之间__range
- 年龄是18-40之间
- 首尾都要
res = models.User.objects.filter(age__range=(18, 40))
print(res)
(7)模糊查询__contains
- 查询出名字中含有 n 的数据 -- 模糊查询
res = models.User.objects.filter(name__contains='n')
print(res)
- 默认区分大小写
res = models.User.objects.filter(name__contains='N')
print(res)
- 忽略大小写__icontains
res = models.User.objects.filter(name__icontains='N')
print(res)
(8)以指定条件开头/结尾
- 以什么开头/结尾__startswith
res = models.User.objects.filter(name__startswith='d')
print(res)
- 以什么结尾__endswith
res = models.User.objects.filter(name__endswith='m')
print(res)
(9)查询时间日期
- 查询出注册时间是2020年1月份的数据/年/月/日
res = models.User.objects.filter(register_time__month='1')
print(res)
res = models.User.objects.filter(register_time__year='2020')
print(res)
res = models.User.objects.filter(register_time__day='28')
print(res)
(9)ORM数据操作 多表查询
- 创建模型表
from django.db import models
# Create your models here.
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
publish_date = models.DateField(auto_now_add=True)
# 一对多
publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)
# 多对多
authors = models.ManyToManyField(to='Author')
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
email = models.EmailField()
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
# 一对一
author_detail = models.OneToOneField(to='Author_Detail',on_delete=models.CASCADE)
class Author_Detail(models.Model):
phone = models.BigIntegerField()
addr = models.CharField(max_length=40)
python manage.py makemigrations
python manage.py migrate
(1)一对多外键增删改
(1)增 create
- 直接写实际
字段id
models.Book.objects.create(title='黑色柳丁', price=99.99, publish_id=1)
- 虚拟字段
对象
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='蝴蝶', price=999.99,publish=publish_obj)
(2)删 delete
- 级联更新 级联删除
models.Book.objects.filter(pk=6).delete()
(3)修改 update
- 直接写实际
字段id
models.Book.objects.filter(pk=2).update(publish_id=2)
- 虚拟字段
对象
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.filter(pk=2).update(publish=publish_obj)
(2)多对多外键删改
- 多对多,增删改查,其实就是在操作第三张表
(1)增 add
- 如何给书籍添加作者?
book_obj = models.Book.objects.filter(pk=8).first()
book_obj.authors.add(1,2) # 书籍id为8的书籍绑定主键为1和2的作者
# 也支持先查出来作者的(id)对象再add添加
book_obj = models.Book.objects.filter(pk=2).first()
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
author_obj2 = models.Author.objects.filter(pk=3).first()
book_obj.authors.add(author_obj)
book_obj.authors.add(author_obj1,author_obj2)
# 书籍id为2的书籍绑定主键为1,2,3的作者
总结:
add给第三张关系表添加数据
括号内既可以传id也可以传对象 并且都支持多个
(2)删 remove
- 删除书籍id为2,作者id为2的绑定关系
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.authors.remove(2)
# 也可以支持放对象
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
book_obj.authors.remove(author_obj,author_obj1)
总结:
remove给第三张关系表删除数据
括号内既可以传id也可以传对象 并且都支持多个
(3)修改 set
- 给书籍id为8的书籍修改为作者id是1和2的作者
book_obj = models.Book.objects.filter(pk=8).first()
book_obj.authors.set([1,2])
# 也可以支持放对象
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
book_obj.authors.set([author_obj,author_obj1])
总结:
set括号内必须传一个可迭代对象,该对象内既可以传id也可以传对象 并且都支持多个
(4)清空 clear
- 在第三张关系表中清空某个书籍与作者的绑定关系
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.authors.clear()
# 清空了书籍id为2的所有绑定关系
总结:
clear括号内不要加任何参数
(3)正反向的概念
- 其实就是看外键字段在哪里
(1)正向
- 外键字段在我手上,我查你就是正向
# 比如书查出版社
book >>> publish (外键字段在书里面)就是正向
(2)反向
- 外键字段不在手上,我查你就是反向
# 比如出版社查书
publish >>> book (外键字段在书里面)就是反向
正向查询按字段
反向查询按表名小写 _set
(4)多表查询
(1)子查询(基于对象的跨表查询)
- 查询书籍主键为2的出版社名称
book_obj = models.Book.objects.filter(pk=2).first()
res = book_obj.publish # 获取出版社对象
print(res.name) # 北方出版社
- 查询书籍主键为8的作者
- 这个地方如果查询出来如果有多个结果,一定要使用
all()
- 不然会出现
booksystem01.Author.None
这种情况
- 不然会出现
- 然后使用for循环遍历结果取出想要的值
book_obj = models.Book.objects.filter(pk=8).first()
# res = book_obj.authors # booksystem01.Author.None
res = book_obj.authors.all()
print(res) # <QuerySet [<Author: Author object (1)>, <Author: Author object (2)>]>
- 查询作者heart的电话号码
author_obj = models.Author.objects.filter(name='heart').first()
res = author_obj.author_detail # 获取作者对象
print(res.phone) # 110
- 查询出版社是东方出版社出版的书
- 出版社查书是反向
表名小写加_set
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
res = publish_obj.book_set.all()
print(res) # <QuerySet [<Book: Book object (3)>, <Book: Book object (5)>, <Book: Book object (7)>]>
- 查询作者是heart写过的书
author_obj = models.Author.objects.filter(name='heart').first()
res = author_obj.book_set.all()
- 查询手机号是110的作者姓名
# author_detail_obj = models.Author_Detail.objects.filter(phone=110).first()
res = author_detail_obj.author
# 当查询结果只有一个的时候 不需要加_set.all()
反向查询的时候:
当查询结果有多个的时候 就必须加_set.all()
当查询结果只有一个的时候 不需要加_set.all()
(2)联表查询(基于下划线的跨表查询)
- 查询heart的手机号
res = models.Author.objects.filter(name='heart').values('author_detail__phone')
print(res) # <QuerySet [{'author_detail__phone': 110}]>
# 反向
res = models.Author_Detail.objects.filter(author__name='heart').values('phone','author__name')
print(res) # <QuerySet [{'phone': 110, 'author__name': 'heart'}]>
- 查询书籍主键为2的出版社名称和书的名称
res = models.Book.objects.filter(pk=2).values('publish__name','title')
print(res) # <QuerySet [{'publish__name': '北方出版社', 'title': '红楼梦'}]>
# 反向
res = models.Publish.objects.filter(book__id=2).values('name', 'book__title')
print(res) # <QuerySet [{'name': '北方出版社', 'book__title': '蝴蝶'}]>
- 查询书籍主键为2的作者姓名
res = models.Book.objects.filter(pk=2).values('authors__name')
print(res) # <QuerySet [{'authors__name': 'heart'}, {'authors__name': 'god'}]>
# 反向
res = models.Author.objects.filter(book__id=2).values('name')
print(res) # <QuerySet [{'name': 'heart'}, {'name': 'god'}]>
- 查询书籍主键是2的作者的手机号和姓名
res = models.Book.objects.filter(pk=2).values('authors__author_detail__phone')
print(res) # <QuerySet [{'authors__author_detail__phone': 110}, {'authors__author_detail__phone': 120}]>
(5)聚合查询 aggregate
- 只要是跟数据库相关的模块 基本都在django.db.models里面
- 如果上述没有那么应该在django.db里面
from django.db.models import Max,Min,Count,Sum,Avg
from django.db.models import Max,Min,Count,Sum,Avg
- 所有书的平均价格
from django.db.models import Max,Min,Count,Sum,Avg
res = models.Book.objects.aggregate(Avg('price'))
print(res) # {'price__avg': Decimal('16959.253333')}
- 一次性使用
res = models.Book.objects.aggregate(Max('price'),Min('price'),Count('pk'),Sum('price'))
print(res) # {'price__max': Decimal('99999.99'), 'price__min': Decimal('99.99'), 'pk__count': 6, 'price__sum': Decimal('101755.52')}
(6)分组查询 annotate
-
需要搭配聚合查询使用
from django.db.models import Max,Min,Count,Sum,Avg
-
models后面点什么,就是按什么分组
-
统计每一本书的作者个数
from django.db.models import Max,Min,Count,Sum,Avg
res = models.Book.objects.annotate(author_num=Count('authors')).values('title','author_num')
print(res)
- 统计每个出版社卖的最便宜的书的价格
from django.db.models import Max,Min,Count,Sum,Avg
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
print(res)
- 统计不止一个作者的图书
res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1)
print(res) # <QuerySet [<Book: Book object (2)>, <Book: Book object (4)>, <Book: Book object (8)>]>
- 查询每个作者出的书的总价格
res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price')
print(res)
(7)F查询
- 能直接获取到某个字段对应的数据
from django.db.models import F
- 查询卖出数大于库存数的书籍
res = models.Book.objects.filter(maichu__gt=F('kucun'))
print(res)
- 将所有书籍的价格提升500块
models.Book.objects.update=(price=F('price') + 500)
- 将所有书的名字后面加上爆款两个字
- 在操作字符类型的数据的时候 F不能够直接做到字符串的拼接 需要借助Concat 和 Value
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title=Concat(F('title'), Value('爆款')))
(8)Q查询
-
Q查询允许以一种更简洁和易读的方式来构建复杂的查询条件
-
from django.db.models import Q
-
查询卖出数大于100或者价格小于600的书籍
filter括号内多个参数是and关系
res = models.Book.objects.filter(maichu__gt=100,price__lt=600)
res = models.Book.objects.filter(Q(maichu__gt=100), Q(price__lt=600)) # , and关系
res = models.Book.objects.filter(Q(maichu__gt=100) | Q(price__lt=600)) # | or关系
res = models.Book.objects.filter(~Q(maichu__gt=100) | Q(price__lt=600)) # ~ not关系
(1)Q的高阶用法
- 能够将查询条件的左边也变成字符串的形式
- 查询卖出数大于100或者价格小于600的书籍
q = Q()
q.connector = 'or'
q.children.append(('maichu__gt', 100))
q.children.append(('price__lt', 600))
res = models.Book.objects.filter(q) # 默认是and关系
print(res)
(9)事务
- ACID
- 原子性
- 一致性
- 隔离性
- 持久性
from django.db import transaction
with transaction.atomic():
# sql1
# sql2
# 在with代码块内书写的所有orm操作都是属于同一个事务