Django框架:模型层
模型层
一、前期准备
1.关于数据库
-
自带的sqlite3数据库对时间字段不够敏感,展示的时候会错乱
-
使用数据库比如MySQL的时候,django的orm不会自动创建库,需要自己创建库
2.测试环境
django默认需要整个项目跑起来才能运行功能函数,如果想要测试某个py文件(主要指模型层models.py)需要准备测试环境
-
测试环境1:pycharm中的python console
但是这个测试环境是命令行式的环境,看起来不够直观
- 测试环境2:自己搭建
(1)打开manage.py文件,复制前四行
(2)并添加两行内容
import django django.setup()
- 最终test.py中的代码
from django.test import TestCase # Create your tests here. import os def main(): os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djmodel.settings') import django django.setup()
3.查看执行的SQL语句
由于Django的orm底层还是SQL语句,我们可以通过两种方法查看
-
(1)如果我们手上是一个QuerySet对象,那么可以直接点query查看SQL语句
但是不是queryset对象就不能点query
-
(2)直接配置settings
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level': 'DEBUG', }, } }
二、模型层常用关键字
1.create
-
该方法有返回值,返回值是实例对象
创建数据并且直接获取当前创建的数据对象
models.User.objects.create(name='duo', age=18) models.User.objects.create(name='bo', age=22) res = models.User.objects.create(name='hong',age=35) print(res) # 用户对象的对象是:hong
2.filter()
- 结果是QuerySet 列表套对象
- 括号内支持多个条件,但是是and关系
res = models.User.objects.filter(name='duo') print(res) # <QuerySet <QuerySet [<User: 用户对象的对象是:duo>]> # 当类中没有双下str方法的时候 res为<QuerySet [<User: User object (1)>]>
3.first() last()
QuerySet支持所用取值,但是只支持正数,并且orm不建议使用索引取值
使用索引取值没有的情况下会报错,但是使用first last方法,没有数据的情况或返回none不会报错
res = models.User.objects.filter()[1] # res = models.User.objects.filter(pk=100)[0] # 没有数据索引会报错 res1 = models.User.objects.filter(pk=100).first() # first方法不会报错 res2 = models.User.objects.filter().last() print(res) # 用户对象的对象是:bo print(res1) # None print(res2) # 用户对象的对象是:hong
4.update()
-
更新数据
根据筛选条件更新不同的个数
# 批量更新 models.User.objects.filter().update() # 单个更新 res = models.User.objects.filter(id=1).update(age=22) print(res) # 1 # sql语句 UPDATE `app01_user` SET `age` = 22 WHERE `app01_user`.`id` = 1'; args=(22, 1)
5.delete()
-
删除数据
根据筛选条件删除不同的个数
models.User.objects.filter().delete() 批量删除 models.User.objects.filter(id=1).delete() 单个删除
6.all()
-
查询所有数据
结果是QuerySet [数据对象1,数据对象2]
与filter不同
models.User.objects.all() <QuerySet [<User: 用户对象的对象是:duo>, <User: 用户对象的对象是:bo>...]>
7.values()
-
指定字段获取 数据,结果是
<QuerySet [{},{},{}]> 列表套字典
-
all().values()
-
filter.values()
-
values()
models.User.objects.all().values() models.User.objects.filter(id=1).values() models.User.objects.values() print(res) # <QuerySet [{},{},{}]> 列表套字典
8.values_list()
- 根据指定字段获取数据
- 结果是
QuerySet[(),(),(),()]
列表套元组
models.User.objects.values_list('name') ------ <QuerySet [('duo',), ('bo',), ('duoduo',), ('bo',), ('hong',)]>
9.distinct()
- 去重,但是得完全一致
10.order_by()
-
根据指定条件排序,默认升序
在条件前面加个减号- 改为降序
res = models.User.objects.all().order_by('age') <QuerySet [<对象1>, <对象2>]>
11.get()
-
根据条件筛选数据并直接获取到数据对象
条件不存在拿不到数据,则会报错
models.User.objects.get(id=1) --- 用户对象的对象是:duo
12.exclude()
排除
- 去反操作
models.User.objects.exclude(age=22) --- <QuerySet [<User: 用户对象的对象是:duoduo>, <User: 用户对象的对象是:hong>]>
13.reverse()
- 颠倒顺序,但是被操作的顺序必须需要提前排序完成
models.User.objects.order_by('age').reverse() --- <QuerySet [<User: 用户对象的对象是:hong>, <User: 用户对象的对象是:duo>, <User: 用户对象的对象是:bo>, <User: 用户对象的对象是:bo>, <User: 用户对象的对象是:duoduo>]>
14.count()
- 统计结果集中数据的个数
models.User.objects.filter(age=22).count() --- 3
15.exists()
-
判断结果集中是否含有数据,如果有则返回True 没有则返回False
当时在Django中结果已经自带了布尔值,所有
exists()
显得有些多余
models.User.objects.filter(id=1).exists() --- True
方法特点
- 返回QuerySet对象的方法有
返回QuerySet对象 |
---|
filter() |
exclude() |
order_by() |
reverse() |
distinct() |
all() |
- 特殊的QuerySet
特殊的QuerySet | 作用 |
---|---|
values() |
返回一个可迭代的字典序列 |
values_list() |
返回一个可迭代的元祖序列 |
- 返回具体对象的
返回具体对象的 |
---|
first() |
last() |
get() |
- 返回布尔值的方法有:
返回布尔值 |
---|
exists() |
- 返回数字的方法有
返回数字的方法 |
---|
count() |
三、ORM执行查询sql语句
1.方式一:通过ORM执行sql语句
通过raw
方法执行查询sql语句,只支持查询,但是结果要转换成列表查看
方式1的结果,还是会被封装对象
res = models.User.objects.raw('select * from app01_user') print(list(res)) ------------ [<User: 用户操作的对象是:duo>, <User: 用户操作的对象是:bo>, <User: 用户操作的对象是:duoduo>, <User: 用户操作的对象是:bo>, <User: 用户操作的对象是:hong>]
2.方式二:通过django封装好了的pymysql模块执行sql语句
方式2的结果,会直接是数据值
- 导入connection,产生游标对象cursor
from django.db import connection cursor = connection.cursor() cursor.execute('select name from app01_user;') print(cursor.fetchall()) ------------ (('duo',), ('bo',), ('duoduo',), ('bo',), ('hong',))
四、神奇的双下划线方法
- QuerySet可以当做1个列表
- QuerySet对象,是一个生成器
- QuerySet用.all()取值的时候,不会一下子全部取出来,LIMIT21,每次只取21条
对于QuerySet对象,就可以无限制的点QuerySet对象的方法
1.__gt
大于
结果是一个QuerySet对象
- 年龄大于30
res = models.Author.objects.filter(age__gt=30) print(res) ---------- <QuerySet [<Author: 作家对象:余华>, <Author: 作家对象:莫言>, <Author: 作家对象:东野圭吾>]>
2.__lt
小于
结果是一个QuerySet对象
res = models.Author.objects.filter(age__lt=40) print(res) ---------- <QuerySet [<Author: 作家对象:路遥>]>
3.__gte
大于等于和__lte
小于等于
"只要是querset对象,就可以无限制的点queryset对象点方法" # 查询年龄大于18的用户名的名字 res = models.User.objects.filter(age__gt=18).filter(id=2) print(res) # <QuerySet [<User: 用户操作的对象是:bo>]>
4.__in
成员运算
res = models.Author.objects.filter(age__in=(60, 30, 50)) print(res) ---------- <QuerySet [<Author: 作家对象:余华>, <Author: 作家对象:路遥>, <Author: 作家对象:东野圭吾>]>
5.__range
范围之间查询
range
后元组内填的数据,左右都包含
"range填的数据,左右都包含" res = models.Author.objects.filter(age__range=(30, 60)) print(res)
6.__contains
和 __icontains
模糊查询
-
__contains
查询字段名中含有某个字母的数据 (区分大小写) -
__icontains
查询字段名中含有某个字母的数据 (不会区分大小写)
res1 = models.Author.objects.filter(author_detail__manner__contains='s') print(res1) ---------- __contains查询时区分大小写 res2 = models.Author.objects.filter(author_detail__manner__icontains='s') print(res2) ---------- __icontains查询时候不区分大小写
7.查询时间关键字
__year
年__month
月__day
日__hour
时

