Django模型层

【一】Django框架之生命周期流程图

在这里插入图片描述

【二】基础准备

【1】测试脚本

(1)介绍

  • 在django项目中,test.py文件是用于存放测试代码的地方
  • 在这个文件中,可以编写对模型、视图、表单、模板等测试代码
  • 这个文件通常位于每一个应用目录中(没有的话可以创建)
  • 你可能会好奇,为啥我们自己创建一个文件直接导入模型层的models不就可以操作了吗
    • 因为django项目内的东西是不提供向外的
    • 所以我们需要一些特殊准备来搭建

(2)搭建测试环境

  • 首先去mange.py文件中复制

    • import os
      if __name__ == '__main__':
          os.environ.setdefault('DJANGO_SETTINGS_MODULE', '项目名.settings')
      
  • 然后写下这两句话

    • # 导入django
      import django
      # 代码启动django
      django.setup()
      
    • 注意:必须要写在if __name__ == '__main__':

  • 最终样子示例

    • 前两行tets.py文件自带
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

    • create database if not exists django_test;
      
  • 修改配置文件settings.py

DATABASES = {
    "default": {
        "ENGINE": 'django.db.backends.mysql',
        # 数据库名字
        "NAME": "django_test",
        "USER": 'root',
        "PASSWORD": "000",
        "HOST": "localhost",
        "PORT": 3306,
        "CHARSET": 'utf8mb4',
    }
}
  • 在项目的__init__.py文件中声明数据据库类型
import pymysql
pymysql.install_as_MySQLdb()

【三】单表操作

【0】准备

(1)知识补充

  1. 结果是queryset
    • 那么就支持链式操作,拥有惰性加载的特点
    • 可以链式执行所有queryset支持的所有操作
    • QuerySet 看起来像一个列表套字典,但它并不是真正的列表。
  2. 条件语句中
    • 可以通过pk=主键值查询
    • 代替了主键字段的关键字,很统一

(2)创建表

  • models.py文件中创建需要的表
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 manage.py makemigrations
# 迁移
python manage.py migrate

【1】数据的增加

(1)语法

  • 方式一(不推荐)
    • 使用类名创建
    • 然后执行save操作
  • 方式二(推荐
    • 使用类的objects下的create方法
    • 有返回值,是创建的对象本身object

(2)示例

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)示例

    # 查询所有
    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)示例

# 将主键是2的用户年龄改成18
res = models.User.objects.filter(pk=2).update(age=18)
print(res)
# 1

【4】数据的删除

(1)语法

  • 删除单个
    • 先使用.filter().first()方法查询object对象
    • 然后执行delete()方法
  • 批量删除
    • 先使用filter()方法查询
    • 然后delete()方法删除
    • 可以写在一行
    • 返回结果是元组(个数,删除的对象)

(2)示例

# 将年龄是18的用户删除
res = models.User.objects.filter(age=18).delete()
print(res, type(res))
# (2, {'app02.User': 2}) <class 'tuple'>

【5】其他简单查询操作

(0)数据重新添加

  • 重置表
truncate user
  • 重新添加数据
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,列表套字典,但并非这么简单
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,列表套元组,但并非这么简单

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方法限制指定字段在去重
# 无法去重
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

# 按照年龄降序
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
# 直接反转,没用
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
# 直接统计
res = models.User.objects.count()
print(res)

# 统计年龄18的个数
res = models.User.objects.filter(age=18).count()
print(res)

(7)反向查询exclude

  • filter相反
  • 结果是queryset
# 查询年龄除18岁以外的用户
res = models.User.objects.exclude(age=18)
print(res)
# <QuerySet [<User: User object (3)>, <User: User object (4)>]>

(8)是否存在exists

  • 检查queryset是否含有对象
  • 没有对象False,有对象True
res = models.User.objects.filter(age=100).exists()
print(res)
# False

【6】双下划线查询

  • 结果都是queryset

(1)比较和范围查询

  • 大于:__gt
res = models.User.objects.filter(age__gt=18)
print(res)
# <QuerySet [<User: User object (4)>]>
  • 小于:__lt
res = models.User.objects.filter(age__lt=18)
print(res)
# <QuerySet [<User: User object (3)>]>
  • 大于等于:__gte
res = models.User.objects.filter(age__gte=18)
print(res)
# <QuerySet [<User: User object (1)>, <User: User object (2)>, <User: User object (4)>]>
  • 小于等于:__lte
res = models.User.objects.filter(age__lte=18)
print(res)
# <QuerySet [<User: User object (1)>, <User: User object (2)>, <User: User object (3)>]>
  • 两个条件之间范围(闭区间):__range
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
res = models.User.objects.filter(age__in=[16, 18])
print(res)
# <QuerySet [<User: User object (1)>, <User: User object (2)>]>

