【一】Django框架之生命周期流程图
【二】基础准备
【1】测试脚本
(1)介绍
- 在django项目中,
test.py
文件是用于存放测试代码的地方 - 在这个文件中,可以编写对模型、视图、表单、模板等测试代码
- 这个文件通常位于每一个应用目录中(没有的话可以创建)
- 你可能会好奇,为啥我们自己创建一个文件直接导入模型层的models不就可以操作了吗
- 因为django项目内的东西是不提供向外的
- 所以我们需要一些特殊准备来搭建
(2)搭建测试环境
-
首先去
mange.py
文件中复制pythonimport os if __name__ == '__main__': os.environ.setdefault('DJANGO_SETTINGS_MODULE', '项目名.settings')
-
然后写下这两句话
python# 导入django import django # 代码启动django django.setup()
- 注意:必须要写在
if __name__ == '__main__':
中
- 注意:必须要写在
-
最终样子示例
- 前两行tets.py文件自带
python
from django.test import TestCase
# Create your tests here.
import os
if __name__ == '__main__':
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '项目名.settings')
import django
django.setup()
# 测试代码编写
from 应用名 import models
【2】配置数据库
-
django自带的sqlite3数据库对日期格式不敏感,改用mysql
-
首先需要去mysql数据库中创建一个数据库
django_test
mysqlcreate database if not exists django_test;
python
DATABASES = {
"default": {
"ENGINE": 'django.db.backends.mysql',
# 数据库名字
"NAME": "django_test",
"USER": 'root',
"PASSWORD": "000",
"HOST": "localhost",
"PORT": 3306,
"CHARSET": 'utf8mb4',
}
}
- 在项目的
__init__.py
文件中声明数据据库类型
python
import pymysql
pymysql.install_as_MySQLdb()
【三】单表操作
【0】准备
(1)知识补充
- 结果是
queryset
- 那么就支持链式操作 ,拥有惰性加载的特点
- 可以链式执行所有
queryset
支持的所有操作 QuerySet
看起来像一个列表套字典,但它并不是真正的列表。
- 条件语句中
- 可以通过
pk=主键值
查询 - 代替了主键字段的关键字,很统一
- 可以通过
(2)创建表
- 在
models.py
文件中创建需要的表
python
from django.db import models
# Create your models here.
class User(models.Model):
# 不写也会自动创建,主键id
id = models.AutoField(primary_key=True, verbose_name='主键')
# 用户名 字符串 最长32位
name = models.CharField(max_length=32)
# 年龄 整型
age = models.IntegerField()
# 注册时间 日期类型
register_time = models.DateField()
- 数据迁移
python
# 生成
python manage.py makemigrations
# 迁移
python manage.py migrate
【1】数据的增加
(1)语法
- 方式一(不推荐)
- 使用类名创建
- 然后执行save操作
- 方式二(推荐 )
- 使用类的objects下的create方法
- 有返回值,是创建的对象本身
object
(2)示例
python
from django.test import TestCase
# Create your tests here.
import os
if __name__ == '__main__':
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_test.settings')
import django
django.setup()
# 测试代码编写
from app02 import models
# 注册时间可以是字符串
res = models.User.objects.create(name="bruce", age=18, register_time='2018-8-8')
print(res) # User object (1)
# 注册时间可以是日期对象
import datetime
register_time = datetime.date(2019, 8, 12)
print(type(register_time)) # <class 'datetime.date'>
res = models.User.objects.create(name='tom', age=22, register_time=register_time)
print(res) # User object (2)
【2】数据的查询
(1)语法
-
查询所有
- 使用
all()方法
(推荐) - 使用
filter()方法
,不给定条件 - 返回类型是
queryset
- 使用
-
查询指定匹配元素方法1(不推荐)
- 使用
get()方法
,给定条件,条件之间是and关系 - 返回类型是对象
object
- 有多个对象满足条件将报错
- 没有对象满足条件也会报错
- 使用
-
查询指定匹配元素方法2(推荐)
- 使用
filter()
方法,查询到所有 - 然后
first()
取出第一个 - 结果是object
- 使用
-
查询满足条件的所有(推荐)
- 使用
filter()方法
,给定条件,条件之间是and关系 - 返回类型是
queryset
- 使用
-
查询满足条件的其他所有
- 使用exclude()方法
- 和
filter()
方法相反
(2)示例
python
# 查询所有
res = models.User.objects.all()
print(res)
# <QuerySet [<User: User object (1)>, <User: User object (2)>]>
# 查询单个
res = models.User.objects.get(age=18)
print(res)
# User object (1)
# 查询满足条件的多个
res = models.User.objects.filter(age=18)
print(res)
# <QuerySet [<User: User object (1)>]>
【3】数据的更改
(1)语法
-
更改单个
- 先
filter().first()或者get()
能拿到单个object - 然后通过
.字段=新值
进行修改 - 最后执行
.save()
操作
- 先
-
批量更改(推荐)
- 先filter()方法查询
- 然后直接update()方法更新
- 可以写在一行
- 返回结果是影响的行数,不在是
queryset
(2)示例
python
# 将主键是2的用户年龄改成18
res = models.User.objects.filter(pk=2).update(age=18)
print(res)
# 1
【4】数据的删除
(1)语法
- 删除单个
- 先使用
.filter().first()
方法查询object对象 - 然后执行
delete()
方法
- 先使用
- 批量删除
- 先使用
filter()方法
查询 - 然后d
elete()方法
删除 - 可以写在一行
- 返回结果是元组(个数,删除的对象)
- 先使用
(2)示例
python
# 将年龄是18的用户删除
res = models.User.objects.filter(age=18).delete()
print(res, type(res))
# (2, {'app02.User': 2}) <class 'tuple'>
【5】其他简单查询操作
(0)数据重新添加
- 重置表
mysql
truncate user
- 重新添加数据
python
1,bruce,18,2018-08-08
2,tom,18,2020-08-01
3,lucy,17,2019-05-06
4,lily,20,2020-05-07
(1)查询指定字段数据values
- values(field, field...)
- 返回结果是
queyset
,列表套字典,但并非这么简单
python
res = models.User.objects.values('name', 'age')
print(res)
# <QuerySet [{'name': 'bruce', 'age': 18},
# {'name': 'tom', 'age': 18},
# {'name': 'lucy', 'age': 17},
# {'name': 'lily', 'age': 20}]>
(2)查询指定字段数据values_list
-
values_list(field, field...)
-
返回结果是queryset,列表套元组,但并非这么简单
python
res = models.User.objects.values_list('name', 'age')
print(res)
# <QuerySet [('bruce', 18),
# ('tom', 18),
# ('lucy', 17),
# ('lily', 20)]>
(3)去重distinct
-
直接使用
distinct()
方法,一定是去不了重复的- 因为有一个主键字段在里面
- 主键字段不可能重复,那么就没有重复数据了
-
distinct()
方法可以用在queryset
对象上- 所以可以先用
values
方法限制指定字段在去重
- 所以可以先用
python
# 无法去重
res = models.User.objects.distinct()
print(res)
# <QuerySet [<User: User object (1)>, <User: User object (2)>, <User: User object (3)>, <User: User object (4)>]>
# 指定字段
res = models.User.objects.values("age").distinct()
print(res)
# <QuerySet [{'age': 18}, {'age': 17}, {'age': 20}]>
(4)排序order_by
-
升序order_by(field, filed)
- 可以指定多个字段
-
降序order_by(-filed)
-
结果任然是
queryset
python
# 按照年龄降序
res = models.User.objects.order_by('-age')
print(res)
# <QuerySet [<User: User object (4)>, <User: User object (1)>, <User: User object (2)>, <User: User object (3)>]>
# 按照年龄升序,年龄相同按id降序
res = models.User.objects.order_by('age', "-id")
print(res)
# <QuerySet [<User: User object (3)>, <User: User object (2)>, <User: User object (1)>, <User: User object (4)>]>
(5)反转reverse
- 需要先排序,才可以反转
- 要不然不起作用
- 返回的任然是
queryset
python
# 直接反转,没用
res = models.User.objects.reverse()
print(res)
# <QuerySet [<User: User object (1)>, <User: User object (2)>, <User: User object (3)>, <User: User object (4)>]>
# 先排序,在反转
res = models.User.objects.order_by('age').reverse()
print(res)
# <QuerySet [<User: User object (4)>, <User: User object (1)>, <User: User object (2)>, <User: User object (3)>]>
(6)统计个数count
- 统计个数
- 返回结果是整型个数,不再是
queryset
python
# 直接统计
res = models.User.objects.count()
print(res)
# 统计年龄18的个数
res = models.User.objects.filter(age=18).count()
print(res)
(7)反向查询exclude
- 和
filter
相反 - 结果是
queryset
python
# 查询年龄除18岁以外的用户
res = models.User.objects.exclude(age=18)
print(res)
# <QuerySet [<User: User object (3)>, <User: User object (4)>]>
(8)是否存在exists
- 检查queryset是否含有对象
- 没有对象False,有对象True
python
res = models.User.objects.filter(age=100).exists()
print(res)
# False
【6】双下划线查询
- 结果都是
queryset
(1)比较和范围查询
- 大于:
__gt
python
res = models.User.objects.filter(age__gt=18)
print(res)
# <QuerySet [<User: User object (4)>]>
- 小于:
__lt
python
res = models.User.objects.filter(age__lt=18)
print(res)
# <QuerySet [<User: User object (3)>]>
- 大于等于:
__gte
python
res = models.User.objects.filter(age__gte=18)
print(res)
# <QuerySet [<User: User object (1)>, <User: User object (2)>, <User: User object (4)>]>
- 小于等于:
__lte
python
res = models.User.objects.filter(age__lte=18)
print(res)
# <QuerySet [<User: User object (1)>, <User: User object (2)>, <User: User object (3)>]>
- 两个条件之间范围(闭区间):
__range
python
res = models.User.objects.filter(age__range=[16,18])
print(res)
# <QuerySet [<User: User object (1)>, <User: User object (2)>, <User: User object (3)>]>
- 多个指定条件之前:
__in
python
res = models.User.objects.filter(age__in=[16, 18])
print(res)
# <QuerySet [<User: User object (1)>, <User: User object (2)>]>
(2)模糊查询contains
-
默认是区分大小写的
- 甚至整型都可以匹配
-
__icontains
忽略大小写
python
res = models.User.objects.filter(name__contains='l')
print(res)
# <QuerySet [<User: User object (3)>, <User: User object (4)>]>
(3)指定开头或结尾
__startwith
:指定开头元素,区分大小写__istartwith
:指定开头元素,忽略大小写__endwith
:指定结尾元素,区分大小写__iendwith
:指定结尾元素,忽略大小写
python
res = models.User.objects.filter(name__startswith='l')
print(res)
# <QuerySet [<User: User object (3)>, <User: User object (4)>]>
res = models.User.objects.filter(name__iendswith='y')
print(res)
# <QuerySet [<User: User object (3)>, <User: User object (4)>]>
(4)查询时间日期
- __year:指定年份,可是数字或字符串
- __month:指定月份,可是数字或字符串
- __day:指定日期,可是数字或字符串
python
res = models.User.objects.filter(register_time__year="2018")
print(res)
# <QuerySet [<User: User object (1)>, <User: User object (2)>]>
res = models.User.objects.filter(register_time__month=8)
print(res)
# <QuerySet []>
res = models.User.objects.filter(register_time__day=5)
print(res)
# <QuerySet [<User: User object (1)>]>
【四】多表操作
【0】准备
(1)知识准备--外键
models.ForeignKey()
- 一对多关系
- 创建语句放在在多的一方
models.ManyToManyField()
- 多对多关系
- django会自动创建第三张关系表
- 创建语句方法查询频率高的一方
models.OneRoOneField()
- 一对一关系
- 创建语句放在查询频率高的一方
- 一对一和一对多的统一的参数级联删除
on_delete=models.CASCADE
- Django1.9版本之前这个参数是默认的,及默认会级联删除
- 1.9版本之后需要手动选择,且是必选项
models.CASCADE
:当关联的对象被删除时,也删除依赖于它的对象。models.PROTECT
:阻止删除关联的对象。models.SET_NULL
:将关联字段设置为NULL
。你必须确保这个字段是允许NULL
的。models.SET_DEFAULT
:将关联字段设置为它的默认值。你必须确保这个字段有默认值。models.SET()
:将关联字段设置为由传递给SET()
的函数的返回值。models.DO_NOTHING
:什么都不做。
(2)创建表
- 创建出版社表
python
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
- 创建作者详情表
python
class AuthorDetail(models.Model):
age = models.IntegerField()
phone = models.BigIntegerField()
addr = models.CharField(max_length=255)
- 创建作者表
- 作者表和作者详情表一对一关系
- 作者表访问频率高,创建语句放这
python
class Author(models.Model):
name = models.CharField(max_length=32)
author_detail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE)
- 创建书籍表
- 书籍和出版社是一对多的关系,一个出版社可以发售多个书籍
- 书籍和作者是多对多的关系
python
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8, decimal_places=2)
publish_date = models.DateField()
publish = models.ForeignKey(to="Publish", on_delete=models.CASCADE)
authors = models.ManyToManyField(to="Author")
- 迁移数据
python
python manage.py makemigrations
python manage.py migrate
(3)添加数据
- 使用终端或者console插入数据
sql
INSERT INTO app02_publish (id, name, addr) VALUES
(1, 'Publish1', 'Address1'),
(2, 'Publish2', 'Address2'),
(3, 'Publish3', 'Address3');
INSERT INTO app02_authordetail (id, age, phone, addr) VALUES
(1, 30, 1234000000, 'Address1'),
(2, 50, 2300008901, 'Address2'),
(3, 30, 3450000012, 'Address3'),
(4, 40, 4500000123, 'Address4');
INSERT INTO app02_author (id, name, author_detail_id)
VALUES (1, 'Author1', 4),
(2, 'Author2', 2),
(3, 'Author3', 3),
(4, 'Author4', 1);
INSERT INTO app02_book (id, title, price, publish_date, publish_id)
VALUES (1, 'Book1', 19.99, '2020-08-01', 2),
(2, 'Book2', 29.99, '2015-02-01', 3),
(3, 'Book3', 29.99, '2015-08-01', 1),
(4, 'Book4', 39.99, '2024-04-01', 2),
(5, 'Book5', 19.99, '2020-08-01', 3),
(6, 'Book6', 29.99, '2015-06-01', 1),
(7, 'Book7', 19.99, '2020-07-01', 3),
(8, 'Book8', 39.99, '2024-04-01', 1);
INSERT INTO app02_book_authors (book_id, author_id)
VALUES (1, 4),
(1, 3),
(2, 1),
(3, 4),
(3, 2),
(4, 4),
(5, 3),
(6, 2),
(7, 1),
(7, 2),
(8, 1);
【1】一对一和一对多的增删改
(1)增加create
- 方式一:
- create方法创建的时候直接写入外键的值
- 但是此时外键需要是django创建的外键名字(_id)
- 方式二:
- 先取得关联字段的对象
- create方法创建时在传入object对象
- 返回的
object
对象
python
res = models.Book.objects.create(title="Book9", price="19.99", publish_date="2012-05-06", publish_id=1)
print(res)
# Book object (9)
author_detail_obj = models.AuthorDetail.objects.create(age=40, phone=2300001254, addr="Address5")
res = models.Author.objects.create(name="Author5", author_detail=author_detail_obj)
print(res)
# Author object (5)
(2)更改update
- 先获取要更改新的外键字段值的对象
- 然后找到要修改的对象
queryset
使用update()更新 - 返回整型,影响到的行数
python
publish_obj = models.Publish.objects.filter(pk=2).first()
res = models.Book.objects.filter(pk=9).update(publish=publish_obj)
print(res)
# 1
(3)删除delete
-
找到要删除的含有外键字段的
queryset
后执行delete
方法 -
返回元组(删除的个数,删除的对象)
python
res = models.Book.objects.filter(pk=9).delete()
print(res)
# (1, {'app02.Book': 1})
res = models.Author.objects.filter(pk=5).delete()
print(res)
# (1, {'app02.Author': 1})
-
注意:
- 如果你现在去数据库查看作者详情表
- 你会发现我们关联上的作者详情表中关联的数据并没有删除
- 可是我们前面设置了级联删除 ,为什么不起作用呢
- 那是因为级联删除应该要删除被关联的数据,主数据才会因为通过外键查找不对对应内容才会发生级联删除
-
重新添加作者和作者信息
- 重新进行删除
- 不过这次删除作者详情表的信息
- 可以发现作者表关联的数据发生级联删除
python
author_detail_obj = models.AuthorDetail.objects.create(age=40, phone=2300001254, addr="Address6")
res = models.Author.objects.create(name="Author6", author_detail=author_detail_obj)
print(res)
# Author object (6)
res = models.AuthorDetail.objects.filter(pk=6).delete()
print(res)
# (2, {'app02.Author': 1, 'app02.AuthorDetail': 1})
【2】多对多的增删改
- 实际操作的是中间表
(1)增加add
- 可以添加多个也可以添加多个,支持直接写外键值
- 先找到要添加的新关系对象object
- 然后找到要更改的对象object
- 最后
object.外键.add(object1, object2...)
python
# 目前书籍2的作者只有作者1
# 添加作者2和作者3到书籍2上
author_object3 = models.Author.objects.filter(pk=3).first()
author_object2 = models.Author.objects.filter(pk=2).first()
book_object2 = models.Book.objects.filter(pk=2).first()
book_object2.authors.add(author_object3, author_object2)
(2)删除remove
- 同样支持删除多个,支持直接写外键值
- 直接找到要删除外键关系的对象object
- 然后
object.外键.remove(pk1, pk2...)
python
# 将书籍2的作者1删除
book_object2 = models.Book.objects.filter(pk=2).first()
book_object2.authors.remove(1)
(3)清空claer\set
- 将外键关系清空
- 找到要清空外键关系的对象object
- 然后
object.外键.set([])
- 或者
object.外键.clear()
python
# 将书籍2的作者所有作者删除
book_object2 = models.Book.objects.filter(pk=2).first()
book_object2.authors.clear()
(4)更改set
- 同样支持更改多个,支持直接写外键值
- 直接找到要更改外键关系的对象object
- 然后
object.外键.set([pk1, pk2...])
- 列表的内容就是新的关键关联关系
python
# 将书籍2的作者改为所有作者四个人
book_object2 = models.Book.objects.filter(pk=2).first()
book_object2.authors.set([1, 2, 3, 4])
【3】正反向的概念
(1)正向
- 从外键字段创建 的对象查找关联的对象信息
(2)反向
- 从外键字段关联的对象查找外键字段创建的对象,和正向相反
(3)查询方法提前总结
-
子查询(基于对象)
- 正向:.外键字段
- 反向:.外键字段_set(一对一没有反向查询)
- all():当返回的是多个对象时用,将得到
queryset
-
联表查询(基于双下划线)
- 无论是filter还是values等操作
- 都是查询另一张表时,通过双下划线连接外键字段或表名或其他字段
- 只要有外键关系,那么就可以一直查询
- 正向:外键字段__
- 反向:表名__
【4】查询案例分析
(1)子查询(基于对象)
- 查询书籍主键是1的出版社名字
python
book_obj = models.Book.objects.filter(pk=1).first()
res = book_obj.publish
print(res.name)
# Publish2
- 查询书籍名字是Book2的所有作者名字
python
book_obj = models.Book.objects.filter(title="Book2").first()
res = book_obj.authors.all()
for i in res:
print(i.name)
# Author1
# Author2
# Author3
# Author4
- 查询作者名字是Author3的电话号码
python
author_obj = models.Author.objects.filter(name='Author3').first()
res = author_obj.author_detail
print(res.phone)
# 3450000012
- 查询出版社是Publish2的所有书籍名字
python
publish_obj = models.Publish.objects.filter(name='Publish2').first()
res = publish_obj.book_set.all()
for i in res:
print(i.title)
# Book1
# Book4
- 查询Author4写过的所有书籍名称
python
author_obj = models.Author.objects.filter(name='Author4').first()
res = author_obj.book_set.all()
for i in res:
print(i.title)
# Book1
# Book3
# Book4
# Book2
- 查询年龄是30岁的第一个作者名字
python
detail_obj = models.AuthorDetail.objects.filter(age=30).first()
res = detail_obj.author
print(res.name)
# Author4
(2)联表查询(基于双下划线)
- 正向:查询Author3作者的电话号码和年龄
python
res = models.Author.objects.filter(name="Author3").values("author_detail__phone", 'author_detail__age')
print(res)
# <QuerySet [{'author_detail__phone': 3450000012, 'author_detail__age': 30}]>
- 反向:查询Author3作者的电话号码和年龄
python
res = models.AuthorDetail.objects.filter(author__name='Author3').values('phone', 'age')
print(res)
# <QuerySet [{'phone': 3450000012, 'age': 30}]>
- 正向:查询书籍id为3的出版社名字和书籍的名字
python
res = models.Book.objects.filter(pk=3).values("publish__name", 'title')
print(res)
# <QuerySet [{'publish__name': 'Publish1', 'title': 'Book3'}]>
- 反向::查询书籍id为3的出版社名字和书籍的名字
python
res = models.Publish.objects.filter(book__pk=3).values('name', 'book__title')
print(res)
# <QuerySet [{'name': 'Publish1', 'book__title': 'Book3'}]>
- 正向:查询书籍id为2的作者姓名
python
res = models.Book.objects.filter(pk=3).values('authors__name')
print(res)
# <QuerySet [{'authors__name': 'Author2'}, {'authors__name': 'Author4'}]>
- 反向:查询书籍id为2的作者姓名
pyhon
res = models.Author.objects.filter(book__pk=3).values('name')
print(res)
# <QuerySet [{'name': 'Author2'}, {'name': 'Author4'}]>
- 正向:查询书籍id为3的作者手机号
python
res = models.Book.objects.filter(pk=3).values('authors__name', 'authors__author_detail__phone')
print(res)
# <QuerySet [{'authors__name': 'Author2', 'authors__author_detail__phone': 2300008901}, {'authors__name': 'Author4', 'authors__author_detail__phone': 1234000000}]>
- 反向:查询书籍id为3的作者手机号
python
res = models.AuthorDetail.objects.filter(author__book__pk=3).values('author__name', 'phone')
print(res)
【五】聚合、分组、F&Q、事务
【1】聚合查询
(1)导入聚合函数
- 要想使用聚合函数首先需要导入
python
from django.db.models import Max, Min, Sum, Count, Avg
- 正常来说我们需要先分组,才可以进行聚合函数的计算,但是
django
为我们提供了一个可以不用分组就能使用聚合函数的方法,aggregate
aggregate
是一个用于执行聚合函数的方法- 它接受一个或多个聚合表达式作为参数,并返回一个字典。
- 字典的键是聚合表达式的别名(如果提供)
- 字典的值是聚合函数计算的结果
(2)示例
- 计算所有书籍的平均价格
python
from django.db.models import Max, Min, Sum, Count, Avg
res = models.Book.objects.aggregate(Avg('price'))
print(res)
# {'price__avg': Decimal('36.657778')}
- 计算所有书籍价格的最大值,最小值,总和,总数量
python
from django.db.models import Max, Min, Sum, Count, Avg
res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), totalC_count=Count('price'))
print(res)
# {'totalC_count': 9,
# 'price__max': Decimal('100.00'),
# 'price__min': Decimal('19.99'),
# 'price__sum': Decimal('329.92')}
【2】分组查询
(1)介绍
-
分组查询往往需要配合聚合函数
-
annotate()是用来直接分组查询的方法
-
只要是
queryset
对象,都可以使用这个方法- 所以可以models.对象.objects.annotate()
- 还可以筛选后
models.对象.filter().annotate()
- 还可以
models.对象.values().annotate()
-
根据谁分组
models.对象.object.annotate()
:按照对象分组models.对象.object.values(filed).annotate()
:按照字段field
分组
-
mysql中的严格分组
- 严格分组将导致除了分组的字段,其他字段都将无法直接获取
- 所以出现错误可能是需要修改严格模式
- ONLY_FULL_GROUP_BY
(2)示例
- 统计每一本数的作者个数
- 两种方式等价
- 输入外键会自动匹配外键对应的id
python
res = models.Book.objects.annotate(author_num=Count("authors__pk")).values('title', 'author_num')
print(res)
res = models.Book.objects.annotate(author_num=Count('authors')).values('title', 'author_num')
print(res)
# <QuerySet [{'title': 'Book1', 'author_num': 2},
# {'title': 'Book2', 'author_num': 4},
# {'title': 'Book3', 'author_num': 2},
# {'title': 'Book4', 'author_num': 1},
# {'title': 'Book5', 'author_num': 1},
# {'title': 'Book6', 'author_num': 1},
# {'title': 'Book7', 'author_num': 2},
# {'title': 'Book8', 'author_num': 1}]>
- 统计每个出版社最便宜书的价格
python
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
print(res)
# <QuerySet [{'name': 'Publish2', 'min_price': Decimal('19.99')},
# {'name': 'Publish3', 'min_price': Decimal('19.99')},
# {'name': 'Publish1', 'min_price': Decimal('29.99')}]>
- 统计不止两个作者的图书名称
python
res = models.Book.objects.annotate(author_num=Count('authors__id')).filter(author_num__gt=2).values('title', 'author_num')
print(res)
# <QuerySet [{'title': 'Book2', 'author_num': 4}]>
- 统计每个作者出书的总价格
python
res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name', 'sum_price')
print(res)
# <QuerySet [{'name': 'Author1', 'sum_price': Decimal('89.97')},
# {'name': 'Author2', 'sum_price': Decimal('109.96')},
# {'name': 'Author3', 'sum_price': Decimal('69.97')},
# {'name': 'Author4', 'sum_price': Decimal('119.96')}]>
【3】F&Q查询
- 导入函数
python
from django.db.models import F, Q
(1)F查询
- F()对象可以在查询中引用模型的字段,并对这些字段进行数据库级别的操作
- 这意味着可以在查询中使用数据库的字段作为值 ,而不是Python代码中的常量
- 示例
python
# 查出高于平局价格的书籍名字和对应的价格
# 不支持这中写法,需要提前算出价格
from django.db.models import Max, Min, Sum, Count, Avg
from django.db.models import F, Q
res = models.Book.objects.filter(price__gt=F(Avg('price')))
print(res)
python
# 将所有书籍的价格提高10块钱
res = models.Book.objects.update(price=F('price') + 10)
print(res)
# 8 8条记录被更改
python
# 将所有书籍的名字后面加上selling
# 报错,F查询不能直接做到字符串的拼接
# res = models.Book.objects.update(title=F('title')+'selling')
from django.db.models.functions import Concat
from django.db.models import Value
res = models.Book.objects.update(title=Concat(F('title'), Value('selling')))
print(res)
# 8 8条数据被更改
(2)Q查询
-
默认情况下,filter中的多个参数是通过and连接的
-
但是Q()对象可以用来构建复杂的查询条件,允许使用OR(|)和NOT(~)操作符,并不是not和or,意思不一样
-
查询是8月份的书籍或者价格大于30的书籍
- 使用,隔开还是并且的关系
- 使用or或|隔开
python
res = models.Book.objects.filter(publish_date__month=8, price__gt=30)
print(res)
# <QuerySet [<Book: Book object (3)>]>
res = models.Book.objects.filter(Q(publish_date__month=8), Q(price__gt=30))
print(res)
# <QuerySet [<Book: Book object (3)>]>
python
res = models.Book.objects.filter((Q(publish_date__month=8) | Q(price__gt=30)))
print(res)
# <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>,
# <Book: Book object (3)>, <Book: Book object (4)>,
# <Book: Book object (5)>, <Book: Book object (6)>,
# <Book: Book object (8)>]>
res = models.Book.objects.filter((Q(publish_date__month=8) or Q(price__gt=30)))
print(res)
# <QuerySet [<Book: Book object (1)>, <Book: Book object (3)>, <Book: Book object (5)>]>
- Python 的
or
操作符在这里不会按照期望的方式工作。- 它会返回第一个为真的表达式,如果第一个表达式为假,那么就返回第二个表达式。在这种情况下,如果
Q(publish_date__month=8)
返回任何结果(即使是空的QuerySet
),Q(price__gt=30)
也不会被执行。
- 它会返回第一个为真的表达式,如果第一个表达式为假,那么就返回第二个表达式。在这种情况下,如果
(3)Q的高阶用法
- 可以将条件的左侧 也变成字符串的格式
python
from django.db.models import Q
q = Q()
q.connector = 'or'
q.children.append(('publish_date__month', 8))
q.children.append(('price__gt', 30))
res = models.Book.objects.filter(q)
print(res)
# <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (3)>, <Book: Book object (4)>, <Book: Book object (5)>, <Book: Book object (6)>, <Book: Book object (8)>]>
- 另一种查询方式
python
# 或
q = Q()
q |= Q(publish_date__month=8)
q |= Q(price__gt=30)
res = models.Book.objects.filter(q)
# 与
q = Q()
q &= Q(publish_date__month=8)
q &= Q(price__gt=30)
res = models.Book.objects.filter(q)
【4】开始事务
(1)事务的四大特性ACID
-
原子性A(Atomicity)
- 事务不可分割
- 要么全部成功,要么失败全部回滚,不存在执行了部分语句的情况
-
一致性C(consistency)
- 事务执行前后,数据库保持一致
- 事务执行前后,数据库的完整性性约束必须满足(列级别的约束,外键等)
-
隔离性I(isolation)
- 事务之间相互隔离,互不影响
- 多个事务可以并发执行
-
持久行D(durability)
- 事务一旦提交,那么就应该永久保存在数据中,即使系统故障,数据也能恢复
- 实现方法:写入磁盘、记录日志、定期创建检查点、复制和冗余、分布式系统
(2)django开启事务
python
from django.db import transaction
try:
# 开启事务
with transaction.atomic():
# 创建保存点
save_id = transaction.savepoint()
# orm操作语句
except Exception as e:
# 异常捕获-回滚
transaction.savepoint_rollback(save_id)
【六】ORM字段和参数
【1】常用字段
verbox_name
参数是填写注释
(1)AutoField
- 整型、自增(必填参数primary_key=True)
- 当没有人为创建这个字段时,django会自动创建命名为id
(2)IntegerField和BigIntegerField
- 都是有符号整数类型
- IntegerField范围:32位,-2^31 到 2^31-1,存手机号不够
- BigIntegerField范围:64位,-2^63到2^63-1
(3)CharFiled
- 字符类型,必须指定max_length参数
(4)EmailField
- 实际存储格式是varchar(254),互联网标准中最大长度
- 虽然是字符串,但是这种格式django会有自己的检验功能
- 必须指定默认值,或者可以为空
(5)DeciamlFiled
-
小数类型, 参数必填
-
max_digits:全部数字最大个数
-
decimal_places:小数部分最大个数
(6)TextField
- 文本类型,mysql中的longtext
- 没有字数限制,但是还是需要指定默认值或者是否可以为空
(7)FileField
- 字符串类型,varchar(100)
- 保存的是路径,文件上传到指定目录
- upload_to ='/data' 指定上传路径
- 需要指定是否可以为空
(8)BooleanField
- 布尔型,tinyint(1)
(9)DateField和DateTimeField
-
参数:auto_now
- 每次保存对象时,这个字段都会被自动设置为当前日期和时间
- 但是update更新不会起作用
- 需要对象修改后save保存才起作用
-
参数:auto_now_add
-
在创建数据的时候自动添加当前时间
-
这个时间是UTC时间,修改settings也没有用,数据库保存的就是UTC时间
-
想要数据库输出我们的时间,需要修改settings,还要使用timezone模块
pythonfrom django.utils import timezone res = models.User.objects.filter().first() print(timezone.localtime(res.time2))
-
-
DatetimeField和DateField的异同
- 一个是精确到天,一个精确到微妙
- 都可以使用双下滑线查询年
- 但是Datetime不能双下滑查询月和日,Date可以
- Datetime需要考虑时区的因素,所以不提供这个方法
(10)ForeignKey
- 外键字段,一对多
- to:要关联的表
- to_field:设置要关联表的字段
on_delete=models.CASCADE
:级联删除
(11)OneToOneField
- 外键字段,一对一
- to:要关联的表
- to_field:要关联的字段
on_delete=models.CASCADE
:级联删除
(12)ManyToManyField
- 外键字段,多对多
- to:要关联的表
【2】常用参数
(1)为空null
- 用于表示某个字段是否可以为空
(2)唯一Unique
- 用于表示字段的值的值是否唯一
python
Foreign(unique=True) --> OneToOneField()
(3)默认值default
- 可以为字段设置默认值
(4)db_index
- 如果为True,则为此字段设置索引
【3】mysql和django的字段对应关系
python
'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)',
【4】自定义字段
- 自定义char类型字段
python
class MysqlChar(models.CharField):
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):
"""
模仿mysql限制生成的长度
:param connection:
:return:
"""
return 'char(%s)' % self.max_length
class User(models.Model):
name = MysqlChar(max_length=5, default='bruce')
【5】多对多的创建方式
(1)全自动(小项目)
- 利用ORM自动帮我们创建第三张表
- 优点
- 第三张表代码不需要我们写,方便快捷,支持ORM操作第三张表
- 不足
- 第三张表扩展性极差,无法添加额外的字段
python
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(to="Author", on_delete=models.CASCADE)
class Author(models.Model):
name = models.CharField(max_length=100)
(2)纯手动(不推荐)
- 第三张表完全由我们自己创建
- 优点
- 第三张表扩展性极高
- 缺点
- 需要自己写代码,不能使用ORM方法
python
class Book(models.Model):
title = models.CharField(max_length=100)
class Author(models.Model):
name = models.CharField(max_length=100)
class AuthorBook(models.Model):
book_id = models.ForeignKey(to='Book', on_delete=models.CASCADE)
author_id = models.ForeignKey(to="Author", on_delete=models.CASCADE)
(3)半自动(大项目)
- 优点
- 第三张表扩展性很高,可以使用ORM正反向查询
- 不足
- 需要自己写代码,没法使用add\set\remove\clear方法
python
class Book2(models.Model):
title = models.CharField(max_length=100)
# through_fields字段
# 第一个参数是当前表所在第三张表的字段
# 第二个参数是另一个表在第三张表的字段
authors = models.ManyToManyField(to='Author2', through='Author2Book2', through_fields=('book_id', 'author_id'))
class Author2(models.Model):
name = models.CharField(max_length=100)
class Author2Book2(models.Model):
book_id = models.ForeignKey(to='Book2', on_delete=models.CASCADE)
author_id = models.ForeignKey(to="Author2", on_delete=models.CASCADE)
【6】choice参数
(1)介绍
- Django的模型字段中,choice是一个可选参数,它允许限制用户输入的范围
- choice参数接收一个可迭代的元组或者列表,每个元组或列表都包含两个元素
- 第一个元素:实际保存到数据库中的值
- 第二个元素:在django后台或者表单中显示的友好名称
- 所以只要是可以完全列举出来的数据,都可以使用这个参数
(2)创建表
python
from django.db import models
class Student(models.Model):
name = models.CharField(max_length=32)
year_in_school_choices = [
[1, 'Freshman'],
[2, 'Sophomore'],
[3, 'Junior'],
[4, 'Senior'],
]
year_in_school = models.IntegerField(choices=year_in_school_choices)
gender_choices = (
('F', "Female"),
("M", "Male"),
("O", "Other")
)
gender = models.CharField(choices=gender_choices, max_length=1)
(3)插入数据
- 通过示例可以发现
- 即使插入的我们定义以为的字段值
- 也不会报错
python
from django.test import TestCase
# Create your tests here.
import os
if __name__ == '__main__':
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_test.settings')
import django
django.setup()
# 测试代码编写
from app02 import models
res = models.Student.objects.create(name="bruce", gender='M', year_in_school=4)
print(res)
# Student object (1)
res = models.Student.objects.create(name="lucy", gender="F", year_in_school=0)
print(res)
# Student object (2)
res = models.Student.objects.create(name="tom", gender="m", year_in_school=2)
print(res)
# Student object (3)
(4)查数据
-
之前的方法只能查到,第一个参数及存储在数据库中参数
-
get_字段名_display()
:可以查看到第二个参数 -
查询正常存储的数据
- 结果:正常
python
res = models.Student.objects.filter(name='bruce').first()
print(res.gender)
# M
print(res.get_gender_display())
# # Male
- 查询非正常存储的数据
- 结果:两种查询方式都显示第一个参数,存在数据库中的值
python
res = models.Student.objects.filter(name='lucy').first()
print(res.year_in_school)
# 0
print(res.get_year_in_school_display())
# 0
python
res = models.Student.objects.filter(name='tom').first()
print(res.gender)
# m
print(res.get_gender_display())
# m
【七】数据库查询优化
【1】查看django
对应的mysql
语句
(1).query属性
- 只要是
queryset
对象,那么就有query属性
python
from app02 import models
res = models.Author.objects.all()
print(res.query)
# SELECT `app02_author`.`id`, `app02_author`.`name`, `app02_author`.`author_detail_id` FROM `app02_author`
(2)settings配置
- 添加配置,实现功能
- 只要执行
sql
语句,那么就输出
- 只要执行
python
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
【2】ORM语句特点
(1)链式调用
- 只要是queyset对象,那么就可以一直链式操作
- 代码更加清晰,易于阅读和维护
(2)数据库无关性
- ORM提供了一种和数据库无关的接口
- 可以轻松在多个数据库之间进行切换,无需修改代码
(3)安全性
- ORM会自动处理一些安全问题
- 如:SQL注入攻击
(4)惰性加载
- 查询集(queryset)的创建是惰性的
- 创建时不会立即执行数据库查询,需要查询的结果时才会执行数据库查询
- 优化性能,避免不必要查询
python
# 配置settings,自动输入sql语句
res = models.Book.objects.all()
# 没有执行sql
python
# 配置settings,自动输入sql语句
res = models.Book.objects.all()
print(res.first().title)
# Book1
# (0.000)
# SELECT VERSION(),
# @@sql_mode,
# @@default_storage_engine,
# @@sql_auto_is_null,
# @@lower_case_table_names,
# CONVERT_TZ('2001-01-01 01:00:00', 'UTC', 'UTC') IS NOT NULL
# ; args=None
# (0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
# (0.015) SELECT `app02_book`.`id`, `app02_book`.`title`, `app02_book`.`price`, `app02_book`.`publish_date`, `app02_book`.`publish_id` FROM `app02_book` ORDER BY `app02_book`.`id` ASC LIMIT 1; args=()
【3】only、defer
- 都是优化数据库查询的方法
- 用于控制哪些字段在初始数据库查询中加载
(1)介绍
-
only
- 用于指定初始查询中加载的字段
-
defer
- 用于指定初始查询中不加载的字段
-
同
- 减少数据库查询的数据量,提高查询效率
- 都不会阻止访问未加载的字段
- 访问未加载的字段时,django会自动执行一个新的数据库查询来加载那个字段,称为"后加载"或"多项加载"
(2)示例
-
查询所有书籍的名字和价格
-
默认方法、only、defer
- 默认方法sql,将所有字段都加载出来了
- only方法的sql,只加载了only指定的字段
- defer方法的sql,只加载了defer指定的其他字段信息
-
only、defer
- 查询没有加载的字段,会自动执行新的sql语句
python
# 默认方法
res = models.Author.objects.all()
for i in res:
print(i.name)
# (0.000) SELECT `app02_book`.`id`, `app02_book`.`title`, `app02_book`.`price`, `app02_book`.`publish_date`, `app02_book`.`publish_id` FROM `app02_book`; args=()
python
# only方法
res = models.Author.objects.only('name', 'age')
for i in res:
print(i.name)
# (0.000) SELECT `app02_book`.`id`, `app02_book`.`title`, `app02_book`.`price` FROM `app02_book`; args=()
python # defer res = models.Book.objects.defer("title", 'price') for i in res: print(i.title, i.price) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`publish_date`, `app02_book`.`publish_id` FROM `app02_book`; args=() # (0.000) SELECT `app02_book`.`id`, `app02_book`.`title` FROM `app02_book` WHERE `app02_book`.`id` = 1 LIMIT 21; args=(1,) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`price` FROM `app02_book` WHERE `app02_book`.`id` = 1 LIMIT 21; args=(1,) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`title` FROM `app02_book` WHERE `app02_book`.`id` = 2 LIMIT 21; args=(2,) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`price` FROM `app02_book` WHERE `app02_book`.`id` = 2 LIMIT 21; args=(2,) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`title` FROM `app02_book` WHERE `app02_book`.`id` = 3 LIMIT 21; args=(3,) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`price` FROM `app02_book` WHERE `app02_book`.`id` = 3 LIMIT 21; args=(3,) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`title` FROM `app02_book` WHERE `app02_book`.`id` = 4 LIMIT 21; args=(4,) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`price` FROM `app02_book` WHERE `app02_book`.`id` = 4 LIMIT 21; args=(4,) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`title` FROM `app02_book` WHERE `app02_book`.`id` = 5 LIMIT 21; args=(5,) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`price` FROM `app02_book` WHERE `app02_book`.`id` = 5 LIMIT 21; args=(5,) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`title` FROM `app02_book` WHERE `app02_book`.`id` = 6 LIMIT 21; args=(6,) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`price` FROM `app02_book` WHERE `app02_book`.`id` = 6 LIMIT 21; args=(6,) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`title` FROM `app02_book` WHERE `app02_book`.`id` = 7 LIMIT 21; args=(7,) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`price` FROM `app02_book` WHERE `app02_book`.`id` = 7 LIMIT 21; args=(7,) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`title` FROM `app02_book` WHERE `app02_book`.`id` = 8 LIMIT 21; args=(8,) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`price` FROM `app02_book` WHERE `app02_book`.`id` = 8 LIMIT 21; args=(8,)
【4】select_related、prefetch_related
(1)介绍
-
同:
- 用于优化查询数据的方法,减少跨表查询 产生的数据库查询次数
- 显著的提升查询效率 ,特别是处理大量数据和复杂的关系时
- 但是会增加单个查询的复杂性,所以在没有必要的情况下避免使用他们
-
select_related
- 对一对一关系特别有用
-
prefetch_related
- 对一对多关系和多对多关系特别有用
(2)示例
- 通过作者表查询作者详情表的年龄
- 默认方法和select_related方法
- 默认方法sql:在每次查询的时候都会执行新的sql语句
- select_related方法sql:只有在第一次查询时有执行sql语句
- 通过inner join连接的两个表
python # 默认方法 res = models.Author.objects.all() for i in res: print(i.author_detail.age) # (0.000) SELECT `app02_author`.`id`, `app02_author`.`name`, `app02_author`.`author_detail_id` FROM `app02_author`; args=() # (0.000) SELECT `app02_authordetail`.`id`, `app02_authordetail`.`age`, `app02_authordetail`.`phone`, `app02_authordetail`.`addr` FROM `app02_authordetail` WHERE `app02_authordetail`.`id` = 4 LIMIT 21; args=(4,) # (0.000) SELECT `app02_authordetail`.`id`, `app02_authordetail`.`age`, `app02_authordetail`.`phone`, `app02_authordetail`.`addr` FROM `app02_authordetail` WHERE `app02_authordetail`.`id` = 2 LIMIT 21; args=(2,) # (0.000) SELECT `app02_authordetail`.`id`, `app02_authordetail`.`age`, `app02_authordetail`.`phone`, `app02_authordetail`.`addr` FROM `app02_authordetail` WHERE `app02_authordetail`.`id` = 3 LIMIT 21; args=(3,) # (0.000) SELECT `app02_authordetail`.`id`, `app02_authordetail`.`age`, `app02_authordetail`.`phone`, `app02_authordetail`.`addr` FROM `app02_authordetail` WHERE `app02_authordetail`.`id` = 1 LIMIT 21; args=(1,) """ """ # select_related方法 res = models.Author.objects.select_related('author_detail') for i in res: print(i.author_detail.age) # (0.000) SELECT `app02_author`.`id`, `app02_author`.`name`, `app02_author`.`author_detail_id`, `app02_authordetail`.`id`, `app02_authordetail`.`age`, `app02_authordetail`.`phone`, `app02_authordetail`.`addr` FROM `app02_author` INNER JOIN `app02_authordetail` ON (`app02_author`.`author_detail_id` = `app02_authordetail`.`id`); args=()
- 通过书籍表查询出版社表的名字
- 默认方法和prefetch_related方法
- 默认方法sql:在每次查询的时候都会执行新的sql语句
- prefetch_related方法sql:只有在第一次查询时有执行sql语句
- 通过子查询连接的两个表
python # 默认方法 res = models.Book.objects.all() # for i in res: # print(i.publish.name) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`title`, `app02_book`.`price`, `app02_book`.`publish_date`, `app02_book`.`publish_id` FROM `app02_book`; args=() # (0.000) SELECT `app02_publish`.`id`, `app02_publish`.`name`, `app02_publish`.`addr` FROM `app02_publish` WHERE `app02_publish`.`id` = 2 LIMIT 21; args=(2,) # (0.000) SELECT `app02_publish`.`id`, `app02_publish`.`name`, `app02_publish`.`addr` FROM `app02_publish` WHERE `app02_publish`.`id` = 3 LIMIT 21; args=(3,) # (0.000) SELECT `app02_publish`.`id`, `app02_publish`.`name`, `app02_publish`.`addr` FROM `app02_publish` WHERE `app02_publish`.`id` = 1 LIMIT 21; args=(1,) # (0.000) SELECT `app02_publish`.`id`, `app02_publish`.`name`, `app02_publish`.`addr` FROM `app02_publish` WHERE `app02_publish`.`id` = 2 LIMIT 21; args=(2,) # (0.000) SELECT `app02_publish`.`id`, `app02_publish`.`name`, `app02_publish`.`addr` FROM `app02_publish` WHERE `app02_publish`.`id` = 3 LIMIT 21; args=(3,) # (0.000) SELECT `app02_publish`.`id`, `app02_publish`.`name`, `app02_publish`.`addr` FROM `app02_publish` WHERE `app02_publish`.`id` = 1 LIMIT 21; args=(1,) # (0.000) SELECT `app02_publish`.`id`, `app02_publish`.`name`, `app02_publish`.`addr` FROM `app02_publish` WHERE `app02_publish`.`id` = 3 LIMIT 21; args=(3,) # (0.000) SELECT `app02_publish`.`id`, `app02_publish`.`name`, `app02_publish`.`addr` FROM `app02_publish` WHERE `app02_publish`.`id` = 1 LIMIT 21; args=(1,) """ """ # prefetch_related方法 res = models.Book.objects.prefetch_related('publish') for i in res: print(i.publish.name) # (0.000) SELECT `app02_book`.`id`, `app02_book`.`title`, `app02_book`.`price`, `app02_book`.`publish_date`, `app02_book`.`publish_id` FROM `app02_book`; args=() # (0.000) SELECT `app02_publish`.`id`, `app02_publish`.`name`, `app02_publish`.`addr` FROM `app02_publish` WHERE `app02_publish`.`id` IN (1, 2, 3); args=(1, 2, 3)
【八】MTV、MVC模型
- 都是软件设计中常用的设计模式,用于组织代码结果,使其更易于理解和维护
- 尽管名字和组件有所不同,但是基本思想是一样 的:
- 将数据处理 (model),用户界面 (View或Template)和控制流(Controller或View)分离开来
【1】MVC模型
- Model(模型)
- 负责处理应用程序的数据逻辑
- 通常与数据库交互
- View(视图)
- 负责显示用户界面,通常是基于模型数据
- Controller(控制器)
- 处理用户的输入,根据用户的输入,更新模型,然后更新视图
【2】MTV模型
-
这是Django的设计模型
-
Model(模型)
- 与MVC的Model一样,负责处理应用程序的数据逻辑
- 通常与数据库交互
-
Template(模板)
- 类似于MVC的View,负责显示用户界面
- 在Django中模板层通常是HTML文件,可以使用模板语言插入模型数据
- 将动态数据和静态数据结合起来,生成最终的用户界面
-
View(视图)
- 类似于MVC的Controller,处理用户的请求,更新模型,然后渲染模板