res = models.Book.objects.filter(publish_time__year=2022).all() print(res)
五、ORM外键字段的创建
1.mysql中的外键关系
-
一对多:外键字段建立在多的一方
-
多对多:外键字段统一建在第三张关系表中,在django中不用自己创建,只需创建外键后django会帮我们自动创建一个表出来
-
一对一:建在任何一方都可以,但是应该建立在热数据表也就是查询频率较高的表中
关系的判断采用换位思考原则
2.orm外键字段的创建
- 先创建基表,再创建外键字段
(1)一对多,ORM与MySQL
- orm会自动帮我们把外键字段后加
_id
的后缀,所以我们不用自己添加_id
后缀
(2)多对多
- ORM比MySQL更多变化
- ORM:外键字段可以直接建在某张查询频率较高的表中,在orm内部会自动帮我们创建第三张表,这个外键字段是虚拟字段在表中并不会显示,只是告诉orm帮我们创建第三张关系表
- MySQL:自己创建第三张关系表并创建外键字段
(3)一对一
- 外键字段可以直接建在某张查询频率较高的表中
通过
ForiegnKey
和OneToOne
创建外键的关键字,同步到表中之后,会自动添加_id的后缀而多对多的外键关键字
MangToMany
创建的是虚拟外键,是告诉ORM帮我们创建多对多的关系表
(实例)图书、出版社、作者数据表
"""先创建几张基表,再再几张基表之上去创建表与表之间的关系""" class Book(models.Model): """图书表""" title = models.CharField(max_length=32, verbose_name='书名') price = models.DecimalField(max_digits=8, decimal_places=3, verbose_name='价格') publish_time = models.DateTimeField(auto_now_add=True, verbose_name='出版日期') "书籍表和出版社表是一对多的关系,也就是一本书对应一个出版社,一个出版社对应多本书,所以外键字段建立在多的那方" # 与出版社的外键,通过ForeignKey,参数to为建立关系的类名 publish = models.ForeignKey(to='Publisher', on_delete=models.CASCADE) "CASCADE对应的是及联更新删除,在django1.X版本都是默认及联更新删除,但是在django2以上版本需要自己声明 " "书籍表与作者表的关系是多对多,一本书可以对应多个作者,一个作者也可以对应多本书,所以外键字段建立在那方都可以,但是书籍表更为常用,所以建立在数据表中" # 与作者的外键,但是对于Book表来说这是一个虚拟字段,相当于是告诉ORM在MySQL数据库中去建立一张新表书籍与作者的关系表 author = models.ManyToManyField(to='Author') "多对多的表不需要建立及联更新删除" def __str__(self): return f"书籍对象:{self.title}" class Publisher(models.Model): """出版社表""" name = models.CharField(max_length=32, verbose_name='出版社名') location = models.CharField(max_length=32, verbose_name='出版社地址') def __str__(self): return f"出版社对象:{self.name}" class Author(models.Model): """作者表""" name = models.CharField(max_length=32, verbose_name='作者名') age = models.IntegerField(verbose_name='年龄') "作者表和作者详情表是一对一的关系,一个作者可以对应一个作者详情,一个作者详情对应一个作者,外键建在哪方都可以,但是建立在热数据的表中更好" author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE) def __str__(self): return f"作家对象:{self.name}" class AuthorDetail(models.Model): """作者详情表""" phone = models.BigIntegerField(verbose_name='手机号') manner = models.CharField(max_length=32, verbose_name='写作风格') def __str__(self): return f"详情对象:{self.manner}"
3.增删改查
-
一对多
book表
针对一对多,插入数据可以之间填写表中的实际数据
也可以填写表中的虚拟字段(类中的字段名)
一对一与一对多一致
- 多对多
第三张orm自动创建的关系表,在orm中点不出来,想要添加关系得通过 含有多对多外键字段的对象去添加 多对多关系
(1)添加数据
对于一对多关系
- 1.直接插入数据
models.Book.objects.create(title='兄弟', price=999.87, publish_id=3) models.Book.objects.create()
- 2.插入对象
publisher_obj = models.Publisher.objects.filter(pk=2).first() models.Book.objects.create(title='红高粱', price=888.68, publish=publisher_obj) models.Book.objects.filter(pk=10).delete() models.Book.objects.filter(pk=8).delete()
(2)多对多绑定关系
多对多
- 1.通过对象来添加数据
book_obj = models.Book.objects.filter(pk=3).first() book_obj.author.add(3,2)
- 2.通过对象来添加对象
book_obj1 = models.Book.objects.filter(pk=4).first() author_obj1 = models.Author.objects.filter(pk=1).first() author_obj2 = models.Author.objects.filter(pk=2).first() book_obj1.author.add(author_obj1,author_obj2)
(3)修改关系
obj.表名小写.set()
中set的参数应该是一个可迭代对象列表或者元组
- 1.参数为数据
book_obj3 = models.Book.objects.filter(pk=3).first() book_obj3.author.set([3])
- 2.参数为对象
book_obj3 = models.Book.objects.filter(pk=3).first() author_obj2 = models.Author.objects.filter(pk=2).first() author_obj1 = models.Author.objects.filter(pk=1).first() book_obj3.author.set((author_obj2, author_obj1))
(4)删除关系
- 1.参数为数据
book_obj3 = models.Book.objects.filter(pk=3).first() book_obj3.author.remove(1)
- 2.参数为对象
author1 = models.Author.objects.filter(pk=1).first() book_obj4 = models.Book.objects.filter(pk=4).first() book_obj4.author.remove(author1)
六、ORM的跨表查询
- 一个数据库应该尽量只对应一个Django项目,不要出现多个django项目使用同一额数据库,会出现报错
1.mysql中的跨表查询思路
(1)子查询
- 分布操作,将一个查询结果当作另一个表的条件
(2)连表查询
- inner join
- left join
- right join
2.正反向查询的概念
- 概念
正向查询:由外键字段所在的表数据查询关联的表数据
反向查询:没有外键字段的表数据查询关联的表数据
- ORM跨表查询的口诀(重要):
正向查询按外键字段
反向查询按表名小写
3.基于对象的跨表查询(子查询)
相当于mysql中的子查询
-
(1)先根据已知条件获取到一个具体的数据对象
-
(2)基于该数据对象运用正反向查询的口诀完成
1.正向
- 1.查询主键为1的书籍对应的出版社名称
# 先根据条件获取数据对象 book_obj = models.Book.objects.filter(pk=1).first() # 在判断正反向的概念,由书获取出版社 --》从由主键的表差没主键的表是正向 res = book_obj.publish.name print(res) # 长江文艺
- 2.查询主键为4的书籍对应的作者姓名
# 先根据条件获取对象 book_obj = models.Book.objects.filter(pk=4).first() res = book_obj.author.name print(res) 这个时候点name得到的结果是None,然后只要点all就可以拿到所有点对象,在通过values 获取想要点字段名点值 res = book_obj.author.all().values('name') print(res) #<QuerySet [{'name': '莫言'}, {'name': '路遥'}]>
- 3.查询余华的电话号码
# 先根据条件获取对象 author_obj = models.Author.objects.filter(name='余华').first() res = author_obj.author_detail.phone print(res) #111
2.反向
- 4.查询长江文艺出版过的书籍
# 先根据条件获取数据对象 publish_obj = ls.Publisher.objects.filter(name='长江文艺').first() # 根据数据对象判断 出版社没有外键字段,所以是反向,所以要点表名小写,然后 res = publish_obj.book_set.all().values('title') print(res) -------------- <QuerySet [{'title': '活着'}, {'title': '平凡的世界'}, {'title': '嫌疑人x的献身'}]>
- 5.查询余华写过的书籍
# 先根据条件获取数据对象 author_obj = models.Author.objects.filter(name='余华').first() # 作者表中没有外键,所以是反向,要点获取表名的小写 res = author_obj.book_set.all().values('title') print(res) -------------- <QuerySet [{'title': '活着'}, {'title': '细雨中的呼喊'}, {'title': '兄弟'}]>
- 6.查询电话号码是111的作者姓名
# 先根据条件获取数据对象 author_detail_obj = models.AuthorDetail.objects.filter(phone=111).first() res = author_detail_obj.author.name print(res) # 余华
4.基于上下划线的跨表查询
- 反向的口诀也使用与values或者values_list中使用
一条出结果
1.正向
- 1.查询主键为1的书籍对应的出版社名称
res = models.Book.objects.filter(pk=1).values('publish__name') print(res) #<QuerySet [{'publish__name': '长江文艺'}]>
- 2.查询主键为4的书籍对应的作者姓名
res = models.Book.objects.filter(pk=4).values('author__name') print(res) # <QuerySet [{'author__name': '莫言'}, {'author__name': '路遥'}]>
- 3.查询余华的电话号码
res = models.Author.objects.filter(name='余华').values('author_detail__phone') print(res) # <QuerySet [{'author_detail__phone': 111}]>
2.反向
- 4.查询长江文艺出版过的书籍
res = models.Publisher.objects.filter(name='长江文艺').values('book__title') print(res) #<QuerySet [{'book__title': '活着'}, {'book__title': '平凡的世界'}, {'book__title': '嫌疑人x的献身'}]>
- 5.查询余华写过的书籍
res = models.Author.objects.filter(name='余华').values('book__title') print(res) # <QuerySet [{'book__title': '活着'}, {'book__title': '细雨中的呼喊'}, {'book__title': '兄弟'}]>
- 6.查询电话号码是111的作者姓名
res = models.AuthorDetail.objects.filter(phone=111).values('author__name') print(res)
5.进阶操作
- 1.查询主键为1的书籍对应的出版社名称
res = models.Publisher.objects.filter(book__pk=1).first().name print(res) # 长江文艺 res2 = models.Publisher.objects.filter(book__pk=1).values('name') print(res2) # <QuerySet [{'name': '长江文艺'}]>
- 2.查询主键为4的书籍对应的作者姓名
res = models.Author.objects.filter(book__pk=1).first().name print(res) # 余华 res1 = models.Author.objects.filter(book__id=4).values('name') print(res1) # <QuerySet [{'name': '莫言'}, {'name': '路遥'}]>
- 3.查询余华的电话号码
res = models.AuthorDetail.objects.filter(author__name='余华').values('phone') print(res) # <QuerySet [{'phone': 111}]>
- 4.查询长江文艺出版过的书籍
res = models.Book.objects.filter(publish__name='长江文艺').first().title print(res) # 活着 res1 = models.Book.objects.filter(publish__name='长江文艺').values('title') print(res1) # <QuerySet [{'title': '活着'}, {'title': '平凡的世界'}, {'title': '嫌疑人x的献身'}]>
- 5.查询余华写过的书籍
res = models.Book.objects.filter(author__name='余华').values('title') print(res) # <QuerySet [{'title': '活着'}, {'title': '细雨中的呼喊'}, {'title': '兄弟'}]> res1 = models.Book.objects.filter(author__name='余华').first().title print(res1) # 活着
- 6.查询电话号码是111的作者姓名
res = models.Author.objects.filter(author_detail__phone=111).values('name') print(res) # <QuerySet [{'name': '余华'}]>
- 查询主键为4的书籍对应的作者的电话号码
# 通过作者表查询 res = models.Author.objects.filter(book__pk=4).values('author_detail__phone') print(res) # 通过书籍表查询 res1 = models.Book.objects.filter(pk=4).values('author__author_detail__phone') print(res1) # 通过作者详情表查询 res2 = models.AuthorDetail.objects.filter(author__book__pk=4).values('phone') print(res2) ------------ <QuerySet [{'author_detail__phone': 222}, {'author_detail__phone': 33}]> <QuerySet [{'author__author_detail__phone': 222}, {'author__author_detail__phone': 33}]> <QuerySet [{'phone': 222}, {'phone': 33}]>
- 修改外键字段的增删改查
七、图书管理系统讲解
1.表设计
- 先考虑普通字段,再考虑外键字段
2.网页搭建
- 首页搭建
- 图书列表展示页面
八、 聚合查询
聚合函数:max min sum count avg,一般和分组查询配合使用
# 导入聚合函数 from django.db.models import Max,Min,Sum,Count,Avg
在ORM中指出单独使用聚合函数,通过关键字aggregate
from django.db.models import Max, Min, Sum, Count, Avg models.Book.objects.aggregate(Max('price'), Count('id'), 最小价格=Min('price'), ) -------- {'最小价格': Decimal('32.450'), 'price__max': Decimal('999.870'), 'id__count': 8}
九、分组查询
通过关键字annotate
,进行分组查询
1.分组依据
-
models后面点什么就是按照什么分组
分组依据:
models.表名.objects.annotate() 按照整张表分组 models.表名.objects.values('字段名').annotate 按照values括号内指定的字段分组
2.例
- 1.查询每一本书的作者
models.Book.objects.annotate(author_sum=Count('author__pk')).values('title','author_sum') ------- <QuerySet [{'title': '活着', 'author__num': 1}, {'title': '细雨中的呼喊', 'author__num': 1}, {'title': '蛙', 'author__num': 1}, {'title': '平凡的世界', 'author__num': 2}, {'title': '嫌疑人x的献身', 'author__num': 1}, {'title': '白夜行', 'author__num': 1}, {'title': '兄弟', 'author__num': 1}, {'title': '红高粱', 'author__num': 1}]>
- 2.筛选作者数量大于1的书
models.Book.objects.annotate(author__num=Count('author__pk')).filter(author__num__gt=1).values('title') ----------- <QuerySet [{'title': '平凡的世界'}]>
- 3.查询每个作者的书的总价格
models.Author.objects.annotate(总价=Sum('book__price')).values('name','总价') ----------- <QuerySet [{'name': '余华', '总价': Decimal('1198.080')}, {'name': '莫言', '总价': Decimal('976.110')}, {'name': '路遥', '总价': Decimal('32.450')}, {'name': '东野圭吾', '总价': Decimal('151.070')}]>
如果执行orm分组查询报错,并且有关键字sql_
show variables like '%mode%'
十、F查询与Q查询
1.F查询
当查询条件不是明确的,也需要从数据库中获取的时候,就需要使用F查询,获取表中指定字段对应的数据作为查询条件
from django.db.models import F

