djando之模型层
模型层之前期准备
1.自带的sqlite3数据库对时间字段不敏感,有上海会展示错乱,所以我们习惯切换成常见的数据库入MySQL
django orm并不会自动帮你创建库,所以需要提前准备好
2.单独测试django某个功能层
默认不循序单独测试某个py文件 如果想要测试某个py文件(主要是models.py)
测试环境1:pycharm提供的python console 但是他不会保存数据,不太推荐
测试环境2:自己搭建(自带的tests或者自己创建)
1.拷贝manage.py前4行
2.自己在加两行内容如下:
import django
django.setup()
1.django orm底层还是SQL语句,我们是可以查看的
如果我们手上是一个QuerySet对象,那么可以直接点query查看SQL语句
如果想查看所有orm底层的SQL语句也可以在配置文件添加日志记录
代码如下:
import os
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoProject2.settings')
import django
django.setup()
from app01 import models 导入模块一定要在下面,要不会报错
针对时间字段两个重要的参数:
auto_now:每次操作数据都会自动更新当前时间
aotu_now_add:创建数据自动获取当前时间,后续修改不会改变
ORM常用关键字
1.create()创建数据并直接获取当前创建的数据对象
res = models.Userinfo.objects.create(name='jason',age='18',gender='男性',heigth='168')
print(res) # 对象:jason
2.filter() 根据条件筛选数据 结果是QuerySet对象[对象1,对象2,对象3]
res = models.Userinfo.objects.filter()
res1 = models.Userinfo.objects.filter(name='jason')
print(res)
print(res1)
3.first()与last():QuerySet支持索引取值,但是只支持正数,索引不存在的数据值会报错
res0 = models.Userinfo.objects.filter()
res = models.Userinfo.objects.filter().first()
res1 = models.Userinfo.objects.filter().last()
print(res0) # <QuerySet [<Userinfo: 对象:jason>, <Userinfo: 对象:李治>, <Userinfo: 对象:闫龙龙>, <Userinfo: 对象:郑凯泽>, <Userinfo: 对象:李晓建>]>
print(res) # 对象:jason
print(res1) # 对象:李晓建
4.update():更新数据(批量更新或者指定更新)
res = models.Userinfo.objects.update(heigth=161)
res1 = models.Userinfo.objects.filter(name='jason').update(age=20)
print(res) # 5
print(res1) # 1
5.delete():删除数据(批量删除或者指定删除)
models.Userinfo.objects.filter().delete() # 删除全部数据
models.Userinfo.objects.filter(id=1).delete() # 删除指定id的数据
ps:删库需小心,尽量不要删
6.all():查询所有数据,结果是QuerySet对象
res = models.Userinfo.objects.all()
print(res) # <QuerySet [<Userinfo: 对象:jason>, <Userinfo: 对象:李治>, <Userinfo: 对象:闫龙龙>, <Userinfo: 对象:郑凯泽>, <Userinfo: 对象:李晓建>]>
7.values():根据指定字段获取数据值,结果是QuerySet [{},{},{},{}]
res = models.Userinfo.objects.values()
res1 = models.Userinfo.objects.all().values()
res2 = models.Userinfo.objects.filter(id=8).values()
print(res)
print(res1)
print(res2)
8.values_list():根据指定字段获取数据,结果是QuerySet [(),(),()]
res = models.Userinfo.objects.all().values_list()
print(res)
9.distinct():去重,数据要一定一模一样才可以,如果有主键参与肯定是不可以的
res = models.Userinfo.objects.values('name').distinct()
print(res)
10.order_by():根据指定条件排序,默认是升序,字段前面加负号是降序
res = models.Userinfo.objects.all().order_by('age') # 升序
print(res)
res1 = models.Userinfo.objects.all().order_by('-age') # 降序
print(res1)
11.get():根据条件筛选数据并直接获取到数据对象,一旦条件不存在会直接报错,不建议使用
res = models.User.objects.get(pk=1)
print(res)
12.exclude():取反操作
res = models.User.objects.exclude(pk=1)
13.reverse() 颠倒顺序(被操作的对象必须是已经排过序的才可以)
res = models.User.objects.all()
res = models.User.objects.all().order_by('age')
res1 = models.User.objects.all().order_by('age').reverse()
14.count():统计结果集中数据的个数
res = models.User.objects.all().count()
print(res)
15.exists() 判断结果集中是否含有数据 如果有则返回True 没有则返回False
res = models.User.objects.all().exists()
print(res)
res1 = models.User.objects.filter(pk=100).exists()
print(res1)
如果你想知道你对数据库进行操作时,Django内部到底是怎么执行它的sql语句时可以加下面的配置来查看
在Django项目的settings.py文件中,在最后复制粘贴如下代码:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
### ORM执行SQL语句
```python
有时候ROM的操作效率可能偏低 我们是可以自己编写SQL的
方式1:
models.类名.objects.raw('select * from 表名')
代码展示:
res = models.Userinfo.objects.raw('select * from app01_userinfo')
for i in res:
print(i) # 对象:jason,对象:闫龙龙
方式2:
基于django的某个模块,用法与我们pymysql一致
from django.db import connection
cursor = connection.cursor()
cursor.execute('select name from 表名')
print(coursor.feschall())
神奇的双下划线查询
"""
对于QuerySet对象:
只要还是queryset对象就可以无限制的点queryset对的方法
queryset.filter().values().filter().values_list().filter()...
"""
大于关键字(__gt):
res= models.Userinfo.objects.filter(age__gt=18) 这句话表示查询年龄大于18的数据
print(res) # 结果是queryset对象,可以否循环取出
小于关键字(__lt):
res= models.Userinfo.objects.filter(age__lt=18) 这句话表示查询年龄小于18的数据
print(res) # 结果是queryset对象,可以否循环取出
大于等于(__gte),小于等于(__lte):
res = models.Userinfo.objects.filter(age__gte=18) 查询年龄大于等于18岁的数据
res1 = models.Userinfo.objects.filter(age__lte=18) 查询年龄小于等于18岁的数据值
print(res,res1)
"总结:大于等于和小于等于是在大于和小于后面加了个字母e"
或者查询关键字(__in):
res = models.Userinfo.objects.filter(age__in=(18, 28, 38))
"查询年龄是18或者28或者38的数据"
print(res)
范围之内查询关键字(__range):
res = models.Userinfo.objects.filter(age__range=(18,38))
"查询年龄在18岁到38岁之间的数据,包含着两个数字,可以元组或者列表"
print(res)
模糊查询含有字母的数据关键字(__contains):
res = models.Userinfo.objects.filter(name__contains='j')
"注意contains是区分大小写的"
print(res)
res = models.Userinfo.objects.filter(name__icontains='j')
"icontains不区分大小写"
print(res)
时间截取关键字(__year):
res = models.Userinfo.objects.filter(register_time__year=2022)
"查询注册时间是2022年的"
print(res)
ORM外键字段的创建
"""
复习MySQL外键关系
一对多
外键字段建在多的一方
多对多
外键字段统一键在第三张关系表
一对一
外键字段建在任何一方都可以,建议建立在查询频率比较高的表中
"""
1.创建基础表(书籍表、出版社表、作者表、作者详情)
2.确定外键关系
一对多 ORM与MySQL一致,外键字段建在多的一方
多对多 ORM与MySQL有更多变化
1.外键字段可以直接键在某张表中(查询频率较高的)
django会自动帮你创建第三张关系表
2.自己创建第三张表管子并创建外键字段
一对一 ORM与MySQL一致,外键字段建在查询较高的一方
3.ORM创建
针对一对多和一对一同步到表中之后会自动加_id的后缀,所以自己不要直接写上
针对多对多 不会在表中有展示 而是创建第三张表
模型层创建表
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) # 价格,小数一共有8位,小数点后面2位
publish_time = models.DateField(auto_now_add=True) # 出版时间,Datefield表示年月日
# 书籍表与出版社的表是一对多的关系,多的一方在书籍
publist = models.ForeignKey(to='Publish', on_delete=models.CASCADE) # 创建外键绑定出版社的表,to等于的是表名,级联删除
# 书籍与作者的表是多对定义的关系,查询比较频繁的是书籍
authors = models.ManyToManyField(to='Author') # 创建外键与作者表多对对
class Publish(models.Model):
"""出版社的表"""
name = models.CharField(max_length=32) # 出版社名字
address = models.CharField(max_length=64) # 地址
class Author(models.Model):
"""作者表"""
name = models.CharField(max_length=32) # 作者名字
age = models.IntegerField() # 作者年龄
# 作者表与作者详情是一对一的关系表,作者查询频率高,建在这里
authordate = models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE) # 与作者详情表一对一关系,要级联更新
class AuthorDetail(models.Model):
"""作者详情表"""
phone = models.CharField(max_length=32) # 作者电话
address = models.CharField(max_length=64) # 作者地址
针对数据的插入
我们想要插入数据,首先需要考虑外键的存在,书籍表与出版社的表是一对多外键在书籍表中,要插入数据的话,1.首先需要在出版社表中插入数据
models.Publish.objects.create(name='东京出版站', address='东京')
models.Publish.objects.create(name='南京出版站', address='南京')
models.Publish.objects.create(name='北京出版站', address='北京')
models.Publish.objects.create(name='西京出版站', address='西京')
2.应该给书籍表插入数据,但是要考虑书籍与作者的多对多关系,有第三张表,所以需要先考虑作者表,又因为作者表与作者详情表一对一的关系,外键在作者表,所以要先创建作者详情表的数据
models.AuthorDetail.objects.create(phone='119', address='上海')
models.AuthorDetail.objects.create(phone='129', address='南京')
models.AuthorDetail.objects.create(phone='139', address='合肥')
models.AuthorDetail.objects.create(phone='169', address='西安')
3.作者详情表创建好,就可以给作者表创建数据,插入外键了
models.Author.objects.create(name='jason', age='18', authordate_id=1)
models.Author.objects.create(name='tom', age='23', authordate_id=2)
models.Author.objects.create(name='李治', age='26', authordate_id=3)
models.Author.objects.create(name='张红', age='18', authordate_id=4)
"""有外键字段的时候,添加数据一定不要忘记在外键字段进行添加"""
4.现在与书籍表具有外键关系的出版社表以及作者表都创建好了,就可以给书籍表添加数据
针对一对多 插入数据可以直接填写表中的实际字段即可
models.Book.objects.create(title='三国演义', price=99.8, publist_id=2)
models.Book.objects.create(title='水浒传', price=99.8, publist_id=1)
models.Book.objects.create(title='西游记', price=99.8, publist_id=2)
models.Book.objects.create(title='钢铁是怎样炼成的', price=99.8, publist_id=3)
'针对一对多和一对一,插入数据也可以填写表中的类中的字段名'
publist_obj = models.Publish.objects.filter(pk=3).first()
上述的话表示,拿到出版社表中id为1的数据j)
# 这句话表示在书籍表中创建数据,publist外键直接就是出版社对象中对应主键值
针对多对多 插入数据,需要设立外键的一方产生一个对象,然后由对象点外键进入第三张表,写入与另一方绑定外键字段,如:书籍表与作者表,外键设置在书籍表中,就需要产生一个书籍对象,然后通过外键进入第三张表添加外键作者表的字段值也可以通过作者表生成的对象来添加
多对多绑定方法1:放入另一个主键对应的数值
book_obj = models.Book.objects.filter(pk=1).first()
# 产生主键为1的书籍对象,也就是主键为1的记录
book_obj.authors.add(1, 2)
# 进入第三张表,绑定多对多与作者关系,写入作者的id,表示这本书有两个作者,作者表中的主键1和2
book_obj2 = models.Book.objects.filter(pk=2).first()
book_obj2.authors.add(1, 2, 3)
多对多绑定方法2:放入另一个主键对应的对象
book_obj5 = models.Book.objects.filter(pk=5).first()
# 筛选id等于5的书籍对象
author_obj4 = models.Author.objects.filter(pk=4).first()
# 筛选id是4的作者对象
book_obj5.authors.add(author_obj4)
# 书籍对象点外键进入第三张表,绑定主键为4的作者对象,绑定就是该作者的主键
多对多修改关系
book_obj = models.Book.objects.filter(pk=3).first()
# 获得书籍的对象
book_obj.authors.set((1, 3))
# 书籍对象进去第三张表修改与作者表的绑定关系为作者1和作者3
------------------------------------------------------------------------------------------------
放入数字可以,对象也是可以的
book_obj = models.Book.objects.filter(pk=7).first()
# 产生一个书籍对象
author_obj = models.Author.objects.filter(pk=1).first()
# 产生一个作者对象
book_obj.authors.set((author_obj,))
# 将书籍对象7绑定的作者修改为作者对象1
多对多移除关系
book_obj = models.Book.objects.filter(pk=2).first()
# 产生书籍对象
book_obj.authors.remove(2)
# 将书籍对象2与作者对象2的绑定关系移除
"""同样移除不止可以使用主键值,也可以使用对应主键值的对象"""
book_obj = models.Book.objects.filter(pk=2).first()
author_obj2 = models.Author.objects.filter(pk=2).first()
book_obj.authors.remove(author_obj1,)
"""
add()\remove() 多个位置参数(数字 对象)
set() 可迭代对象(元组 列表) 数字 对象
clear() 清楚当前数据对象的关系
"""
ORM跨表查询
"""
复习MySQL跨表查询的思路
子查询
分步操作:将一条SQL语句用括号括起来当做另外一条SQL语句的条件
连表操作
先整合多张表之后基于表单查询即可
inner join 内连接
left join 左连接
right join 右连接
"""
正反向查询的概念(重要)
正向查询
由外键字段所在的表数据查询关联的表数据 正向
反向查询
没有外键字段的表数据查询关联的表数据 反向
ps:正反向的核心就看外键字段在不在当前数据所在的表中
ORM跨表查询的口诀(重要)
正向查询按外键字段
反向查询按表名小写
基于对象的跨表查询
1.查询主键为1的书籍对应的出版社名称
# 按照主键为1,拿到书籍对象
book_obj = models.Book.objects.filter(pk=1).first()
# 查询出版社名字,书籍表与出版社表是一对多,主键在书籍表,正向查询
print(book_obj.publist.name) # 东京出版站
2.查询主键为4的书籍对应的作者姓名
# 主键4的书籍,先获取书籍对象
book_obj = models.Book.objects.filter(pk=4).first()
# 获取作者姓名,书籍与作者是多对多的关系,主键在数据表中,正向查询
# print(book_obj.authors) # app01.Author.None是因为是可能对应多个
# 所以更好的写法
print(book_obj.authors.all()) # <QuerySet [<Author: 作者:jason>, <Author: 作者:李治>, <Author: 作者:张红>]>
print(book_obj.authors.all().values('name')) # 获取名字<QuerySet [{'name': 'jason'}, {'name': '李治'}, {'name': '张红'}]>
3.查询jason的电话号码
# 根据条件先获得jason的对象
author_obj = models.Author.objects.filter(name='jason').first()
# 作者表与详情表是一对一关系,主键在作者表中,正向查询
print(author_obj.authordate.phone) # 119,一对一就只有一个条件不用再写all
4.查询东京出版社出版过的书籍
# 先获取东京出版社的对象
publish_obj = models.Publish.objects.filter(name='东京出版站').first()
# 出版社和书籍是一对多的关系,外键在书籍表中,反向查询
print(publish_obj.book_set.all()) # <QuerySet [<Book: 书籍对象:水浒传>]>
print(publish_obj.book_set.all().values('title')) # <QuerySet [{'title': '水浒传'}]>
5.查询jason写过的书籍
# 根据条件先获得作者jason的对象
author_obj = models.Author.objects.filter(name='jason').first()
# 作者表与书籍表是多对多关系,主键在书籍表,反向查询
print(author_obj.book_set.all()) # <QuerySet [<Book: 书籍对象:三国演义>, <Book: 书籍对象:西游记>, <Book: 书籍对象:水浒传>]>
print(author_obj.book_set.all().values('title')) # <QuerySet [ {'title': '三国演义'}, {'title': '西游记'},{'title': '水浒传'}]>
6.查询电话号码是119的作者姓名
authordetail_obj = models.AuthorDetail.objects.filter(phone=119).first()
# 详情表与作者表是一对一关系,主键在作者表中,反向查询
print(authordetail_obj.author.name) # jason
这句话表示反向查询到作者表中,然后作者记录点name字段获得名字
基于双下划线的跨表查询
1.查询主键为1的书籍对应的出版社名称
# 先获得主键为1的书籍对象,书籍表与出版社是一对多关系,主键在书籍表,正向查询
res = models.Book.objects.filter(pk=1).values('publist__name','title')
# 这句话表示先获取主键为1的书籍对象,利用values('publist')进入出版社表,获取名字
print(res) # [{'publist__name': '东京出版站'}]>
2.查询主键为4的书籍对应的作者姓名
# 查询主键为4的书籍对应的作者姓名
# 现获取主键为4的书籍的对象,书籍与作者是多对多,主键在书籍表,正向查询
res = models.Book.objects.filter(pk=4).values('authors__name')
print(res) # QuerySet [{'authors__name': 'jason'}, {'authors__name': '李治'}, {'authors__name': '张红'}]>
3.查询jason的电话号码
# 先获取jason的作者对象,作者跟详情表是一对一关系,主键在作者表,正向查询
res = models.Author.objects.filter(name='jason').values('authordate__phone')
print(res)
4.查询北方出版社出版过的书籍名称和价格
# 先获取东京出版社对象,出版社表与书籍表是一对多,主键在书籍表,反向查询
res = models.Publish.objects.filter(name='东京出站').values('book__title','book__price')
print(res) # <QuerySet ['book__price': Decimal('99.80')}, {'book__title': '水浒传', 'book__price': Decimal('96.80')}]>
5.查询jason写过的书籍名称
# 先获取jason的作者对象,作者与书籍是多对多的关系,外键在书籍表,反向查询
res = models.Author.objects.filter(name='jason').values('book__title')
print(res)
6.查询电话号码是119的作者姓名
# 先获取电话是119的对象,作者与详情表是一对一,主键在作者表,反向操作
res = models.AuthorDetail.objects.filter(phone=119).values('author__name')
print(res) # <QuerySet [{'author__name': 'jason'}]>
进阶操作(王炸)
1.查询主键为1的书籍对应的出版社名称
# 先获得主键是1的书籍对象,书籍与出版社是多对多,主键在书籍表,正向查询
res = models.Publish.objects.filter(book__pk=1).values('name')
"这句话表示查询书籍表主键是1绑定的出版社的对象,然后出版社对象点values对应的name"
print(res)
2.查询主键为4的书籍对应的作者姓名
# 书籍表与作者表是多对多的关系,主键在书籍表中,反向查询
res = models.Author.objects.filter(book__id=4).values('name')
"查询书籍主键是4对应的作者对象,点获得作者的名字"
print(res) # <QuerySet [{'name': 'jason'}, {'name': '李治'}, {'name': '张红'}]>
3.查询jason的电话号码
# 作者表与作者详情表是一对一,主键在作者表,可以根据作者名字,直接反向获取详情表
res = models.AuthorDetail.objects.filter(author__name='jason').values('phone')
print(res) # <QuerySet [{'phone': '119'}]>
4.查询东京出版社出版过的书籍名称和价格
# 出版社跟书籍表是一对多的关系,主键在书籍表中,正向查询,根据出版社获取书籍对象
res = models.Book.objects.filter(publist__name='东京出版站').values('title','price')
print(res)
# <QuerySet [{'title': '水浒传', 'price': Decimal('96.80')}]>
5.查询jason写过的书籍名称
# 作者表与书籍表是多对多关系,主键在书籍表,正向查询,根据作者名获取书籍对象
res = models.Book.objects.filter(authors__name='jason').values('title')
print(res)
# QuerySet [{'title': '三国演义'}, {'title': '西游记'}]
6.查询电话号码是119的作者姓名
# 作者表与详情表是一对一关系,主键在作者表,根据电话号码获取作者姓名
res = models.Author.objects.filter(authordate__phone=119).values('name')
print(res) # <QuerySet [{'name': 'jason'}]>
补充操作(王炸):
查询主键为4的书籍对应的作者的电话号码
方法1:从书籍对象出发
"""
分步思考:
1.先获取主键4的书籍对象
2.作者表与书籍表是多对多关系,主键在书籍表
3.作者表,作者表与详情表是一对一关系,进入详情表
4.进入详情表获取电话号码
"""
res = models.Book.objects.filter(pk=4).values('authors__authordate__phone')
print(res)
# <QuerySet [{'authors__authordate__phone': '119'}, {'authors__authordate__phone': '139'}, {'authors__authordate__phone': '169'}]>
方法2:以详情表为中心
"""
以详情表为中心:
1.详情表与作者表是一对一的关系,主键在作者表,先进入作者表
2.作者表与书籍表是多对多的关系,主键在书籍表,反向查询
"""
res = models.AuthorDetail.objects.filter(author__book__id=4).values('phone')
print(res)
#<QuerySet [{'authordate__phone': '119'}, {'authordate__phone': '139'}, {'authordate__phone': '169'}]>
方法3:从作者表出发
"""
1.从作者表出发,先获取作者对象,作者与书籍对象是多对多关系,主键在书籍表,反向查询
2.获取作者对象,作者表与详情表是一对一关系,主键在详情表,正向查询,进去详情表拿电话
"""
res = models.Author.objects.filter(book__id=4).values('authordate__phone')
print(res)
# <QuerySet [{'authordate__phone': '119'}, {'authordate__phone': '139'}, {'authordate__phone': '169'}]>
聚合查询
"""
SQL语句中的聚合函数:max,min,sum,count,avg
"""
在ORM中支持单独使用聚合函数 关键字:aggregate
需要先导入模块
import django.db.models impore Max,Min,Sum,Count,Avg
from django.db.models import Max, Min, Count, Sum, Avg
res = models.Book.objects.aggregate(最高价格=Max('price'), 最低价格=Min('price'), 书籍总量=Count('pk'),总价=Sum('price'), 平均价格=Avg('price')),
print(res)
# ({'最高价格': Decimal('999.80'), '最低价格': Decimal('20.80'), '书籍总量': 8, '总价': Decimal('1665.60'), '平均价格': Decimal('208.200000')},)
分组查询
分组查询报错
如果执行orm分组查询报错,并且有关键字sql_mode stict mode
解决办法:移除sql_mode中的only_full_group_by
分组查询:annotate
统计每一本书的作者个数
res=models.Book.objects.annotate(authors_num=Count('authors__pk')).values('title','authors_num')
print(res)
"""
每本书作者的个人,首先我们已书籍表为分组,关键字annodate,分组之后统计作者的个数,count统计作者,直接正向查询进入第三张表获得作者的id数统计,赋值给变量名,values拿出数据值
"""
统计出每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(最低价格=Min('book__price')).values('name', '最低价格')
print(res)
统计不止一个作者的图书
"""
拆开思考:
1.不止一个作者的图书,首先我们要获得每本图书的作者数量
2.然后再进行判断大于1的
"""
res = models.Book.objects.annotate(book_outher=Count('authors__pk')).filter(book_outher__gt=1).values('title','book_outher')
print(res)
查询每个作者出的书的总价格
res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price')
print(res)
分组查询小窍门
"""
models.表名.objects.annotate() 按照表分组
models.表名.objects.values('字段名').annotate()
这句话表示按照values括号内指定的字段名分组
"""
查询每本书出版社的个数
res = models.Book.objects.values('publish_id').annotate(count_pk=Count('pk')).values('publish_id', 'count_pk')
print(res)
F与Q查询
前期准备,已创建好的数据库添加字段
注意:给已经创建好的数据库添加字段,必须要重新执行数据库迁移,但是需要给默认值,否则无法添加,具体如下
要做以下修改:
给后添加的字段添加默认值
F查询
1.查询库存数大于卖出数的书籍
"""
当查询条件不是很明确的,也需要从数据库中获取,就需要使用F查询
"""
from django.db.models import F
res = models.Book.objects.filter(kuncun__gt=F('maichu'))
print(res)
"F相当于直接取出来括号内字段名对应的数据值,进行比对"
2.将所有书的价格涨800
res = models.Bood.objects.filter(price=F('price')+800)
"这句话表示将书籍表中所有价格用F取出来加800并重新赋值给price"
3.将所有书的名称后面追加爆款
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title = Concat(F('titie'),Value('新款')))
"""
这句话表将字段名title的数据用F查询取出来,添加拼接属性,重新赋值给title
"""
Q查询
查询主键是1或者价格大于2000的书籍
res = models.Book.objects.filter(pk=1,publist__gt=2000)
print(res) # ,号默认表示的是and关系,可以用Q来改变
form django.db.models import Q
res = models.Book.objects.filter(Q(pk=1) | Q(price__gt=2000))
print(res)
"""
用Q把条件包起来,中间用|符,就表示或的意思
"""
res = models.Book.objects.filter(~Q(pk=1) | Q(price__gt=2000))
print(res)
"""
在Q前面放~就表示not的意思,表示主键不是1或者价格大于2000的
"""
### Q查询进阶操作
```python
from django.db.models import Q
q_obj = Q() # 1.产生一个Q对象
q_obj.connector = 'or' # 默认多个条件连接是and可以修改为or
q_obj.children.append(('pk', 1)) # 这句表示添加pk是1的查询条件
q_obj.children.append(('price__gt', 100)) # 这句表示添加价格大于100的查询条件
res = models.Book.objects.filter(q_obj)
"这句话表示在书籍表中查询pk是1或者价格大于100的书籍"
print(res)
ORM查询优化
1.ORM的查询默认都是惰性查询
res = models.Book.objects.all()
"""这句话表示查询书籍表中所有的记录,但是并不会显示底层的SQL执行,证明这句话没有执行查询语句"""
res = models.Book.objects.all()
print(res)
print(res)
"""我们打印了这句话,就会执行SQL语句,这就是ORM的惰性查询,如果我们没使用到查询结果,底层是不会执行的,但是如果我们执行几次打印查询结果,查询语句就会执行几次
"""
2.ORM的查询自带分页处理
res = models.Book.objects.all()
print(res)
直接打印查询的多条结果的时候,SQL语句在底层执行的时候会自带分页的操作,防止打印的记录过多出现内存不足的情况
3.only与defer
only:
res = models.Book.objects.only('name','price')
for obj in res:
print(obj.name) # 打印only括号内填写的字段,不走SQL查询
print(obj.price)
print(obj.publish_time) # 可以点括号内没有的字段获取数据,但是会走SQL查询
"""
总结:only会将括号内填写的字段封装到数据对象中,后续获取括号内的值就不会再次执行SQL语句,获取不是括号内的值会再次执行SQL语句
"""
defer:
res = models.Book.objects.defer('name','price')
for obj in res:
print(obj.name) # 括号内填写的打印一次执行一次
print(obj.price) # 括号内填写的打印一次执行一次
print(obj.publish_time) # 括号内没有的不会执行
"""
总结:defer与only正好是正反的,括号内的字段在使用的时候使用一次执行SQL语句一次,括号内没有的才不会执行SQL语句
"""
4.select_related与prefetch_related
select_relate:
res = models.Book.objects.select_related('publish')
"连表查询操作,这样才查询查询关键表也不会多执行SQL"
for i in res:
print(i.name)
print(i.publish.name) # 这句话查关联出版社的名字,不会再次走SQL语句
"""
总结:表示先按照外键字段将两个表拼接起来一起封装数据,这样不管是查询主表的内容还是查询关联表的内容都不会再次执行SQL语句,但是括号内填写的一定要是真实的外键字段,也就是多对多的外键不能够填写,是虚拟的
"""
prefetch_related:
res = models.Book.objects.prefetch_related('publish') # 子查询
for obj in res:
print(obj.publish.name)
ORM事务操作
"""
回顾MySQL中事务的四大特性(ACID)
原子性、一致性、隔离性、持久性
相关的SQL关键字:
start transaction; 开启事务
rollback 回滚
commit 保存,保存以后不可修改
savepoint 设立节点,回滚不会直接到刚开始,而是到 节点
"""
3种开启事务的方式
方式1:配置文件数据库相关添加键值对,全局有效
"ATOMIC_REQUESTS": True每次请求所涉及到的orm操作同属于一个事务
方式2:装饰器 被装饰的视图函数或者类有效
from django.db import transaction
@transaction.atomic
def index():pass
方式3:with 上下文管理 局部有效
from django.db import transaction
def reg():
with transaction.atomic():
pass
"""
添加事务的三种方式对应的三种不同的范围,但是针对事务的特性是不变的,当我们在执行ORM操作的时候,同一个事务(可以理解为在同一个ORM操作)中间出现错误的时候,会直接自动回滚到初始状态,比如我们一个记录数据的ORM操作,当一条创建好以后,中间出现报错,那创建好的数据直接会随着回顾而删除掉,并不会真正写入数据库
"""
ORM常用字段类型
AutoField
int自增列,必须填入参数primary_key,因此只有主键才会使用这个字段类型
CharField
字符类型,必须提供max_length参数,max_length表示字符长度
DateField
日期字段,日记格式Y-M-D,年月日,填auto_now或者auto_now_add
DateTimeField
日期时间字段,格式Y-M-D H:M:S
IntegerField
正整数
DecimalField
小数,必须提供max_digits(多少位),decimal_places(小数点后面几位)
BooleanField
传布尔值,自动存为0或者1
TextField
存储文本类型,大段文本
EmailField
存储邮箱格式数据
FileField
传文件对象,自动报错到提前配置好的路径下并存储该路径信息
ORM还支持用户自定义字段类型
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
class User(models.Model):
name = models.CharField(max_length=32)
info = MyCharField(max_length=64)
ORM常用字段参数
字段参数 | 作用 | 场景 |
---|---|---|
primary_key | 主键 | 手动设置主键的时候 |
verbose_name | 注释 | 告诉别人这个字段是什么 |
max_lengtn | 字段长度 | 配合字符串字段类型 |
decimal_places | 小数点后面的位数 | 配合小数字段类型,如:DecimalField |
max_digits | 小数总共多少为 | 配合小数字段类型,如:DecimalField |
auto_now | 每次操作数据自动更新时间 | 配合DateField或者DateTimeField |
auto_now_add | 首次创建更新时间,后续不自动更新 | 配合DateField或者DateTimeField |
null | 允许字段是否为空,null=True | 数据迁移之后添加新的字段 |
default | 默认值,default=数据值 | 数据迁移之后添加新的字段,或者创建时 |
unique | 唯一值 | 表示这个字段不能够重复 |
db_index | 给字段添加索引 | 这个字段需要被频繁查找 |
choice | 提前列表好对应关系 | 某个字段可能性能被列举完全 |
to | 关联表,写法to='关联表名' | 在外键字段里面写 |
to_field | 关联字段,不写默认关联主键 | |
on_delete | 级联删除 | 一对一或者一对多外键字段 |
choice用法展示
class User(models.Model): # 创建表
name = models.CharField(max_length=32)
info = models.CharField(max_length=64)
gender_choice = (
(1, '男性'),
(2, '女性')
) # 提前设置好对应关系
# 设置字段
gender = models.IntegerField(choices=gender_choice)
# 该字段是整型,但是有对应数据关系
user_obj = models.User.objects.filter(pk=1).first()
print(user_obj.gender) # 1,按照正常取值是数字1
print(user_obj.get_gender_display()) # 男性,按照固定方式就是拿到对应的选择
on_delete参数补充说明
"""
解释:
主表:在一对多之中,主表是一的那一方,只有一的被删除,多的才会随之删除
在一对一之间,可以理解为两张表互为主表,主要有一张表的记录被删除另一张表对应的记录也要被删除
"""
1.models.CASCADE
级联操作,当主表中被连接的一条数据删除时,从表中所有与之关联的数据同时被删除,从表中所有与之关联的数据同时被删除
2.models.SET_NULL
当主表中的一行主句删除时,从表中所有与之关联的数据的相关字段设置为null,此时注意定义外键时,这个字段必须可以允许为空,也就是外键字段要有(null=True)
3.models.PROTECT
当主表中的一行数据删除时,由于从表中相关字段是受保护的外键,所以都不循序删除
4.models.SET_DEFAULT
当主表中的一行数据删除时,从表中所有相关的数据的关联字段设置为默认值,此时注意定义外键时,这个外键字段应该有一个默认值,也就是这个外键字段要有(default=默认值)
5.models.SET()
当主表中的一条数据删除时,从表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时总表中的相关字段不需要设置default参数
6.models.DO_NOTHING
什么都不做,一切都看数据库级别的约束,数据库级别的默认约束为RESTRICT,这个约束与django中的models.PROTECT相似
多对多三种创建方式
1.全自动创建
class Book(models.Model):
name = models.CharField(max_length=32)
authors = models.ManyToManyField(to='Author')
class Author(models.Model):
name = models.CharField(max_length=32)
优势:自动创建第三张表,并且提供了add、remove、set、clear四种操作方式
劣势:第三张表由于是自动创建的,无法创建更多的字段,扩展性比较差
2.纯手动创建
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 = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')
others = models.CharField(max_length=32)
join_time = models.DateField(auto_now_add=True)
优势:第三张表完全由自己创建 扩展性强
劣势:编写繁琐 并且不再支持add、remove、set、clear以及正反向概念
3.半自动创建
class Book(models.Model):
title = models.CharField(max_length=32)
authors = models.ManyToManyField(to='Author',through='Book2Author',
through_fields=('booj','author'))
参数的意义:
to=还是关联的表
through=指定第三张表
through_fields=指定第三张表中的外键字段,多对多的外键设在哪一方,指定的表字段先是哪个
class Author(models.Model):
name = models.CharField(max_length=32)
class Book2Author(models.Model):
book = models.ForeignKey(to='Book', on_delete=models.CASCADE)
author = models.ForeignKey(to='Author', on_delete=models.CASCADE)
others = models.CharField(max_length=32)
join_time = models.DateField(auto_now_add=True)
优势:第三张表完全由自己创建 扩展性强 正反向概念依然清晰可用
劣势:编写繁琐不再支持add、remove、set、clear