(2)模糊查询contains

  • 默认是区分大小写的

    • 甚至整型都可以匹配
  • __icontains忽略大小写

res = models.User.objects.filter(name__contains='l')
print(res)
# <QuerySet [<User: User object (3)>, <User: User object (4)>]>

(3)指定开头或结尾

  • __startwith:指定开头元素,区分大小写
  • __istartwith:指定开头元素,忽略大小写
  • __endwith:指定结尾元素,区分大小写
  • __iendwith:指定结尾元素,忽略大小写
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:指定日期,可是数字或字符串
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)创建表

  • 创建出版社表
class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
  • 创建作者详情表
class AuthorDetail(models.Model):
    age = models.IntegerField()
    phone = models.BigIntegerField()
    addr = models.CharField(max_length=255)
  • 创建作者表
    • 作者表和作者详情表一对一关系
    • 作者表访问频率高,创建语句放这
class Author(models.Model):
    name = models.CharField(max_length=32)
    author_detail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE)
  • 创建书籍表
    • 书籍和出版社是一对多的关系,一个出版社可以发售多个书籍
    • 书籍和作者是多对多的关系
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 manage.py makemigrations
python manage.py migrate

(3)添加数据

  • 使用终端或者console插入数据
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对象
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()更新
  • 返回整型,影响到的行数
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方法

  • 返回元组(删除的个数,删除的对象)

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})
  • 注意:

    • 如果你现在去数据库查看作者详情表
    • 你会发现我们关联上的作者详情表中关联的数据并没有删除
    • 可是我们前面设置了级联删除,为什么不起作用
    • 那是因为级联删除应该要删除被关联的数据,主数据才会因为通过外键查找不对对应内容才会发生级联删除
  • 重新添加作者和作者信息

    • 重新进行删除
    • 不过这次删除作者详情表的信息
    • 可以发现作者表关联的数据发生级联删除
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...)
# 目前书籍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...)
# 将书籍2的作者1删除
book_object2 = models.Book.objects.filter(pk=2).first()
book_object2.authors.remove(1)

(3)清空claer\set

  • 将外键关系清空
    • 找到要清空外键关系的对象object
    • 然后object.外键.set([])
    • 或者object.外键.clear()
# 将书籍2的作者所有作者删除
book_object2 = models.Book.objects.filter(pk=2).first()
book_object2.authors.clear()

(4)更改set

  • 同样支持更改多个,支持直接写外键值
    • 直接找到要更改外键关系的对象object
    • 然后object.外键.set([pk1, pk2...])
    • 列表的内容就是新的关键关联关系
# 将书籍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的出版社名字
book_obj = models.Book.objects.filter(pk=1).first()
res = book_obj.publish
print(res.name)
# Publish2
  • 查询书籍名字是Book2的所有作者名字
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的电话号码
author_obj = models.Author.objects.filter(name='Author3').first()
res = author_obj.author_detail
print(res.phone)
# 3450000012
  • 查询出版社是Publish2的所有书籍名字
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写过的所有书籍名称
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岁的第一个作者名字
detail_obj = models.AuthorDetail.objects.filter(age=30).first()
res = detail_obj.author
print(res.name)
# Author4

(2)联表查询(基于双下划线)

  • 正向:查询Author3作者的电话号码和年龄
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作者的电话号码和年龄
res = models.AuthorDetail.objects.filter(author__name='Author3').values('phone', 'age')
print(res)
# <QuerySet [{'phone': 3450000012, 'age': 30}]>
  • 正向:查询书籍id为3的出版社名字和书籍的名字
res = models.Book.objects.filter(pk=3).values("publish__name", 'title')
print(res)
# <QuerySet [{'publish__name': 'Publish1', 'title': 'Book3'}]>
  • 反向::查询书籍id为3的出版社名字和书籍的名字
res = models.Publish.objects.filter(book__pk=3).values('name', 'book__title')
print(res)
# <QuerySet [{'name': 'Publish1', 'book__title': 'Book3'}]>
  • 正向:查询书籍id为2的作者姓名
res = models.Book.objects.filter(pk=3).values('authors__name')
print(res)
# <QuerySet [{'authors__name': 'Author2'}, {'authors__name': 'Author4'}]>
  • 反向:查询书籍id为2的作者姓名
res = models.Author.objects.filter(book__pk=3).values('name')
print(res)
# <QuerySet [{'name': 'Author2'}, {'name': 'Author4'}]>
  • 正向:查询书籍id为3的作者手机号
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的作者手机号
res = models.AuthorDetail.objects.filter(author__book__pk=3).values('author__name', 'phone')
print(res)

【五】聚合、分组、F&Q、事务

【1】聚合查询

(1)导入聚合函数

  • 要想使用聚合函数首先需要导入
from django.db.models import Max, Min, Sum, Count, Avg
  • 正常来说我们需要先分组,才可以进行聚合函数的计算,但是django为我们提供了一个可以不用分组就能使用聚合函数的方法,aggregate
  • aggregate是一个用于执行聚合函数的方法
    • 它接受一个或多个聚合表达式作为参数,并返回一个字典。
    • 字典的键是聚合表达式的别名(如果提供)
    • 字典的值是聚合函数计算的结果

(2)示例

  • 计算所有书籍的平均价格
from django.db.models import Max, Min, Sum, Count, Avg
res = models.Book.objects.aggregate(Avg('price'))
print(res)
# {'price__avg': Decimal('36.657778')}
  • 计算所有书籍价格的最大值,最小值,总和,总数量
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
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}]>
  • 统计每个出版社最便宜书的价格
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')}]>
  • 统计不止两个作者的图书名称
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}]>
  • 统计每个作者出书的总价格
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查询

  • 导入函数
from django.db.models import F, Q

(1)F查询

  • F()对象可以在查询中引用模型的字段,并对这些字段进行数据库级别的操作
  • 这意味着可以在查询中使用数据库的字段作为值,而不是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)
# 将所有书籍的价格提高10块钱
res = models.Book.objects.update(price=F('price') + 10)
print(res)
# 8 8条记录被更改
# 将所有书籍的名字后面加上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或|隔开
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)>]>
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的高阶用法

  • 可以将条件的左侧也变成字符串的格式
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)>]>
  • 另一种查询方式
# 或
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开启事务

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位,-263到263-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模块

    • from 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

  • 用于表示字段的值的值是否唯一
Foreign(unique=True) --> OneToOneField()

(3)默认值default

  • 可以为字段设置默认值

(4)db_index

  • 如果为True,则为此字段设置索引

【3】mysql和django的字段对应关系

'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类型字段
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操作第三张表
  • 不足
    • 第三张表扩展性极差,无法添加额外的字段
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方法
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方法
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)创建表

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)插入数据

  • 通过示例可以发现
    • 即使插入的我们定义以为的字段值
    • 也不会报错
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():可以查看到第二个参数

  • 查询正常存储的数据

    • 结果:正常
res = models.Student.objects.filter(name='bruce').first()
print(res.gender)
# M
print(res.get_gender_display())
# # Male
  • 查询非正常存储的数据
    • 结果:两种查询方式都显示第一个参数,存在数据库中的值
res = models.Student.objects.filter(name='lucy').first()
print(res.year_in_school)
# 0
print(res.get_year_in_school_display())
# 0
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属性
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语句,那么就输出
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)的创建是惰性
  • 创建时不会立即执行数据库查询,需要查询的结果时才会执行数据库查询
  • 优化性能,避免不必要查询
# 配置settings,自动输入sql语句
res = models.Book.objects.all()
# 没有执行sql
# 配置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语句
# 默认方法
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=()
# 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,处理用户的请求,更新模型,然后渲染模板

相关推荐

  1. Django -- 模型

    2024-03-26 10:16:04       30 阅读
  2. Django框架之模型

    2024-03-26 10:16:04       25 阅读
  3. Django框架之模板

    2024-03-26 10:16:04       23 阅读
  4. Web框架开发-Django模板

    2024-03-26 10:16:04       36 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-26 10:16:04       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-26 10:16:04       106 阅读
  3. 在Django里面运行非项目文件

    2024-03-26 10:16:04       87 阅读
  4. Python语言-面向对象

    2024-03-26 10:16:04       96 阅读

热门阅读

  1. Linux配置elasticsearch开机自启

    2024-03-26 10:16:04       38 阅读
  2. 「Linux系列」Shell 函数详解

    2024-03-26 10:16:04       46 阅读
  3. android卡顿流程分析总结

    2024-03-26 10:16:04       44 阅读
  4. gstreamer udp rtp发送本地视频文件

    2024-03-26 10:16:04       39 阅读
  5. 4A架构:企业数字化转型的核心引擎

    2024-03-26 10:16:04       40 阅读
  6. vue2项目关联el-table和el-pagination

    2024-03-26 10:16:04       44 阅读
  7. 服务器为互联网发送数据出现丢包情况

    2024-03-26 10:16:04       47 阅读
  8. 28.找出字符串中第一个匹配项的下标

    2024-03-26 10:16:04       40 阅读
  9. MySQL数据结构B树与B+树的区别

    2024-03-26 10:16:04       47 阅读