- 1.将所有书的价格减少800
models.Book.objects.update(price=F('price')-800)

-
2.将所有书的名称后面追加爆款
针对字符串类型的数据拼接需要使用模块:
Concat
字符串拼接 和Value
from django.db.models.functions import Concat from django.db.models import Value models.Book.objects.update(title=Concat(F('title'), Value('新款')))
2.Q查询
可以将多个查询条件的关系做修改,Q查询中间的符号逗号是and,|是or,~是not
res = models.Book.objects.filter(Q(pk=1), Q(price__gt=80)) res2 = models.Book.objects.filter(Q(pk=1)| Q(price__gt=80)) res3 = models.Book.objects.filter(~Q(pk=1)| Q(price__gt=80)) print(res) print(res2) print(res3) <QuerySet [<Book: 书籍对象:活着>]> <QuerySet [<Book: 书籍对象:活着>, <Book: 书籍对象:细雨中的呼喊>, <Book: 书籍对象:兄弟>, <Book: 书籍对象:红高粱>]> <QuerySet [<Book: 书籍对象:活着>, <Book: 书籍对象:细雨中的呼喊>, <Book: 书籍对象:蛙>, <Book: 书籍对象:平凡的世界>, <Book: 书籍对象:嫌疑人x的献身>, <Book: 书籍对象:白夜行>, <Book: 书籍对象:兄弟>, <Book: 书籍对象:红高粱>]>
3.Q查询进阶操作
可以使得查询条件的左侧变成字符串
查询条件的左边是字段名/变量名
q_obj = Q() # 1.产生q对象 q_obj.connector = 'or' # 默认多个条件的连接是and可以修改成or q_obj.children.append(('pk', 1)) # 2.添加查询条件 q_obj.children.append(('price__gt', 100)) # 支持添加多个查询条件 res = models.Book.objects.filter(q_obj) # 查询支持直接添写q对象 res1 = models.Book.objects.filter(q_obj).first() # 查询支持直接添写q对象 print(res) <QuerySet [<Book: 书籍对象:活着>, <Book: 书籍对象:细雨中的呼喊>, <Book: 书籍对象:兄弟>, <Book: 书籍对象:红高粱>]> print(res1) 书籍对象:活着 print(res1.author.all()) <QuerySet [<Author: 作家对象:余华>]>
十一、ORM查询优化
1.ORM的查询特点
(1)ORM的查询默认都是惰性查询
当写了一条ORM,在我们不用的时候,ORM就不会启用这条ORM语句进行查询,要用的时候才会去访问数据库
(2)ORM的查询自带分页处理
ORM每次查询的时候,最多21条
(3)查询关键字only与defer (去反操作)
-
only
only
可以将括号内列举的字段封装到数据对象内,当我们通过点点方式获取括号内的字段对应的值时,直接通过封装好的对象获取;当我们没有想获取括号外的字段时,就会通过sql语句进行查询

-
defer
与only相反
defer
可以将括号内列举的字段封装到数据对象内,当我们通过点点方式获取括号内的字段对应的值时,会通过sql语句查询;当我们没有想获取括号外的字段时,则直接获取

(4)sql优化
-
select_related
底层是连表操作对于一对一字段(OneToOneField)和外键字段(ForeignKey,一对多),可以使用
select_related
来对QuerySet进行优化在对QuerySet使用s
elect_related()
函数后,Django会获取相应外键对应的对象,从而在之后需要的时候不必再查询数据库
model.XXX.objects.all().select_related('外键字段') model.XXX.objects.all().select_related('外键字段__外键字段')
-
prefetch_related
底层子查询对于一对一字段(OneToOneField)和一对多字段(ForeignKey),可以使用
prefetch_related()
来进行优化。将
prefetch_related()
括号内填写的一对一、一对多字段,基于子查询将其结果数据封装成对象。
但是两者在使用上的时候差不多
预取prefetch
v.预先载入 n.预先载入
十二、ORM事务操作
1.事务的四大特性 ACID
A:atomicity原子性,一个事务是一个不可分割的工作单位,要么同时成功,要么同时失败
C:consistency一致性,事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
I:isolation隔离性,一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
D:durability持久性,持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
2.SQL相关
(1)关键字
- 开启事务
START TRANSACTION;
- 手动提交事务
COMMIT;
- 取消事务(即回滚)
ROLLBACK;
- 设置保存点
SAVEPOINT
(2)事务的隔离级别
MySQL中,事务有4种隔离级别,分别为READ UNCOMMITTED(读未提交)、READ COMMITTED(读已提交)、REPEATABLE READ(可重复读)和SERIALIZABLE(串行化)。
- READ UNCOMMITTED 未提交读-脏读
事务A读取到了事务已经修改但未提交的数据,这种数据就叫脏数据,是不正确的
- READ COMMITTED 读已提交
不可重复读:对于事务A多次读取同一个数据时,由于其他是事务也在访问这个数据,进行修改且提交,对于事务A,读取同一个数据时,有可能导致数据不一致,叫不可重复读
- REPEATABLE READ 可重复读-幻读
幻读:原因:因为mysql数据库读取数据时,是将数据放入缓存中,当事务B对数据库进行操作:例如删除所有数据且提交时,事务A同样能访问到数据,这就产生了幻读。
问题:解决了可重复读,但是会产生一种问题,错误的读取数据,对于其他事务添加的数据也将访问不到
- SERIALIZABLE 串行化
串行化:事务A和事务B同时访问时,在事务A修改了数据,而没有提交数据时,此时事务B想增加或修改数据时,只能等待事务A的提交,事务B才能够执行。
3.ORM事务相关操作
-
方式一:配置文件数据库添加相关键值对
有效范围:全局有效
"ATOMIC_REQUESTS":True
每次请求涉及的orm操作同属于一个事务
如果报错会自动回滚
-
方式二:通过装饰器配置事务
有效范围:局部有效
from django.db import transaction @transaction.atomic def 函数名(request): orm/sql
-
方式三:with上下文管理
有效范围:代码块有效
将with体代码中的orm或者sql语句支持事务
def 函数名(request): with transacion.atomic(): orm/sql
十三、ORM模型层字段类型
1.常用字段
字段类型 | 作用 | 特点 |
---|---|---|
AutoField | 自增 | 必须填入参数primary_key=True ,如果没有该自增列django则会自动创建 |
CharField | 字符串类型 | 必须提供提供max_length 参数,来表示字符长度 |
IntergerField | 整形 | 范围:-2147483648 ~ 2147483647 |
BigIntergerField | 长整型 | -9223372036854775808 ~ 9223372036854775807 |
DecimalField | 10进制小数 | 参数:max_digits :最大长度decimal_places :小数点后几位 |
DateField | 日期类型 | 格式:YYYY-MM-DD 年-月-日 |
DateTimeField | 日期类型 | 格式:YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] 年-月-日 时-分-秒 |
BooleanField | 布尔值类型 | 传布尔值自动存0或者1 |
FileField | 文件类型 属于字符串类型 |
传文件对象,自动保存到提前配置好的路径下,并在数据库保存路径 upload_to = "":上传文件的保存路径 storage = None:存储组件,默认django.core.files.storage.FileSystemStorage |
TextField | 文本类型 | 存储大文本 |
EmailField | 邮箱类型 属于字符串类型 |
Django Admin以及ModelForm中提供验证机制 |
ImageField | 图片类型 属于字符串类型 |
路径保存在数据库,文件上传到指定目录 upload_to = "":上传文件的保存路径 storage = None:存储组件,默认django.core.files.storage.FileSystemStorage width_field=None:上传图片的高度保存的数据库字段名(字符串) height_field=None:上传图片的宽度保存的数据库字段名(字符串) |
2.不常用字段
字段 | 类型 | 特点 |
---|---|---|
BigAutoField | bigint自增列 | 必须填入参数 primary_key=True 当model中如果没有自增列,则自动会创建一个列名为id的列 |
SmallIntegerField | 小整数 | 范围:-32768 ~ 32767 |
PositiveSmallIntegerField | 正小整数 | 范围:0 ~ 32767 |
PositiveIntegerField | 正整数 | 范围:0 ~ 2147483647 |
BigIntegerField | 长整型(有符号的) | 范围:-9223372036854775808 ~ 9223372036854775807 |
BooleanField | 布尔值 | 有True和False |
NullBooleanField | 可以为空的布尔值 | 可以为空 |
IPAddressField | IP类型 属于 字符串类型 | Django Admin以及ModelForm中提供验证 IPV4 机制 |
GenericIPAddressField | 属于 字符串类型 | 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 | URL类型 属于 字符串类型 | Django Admin以及ModelForm中提供验证 URL |
SlugField | 属于 字符串类型 | Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号) |
CommaSeparatedIntegerField | 属于 字符串类型 | 格式必须为逗号分割的数字 |
UUIDField | 属于 字符串类型 | Django Admin以及ModelForm中提供对UUID格式的验证 |
FilePathField | 文件路径类型 属于 字符串类型 | Django Admin以及ModelForm中提供读取文件夹下文件的功能 path:文件夹路径 match=None:正则匹配 recursive=False:递归下面的文件夹 allow_files=True:允许文件 allow_folders=Fals:允许文件夹 |
TimeField | 时间类型 | HH:MM[:ss[.uuuuuu]] 时分秒 |
DurationField | 长整数 | 时间间隔,数据库中按照bigint存储 ORM中获取的值为datetime.timedelta类型 |
FloatField | 浮点型 | 可以保存小数 |
BinaryField | 二进制型 | 存储二进制 |
3.自定义字段
(1)继承models.Field
(2)使用两个方法__init__
和db_type
class MyCharField(models.Field): def __init__(self, max_length, *args, **kwargs): self.max_length = max_length super().__init__(max_length=max_length, *args, **kwargs) # max_length需要按照关键字参数的方式传进去 def db_type(self, connection): return 'char(%s)' % self.max_length
十四、常用字段参数
1.常用属性
字段 | sql语句 |
---|---|
null | null=True 该字段可以为空 |
unique | unique=True 该字段是唯一的 |
default | 设置默认值,一般设置可以为null |
db_index | 建立索引字段加快搜索 |
max_digits | 小数最大长度 |
max_length | 最大长度 |
decimal_places | 小数点后面几位数 |
auto_now | 当修改的时候自动保存当前时间 |
auto_now_add | 当创建时自动保存当前时间 |
primary_key | 主键 |
verbose_name | 注释 |
choices | 当某个字段的可能性能够被列举完全的情况下使用 |
class MyCharField(models.Field): def __init__(self, max_length, *args, **kwargs): self.max_length = max_length super().__init__(max_length=max_length, *args, **kwargs) # max_length需要按照关键字参数的方式传进去 def db_type(self, connection): return 'char(%s)' % self.max_length # choices 列举可能的完全性 class User(models.Model): name = models.CharField(max_length=32) info = MyCharField(max_length=64) # 提前列举好对应关系 gender_choice = ( (1, '男性'), (2, '女性'), (3, '其他'), ) # 存取的还是数字,但是只要是在gender_choice中有对应关系的,取出来就是男性或者女性 gender = models.IntegerField(choices=gender_choice,null=True)
-
choices
在pycharm的console中演示
>>> from app01 import models >>> user_obj = models.User.objects.filter(pk=1).first() >>> user_obj.get_gender_display() '男性'
通过
obj.get_关联字段_display()
的方式来获取对应关系
2.外键字段参数
外键字段参数 | 作用 |
---|---|
to | 设置要关联的表 |
to_field | 设置要关联的表的字段 |
on_delete | 当删除关联表中的数据时,当前表与其关联的行为 |
-
on delete的参数配置
常用:
models.CASCADE
删除关联数据,与之关联也删除
1、models.CASCADE 级联操作,当主表中被连接的一条数据删除时,从表中所有与之关联的数据同时被删除 2、models.SET_NULL 当主表中的一行数据删除时,从表中所有与之关联的数据的相关字段设置为null,此时注意定义外键时,这个字段必须可以允许为空 3、models.PROTECT 当主表中的一行数据删除时,由于从表中相关字段是受保护的外键,所以都不允许删除 4、models.SET_DEFAULT 当主表中的一行数据删除时,从表中所有相关的数据的关联字段设置为默认值,此时注意定义外键时,这个外键字段应该有一个默认值 5、models.SET() 当主表中的一条数据删除时,从表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时从表中的相关字段不需要设置default参数 6、models.DO_NOTHING 什么都不做,一切都看数据库级别的约束,注数据库级别的默认约束为RESTRICT,这个约束与django中的models.PROTECT相似
十五、多对多三种创建方式
1.全自动创建
class Book(models.Model): ... author = models.ManyToManyField(to='Author') class Author(models.Model): ...
- 优势:自动创建第三章表,并且提供了add,remove,set,clear四种操作关系
- 劣势:第三表扩展性很差,无法创建更多的字段
2.纯手动创建
class Book(models.Model): ... class Author(models.Model): ... # 第三张表 class Book2Author(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to='Author') # 其他更多字段 ...
- 优势:第三张表完全由自己创建,扩展性强
- 劣势:编写繁琐,并且不支持add,remove,set,clear四种操作关系,也没有正反向的概念
3.半自动创建
class Book(models.Model): ... # 在表中创建外键字段,但是要通过参数告诉orm我们已经创建了外键关系的第三章表 author = models.ManyToManyField(to='Author', trough='Book2Author', trough_fields=('book','author') ) class Author(models.Model): ... # 第三张表 class Book2Author(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to='Author') # 其他更多字段 ...
- 优势:第三张表完全自己创建,扩展性强,正反向的概念可以运用
- 劣势:但是不支持add,remove,set,clear四种操作关系
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY