四方显神

导航

Django开发笔记(六)四件套之四:模型层ORM(上)

Django中内嵌了ORM框架,不需要直接编写SQL语句进行数据库操作,而是通过定义模型类,操作模型类来完成对数据库中表的增删改查和创建等操作。

O是object,也就类对象的意思。

R是relation,翻译成中文是关系,也就是关系数据库中数据表的意思。

M是mapping,是映射的意思。

映射:(这个很熟,做项目经常用,和mybatis里的映射一样的)

类:sql语句table表

类成员变量:table表中的字段、类型和约束

类对象:sql表的表记录

ORM的优点:

  • 数据模型类都在一个地方定义,更容易更新和维护,也利于重用代码。

  • ORM 有现成的工具,很多功能都可以自动完成,比如数据消除、预处理、事务等等。

  • 它迫使你使用 MVC 架构,ORM 就是天然的 Model,最终使代码更清晰。

  • 基于 ORM 的业务代码比较简单,代码量少,语义性好,容易理解。

  • 新手对于复杂业务容易写出性能不佳的 SQL,有了ORM不必编写复杂的SQL语句, 只需要通过操作模型对象即可同步修改数据表中的数据.

  • 开发中应用ORM将来如果要切换数据库.只需要切换ORM底层对接数据库的驱动【修改配置文件的连接地址即可】

ORM 缺点:

  • ORM 库不是轻量级工具,需要花很多精力学习和设置,甚至不同的框架,会存在不同操作的ORM。
  • 对于复杂的业务查询,ORM表达起来比原生的SQL要更加困难和复杂。
  • ORM操作数据库的性能要比使用原生的SQL差。但是吧,对于我们初学的简单的,性能问题差异不大。
  • ORM 抽象掉了数据库层,开发者无法了解底层的数据库操作,也无法定制一些特殊的 SQL。【自己使用pymysql另外操作即可,用了ORM并不表示当前项目不能使用别的数据库操作工具了。】

我们可以通过以下步骤来使用django的数据库操作

  1. 配置数据库连接信息
  2. 在models.py中定义模型类
  3. 生成数据库迁移文件并执行迁文件[注意:数据迁移是一个独立的功能,这个功能在其他web框架未必和ORM一块的]
  4. 通过模型类对象提供的方法或属性完成数据表的增删改查操作

零、准备工作(已经下载好mysql和可视化工具可以跳过这一小节)

首先mysql下载及安装,参考:MySQL数据库下载及安装教程(最最新版)-CSDN博客

老师上课没有用可视化工具,我觉得直接Navicat用上比较好,也不需要中间过渡了。

另外,pycharm自己也提供了一个类似Navicat的东西:

 然后配置一下,填完,左下角那个test Connection试一下,它告诉我需要下载一下驱动。下载驱动就在三角形提醒这里,download点一下就行。

下载需要一两分钟吧,完了就好了,apply+ok即可:

这个也就是配着玩玩,我还是建议使用Navicat一劳永逸。

一、配置数据库连接

1)首先在mysql数据库里创建一个数据库:

create DATABASE student,后续就使用这个库。

2)在settings.py中保存了数据库的连接配置信息,Django默认初始配置使用sqlite数据库,修改配置信息,改成我们现在的mysql数据库:

# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

# DATABASES = {
#     'default': {
#         'ENGINE': 'django.db.backends.sqlite3',
#         'NAME': BASE_DIR / 'db.sqlite3',
#     }
# }

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'HOST': '127.0.0.1', # 数据库主机
        'PORT': 3306, # 数据库端口
        'USER':'root',
        'PASSWORD':'123456',
        'NAME': 'student' # 数据库名字
    }
}

3)安装数据库驱动:

现在运行时会报错的,因为Django默认是数据库驱动是MySQLDB,Django2.0后都改为了PyMySQL,安装驱动:

pip install PyMySQL

4)在项目全局目录下的__init__.py文件中添加以下语句:

import pymysql
pymysql.install_as_MySQLdb() 

作用是让Django的ORM能以mysqldb的方式来调用PyMySQL。现在再启动就不会报错了。

5)注意: 如果想打印orm转换过程中的sql,需要在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',
        },
    }
}  

二、定义模型类

  • 模型类被定义在"子应用/models.py"文件中。
  • 模型类必须直接或者间接继承自django.db.models.Model类。

student/models.py文件:

from django.db import models

# Create your models here.

'''

create table student(
    name varchar(32) unique ,
    age int ,
    ...
)

'''
# 模型类必须继承models.Model
class Student(models.Model):
    # 语法上做一个限制,元组套元组,第一个值是要存入数据库中的值,第二个值是我们展示给用户看的字符串
    sex_choices = (
        (0,'女'),
        (1,'男'),
        (2,'无性别')
    )

    # AutoField自增约束
    id = models.AutoField(primary_key = True)

    # verbose_name相当于一个注释,展示起来会优先verbose_name
    name = models.CharField(max_length=32,unique=True,verbose_name="姓名")
    age = models.SmallIntegerField(verbose_name="年龄",default=18)
    sex = models.SmallIntegerField(verbose_name="性别")
    birthday = models.DateField(verbose_name="生日")

    class Meta: # 这里,注意,是写在Student类内部的!不然没有作用
        db_table  = "db_student"
        # 其他配置....

关于这个模型类的一些参数什么的:

(1) 数据库表名

模型类如果未指明表名db_table,Django默认以 小写app应用名_小写模型类名 为数据库表名。

可通过db_table 指明数据库表名,比如 db_table = "db_student"。

(2) 关于主键

django会为表创建自动增长的主键列,每个模型只能有一个主键列。

如果使用选项设置某个字段的约束属性为主键列(primary_key)后,django不会再创建自动增长的主键列。

class Student(models.Model):
    # django会自动在创建数据表的时候生成id主键/还设置了一个调用别名 pk
    id = models.AutoField(primary_key=True, null=False, verbose_name="主键") # 设置主键

默认创建的主键列属性为id,可以使用pk代替,pk全拼为primary key

(3) 属性命名限制

  • 不能是python的保留关键字。

  • 不允许使用连续的2个下划线,这是由django的查询方式决定的。__ 是关键字来的,不能使用!!!

  • 定义属性时需要指定字段类型,通过字段类型的参数指定选项,语法:属性名 = models.字段类型(约束选项, verbose_name="注释")

(4)字段类型

类型说明
AutoField 自动增长的IntegerField,通常不用指定,不指定时Django会自动创建属性名为id的自动增长属性
BooleanField 布尔字段,值为True或False
NullBooleanField 支持Null、True、False三种值
CharField 字符串,参数max_length表示最大字符个数,对应mysql中的varchar
TextField 大文本字段,一般大段文本(超过4000个字符)才使用。
IntegerField 整数
DecimalField 十进制浮点数, 参数max_digits表示总位数, 参数decimal_places表示小数位数,常用于表示分数和价格 Decimal(max_digits=7, decimal_places=2) ==> 99999.99~ 0.00
FloatField 浮点数
DateField 日期
参数auto_now表示每次保存对象时,自动设置该字段为当前时间。
参数auto_now_add表示当对象第一次被创建时自动设置当前。
参数auto_now_add和auto_now是相互排斥的,一起使用会发生错误。
TimeField 时间,参数同DateField
DateTimeField 日期时间,参数同DateField
FileField 上传文件字段,django在文件字段中内置了文件上传保存类, django可以通过模型的字段存储自动保存上传文件, 但是, 在数据库中本质上保存的仅仅是文件在项目中的存储路径!!
ImageField 继承于FileField,对上传的内容进行校验,确保是有效的图片

(5)约束选项

选项说明
null 如果为True,表示允许为空,默认值是False。相当于python的None
blank 如果为True,则该字段允许为空白,默认值是False。 相当于python的空字符串,“”
db_column 字段的名称,如果未指定,则使用属性的名称。
db_index 若值为True, 则在表中会为此字段创建索引,默认值是False。 相当于SQL语句中的key
default 默认值,当不填写数据时,使用该选项的值作为数据的默认值。
primary_key 如果为True,则该字段会成为模型的主键,默认值是False,一般不用设置,系统默认设置。
unique 如果为True,则该字段在表中必须有唯一值,默认值是False。相当于SQL语句中的unique

注意:null是数据库范畴的概念,blank是表单验证范畴的

(6) 外键

在设置外键时,需要通过on_delete选项指明主表删除数据时,对于外键引用表数据如何处理,在django.db.models中包含了可选常量:

  • CASCADE 级联,删除主表数据时连通一起删除外键表中数据

  • PROTECT 保护,通过抛出ProtectedError异常,来阻止删除主表中被外键应用的数据

  • SET_NULL 设置为NULL,仅在该字段null=True允许为null时可用

  • SET_DEFAULT 设置为默认值,仅在该字段设置了默认值时可用

  • SET() 设置为特定值或者调用特定方法,例如:

    from django.conf import settings
    from django.contrib.auth import get_user_model
    from django.db import models
    
    def get_sentinel_user():
        return get_user_model().objects.get_or_create(username='deleted')[0]
    
    class UserModel(models.Model):
        user = models.ForeignKey(
            settings.AUTH_USER_MODEL,
            on_delete=models.SET(get_sentinel_user),
        )
  • DO_NOTHING 不做任何操作,如果数据库前置指明级联性,此选项会抛出IntegrityError异常

商品分类表

idcategory 
1 蔬菜  
2 电脑  

商品信息表

idgoods_namecid
1 冬瓜 1
2 华为笔记本A1 2
3 茄子 1
  1. 当模型字段的on_delete=CASCADE, 删除蔬菜(id=1),则在外键cid=1的商品id1和3就被删除。

  2. 当模型字段的on_delete=PROTECT,删除蔬菜,mysql自动检查商品信息表,有没有cid=1的记录,有则提示必须先移除掉商品信息表中,id=1的所有记录以后才能删除蔬菜。

  3. 当模型字段的on_delete=SET_NULL,删除蔬菜以后,对应商品信息表,cid=1的数据的cid全部被改成cid=null

  4. 当模型字段的on_delete=SET_DEFAULT,删除蔬菜以后,对应商品信息表,cid=1的数据记录的cid被被设置默认值。

三、数据迁移

将模型类定义表架构的代码转换成SQL同步到数据库中,这个过程就是数据迁移。django中的数据迁移,就是一个类,这个类提供了一系列的终端命令,帮我们完成数据迁移的工作。

就两条命令:

1)生成迁移文件:

python manage.py makemigrations

本来在我们的子应用目录migrations下,没有任何文件,执行完这条命令,就生成了0001_initial.py文件了。

此时去数据库中show tables 查看一下,什么都没有。

2)同步到数据库中:

python manage.py migrate

再去数据库中show tables查看一下,有了很多张表了:

 

这里我发现我之前写的models.py文件,数据库配置的时候,定义Meta类应该在Student类内部,我上面写错了,因此生成的数据库表命名依然是默认的student_student,不是db_student。改正后重新执行以上两条命令,就会自动更新操作到数据库了,会在migrations目录下生成0002_alter_student_table.py文件:

这里更加深了对这个工作原理的理解,其实对数据库的每一步操作,这些文件就对应于数据库相关的操作,表结构变化什么的一次就会生成一个新的文件。

就是migrations会将你这次要做的迁徙工作与你上次比较,比如我想要再给这张表加两个字段classmate和description,改动了文件后再执行python manage.py makemigrations生成迁徙文件的时候,会有提示如下:

临时添加字段需要给字段加一个默认值,要么就是在终端弹提示这里写默认值,要么就是在代码添加字段那里加上默认值:classmate = models.CharField(db_column='class',max_length=5,db_index=True,verbose_name='班级',default="10")。

 

注:并非所有你下载models.py里的类都能映射到数据库中,生成响应的表。只有在settings文件中INSTALLED_APPS的配置中注册的应用才可以(每一次创建app的时候就应该确保这个注册完成,Django3.2会自动创建):

四、数据库基本操作

学习阶段,我们需要在控制台打印生成的sql语句,需要完成的配置在本文"一、配置数据库"部分第5小标题。

1.添加记录

两种方式:

save方法:通过创建模型类对象,执行对象的save()方法保存到数据库中。

create()方法:通过模型类.objects.create()保存,返回生成的模型类对象。

import datetime

from django.shortcuts import render,HttpResponse
from .models import Student

# Create your views here.

def add_student(request):

    # 添加记录
    # 方法1:实例化+save
    # 有default值的和null=TRUE的字段可以不传,不是这俩情况的就是not null,必须要传咯
    # 生日是date类型应该传一个date对象,或者用字符串"年-月-日"这个字符串格式很严格
    birth = datetime.date(2002,12,12)
    student1 = Student(name="张三",age=22,sex=1,birthday="2001-12-12")
    student2 = Student(name="李四", age=21, sex=1, birthday=birth)

    student1.save() # 创建对应的sql语句并执行
    student2.save()
    print(student2.name) #现在这个student是一个对象了,可以用.来拿它的某个字段值


    # 方法2:创建记录+返回对象 这个实战中用得多,需要掌握
    # objects里封装了关于模型类的增删改查的所有方法
    # create方法帮助我们实例化+save了,返回创建的模型类对象
    student3 = Student.objects.create(name="王五",age=26,sex=0,birthday="1998-4-18")
    print(student3.name)


    return HttpResponse("添加成功")

2.基础查询

十个查询函数都需要掌握。

ORM中针对查询结果的限制,提供了一个查询集【QuerySet】,这个QuerySet,是ORM中针对查询结果进行保存数据的一个类型,我们可以通过了解这个QuerySet进行使用,达到查询优化,或者限制查询结果数量的作用。

QuerySet,查询集,是很接近list的一个数据类型,但list里可以放置任意数据类型,但QuerySet里的元素是统一类型,比如说放的是我们现在使用的模型类对象,或者字典。

1)all():查询所有对象,返回queryset对象。

2)first()、last():分别为查询集的第一条记录和最后一条记录,返回的是模型类对象

注意:first()和last()最终生成的sql如下,可以看到,他其实是按升序降序排序后取一条记录的。

3)filter():筛选条件相匹配的对象,返回queryset对象。WHERE语句。

4)exclude():排除条件匹配的对象,返回queryset对象。WHERE NOT 语句。

5)get():返回与所给筛选条件相匹配的对象,返回结果有且只有一个, 如果符合筛选条件的对象超过一个或者没有都会抛出错误。返回一个查询到的模型类对象。

注意:get()产生的sql,如果查询没有结果,在数据库是可以执行查出空集并且不报错的,但是在Django中会报错“matching query does not exist.”:

6)order_by():对查询结果排序,是QuerySet类型的一个内置方法

7)count():查询集中对象的个数,返回int。

8)exists():判断查询集中是否有数据,如果有则返回True,没有则返回False。

9)values()、values_list():

  • values()把结果集中的模型对象转换成字典,并可以设置转换的字段列表,达到减少内存损耗,提高性能;
  • values_list()把结果集中的模型对象转换成列表,并可以设置转换的字段列表(元祖),达到减少内存损耗,提高性能

如果想要查询所有男生的姓名和年龄,那就需要先filter后value了,这个顺序和sql执行顺序也是一样的。

补充一个sql执行顺序的知识点:sql语句的执行顺序以及流程(最新,最全,直接用)_sql执行顺序-CSDN博客 

10)distinct():从返回结果中剔除重复纪录。返回queryset。

def select_student(request):

    # 1)all():返回一个QuerySet 这是一个很接近列表的一个数据类型
    # 查询所有学生
    all_set = Student.objects.all()
    print(all_set)

    # 2) first、last()
    first_stu = Student.objects.all()[0] # first_stu是一个模型类对象
    print(first_stu.name)

    first = Student.objects.first()  #first和last取出来的不是QuerySet,也是模型类对象!
    last = Student.objects.last()
    print(first.name,last.name)

    # 3)filter():WHERE
    # 查询所有的女生
    filter_set = Student.objects.filter(sex = 0)
    filter_set2 = Student.objects.filter(sex = 0,name="码云") # 逻辑与的关系 and
    print(filter_set2)

    # 4)exclude(): WHERE NOT语句
    # 查询除了张三以外的所有学生:
    exclude_set = Student.objects.exclude(name="张三")
    print(exclude_set)

    # 5)get():查询结果必须是有且仅有一条符合条件的记录。返回一个查询到的模型类对象
    get_stu = Student.objects.get(name="jfj")
    print(get_stu)

    # 6)order_by():对查询结果排序,是QuerySet类型的一个内置方法
    # 如果age有一样的,按照主键再排序。排序都是默认升序
    order_set = Student.objects.all().order_by("age")
    print(order_set)
    # 加一个负号就是降序,现在就是age升序吗,id降序
    order_set2 = Student.objects.all().order_by("age","-id")
    print(order_set2)

    # 7)count():查询集中对象的个数,返回int。
    count = Student.objects.all().count()
    print(count)

    # 8)exists():判断查询集中是否有数据,如果有则返回True,没有则返回False。
    # 它生成的sql语句:SELECT (1) AS `a` FROM `db_student` LIMIT 1,性能很好
    exist_stu = Student.objects.exists()
    print(exist_stu)


    # 9)values()、values_list():
    # values()返回QuerySet,但QuerySet里装的是字典,一个字典代表一条记录
    # 运行结果是: <QuerySet [{'name': '张三', 'age': 22, 'id': 1}, {'name': '李四', 'age': 21, 'id': 3},
    # {'name': '王五', 'age': 21, 'id': 4}, {'name': '张昭阳', 'age': 52, 'id': 5}]>
    value_set = Student.objects.values("name","age","id")
    print(value_set)
    # --------
    # values_list()返回QuerySet,但QuerySet里装的是列表
    # 运行结果:<QuerySet [('张三', 22, 1), ('李四', 21, 3), ('王五', 21, 4), ('张昭阳', 52, 5)]>
    value_list = Student.objects.values_list("name", "age", "id")
    print(value_list)

    # 扩展:查询所有男生的姓名和年龄
    # 生成的sql语句: SELECT `db_student`.`name`, `db_student`.`age` FROM `db_student` WHERE `db_student`.`sex` = 1 LIMIT 21;
    # 这里其实和sql执行语句一样的顺序:先where后select
    boy_stu = Student.objects.filter(sex=1).values("name","age")
    print(boy_stu)

    # 10)distinct():从返回结果中剔除重复纪录。返回queryset。
    # 查询所有学生出现过的年龄
    print(Student.objects.values("age").distinct())

    return HttpResponse("查询成功")

3.模糊查询

1)startswith、endswith

2)contains

    stu_set1 = Student.objects.filter(name__startswith="张")  #  
    stu_set2 = Student.objects.filter(name__endswith="三")
    stu_set3 = Student.objects.filter(name__contains="张")

以上三个查询生成的sql如下,自己品:

3)isnull

4)in

5)比较查询

6)日期查询

def fuzzy_student(request):

    # 1)startswith、endswith
    # 2)contains
    stu_set1 = Student.objects.filter(name__startswith="张")  # LIKE BINARY '张%'
    stu_set2 = Student.objects.filter(name__endswith="三")  # LIKE BINARY '%三'
    stu_set3 = Student.objects.filter(name__contains="张")  # LIKE BINARY '%张%'
    print(stu_set1, stu_set2, stu_set3)

    # 3)isnull 查询description不为空的数据
    stu_set4 = Student.objects.filter(description__isnull=False)
    print(stu_set4)

    # 5)比较查询 gt lt gte lte
    stu_set5 = Student.objects.filter(age__gte=30)
    stu_set6 = Student.objects.filter(age__lt=30)
    print(stu_set5,stu_set6)
    stu_set7 = Student.objects.filter(age__range=(20,30))
    print(stu_set7)

    # 4)in
    stu_set8 = Student.objects.filter(age__in=(21,22))
    print(stu_set8)

    # 6)日期查询
    # SELECT `db_student`.`id`, `db_student`.`name`, `db_student`.`age`, `db_student`.`sex`, `db_student`.`birthday`, `db_student`.`class`, `db_student`.`description` FROM `db_student`
    # WHERE `db_student`.`birthday` BETWEEN '1998-01-01' AND '1998-12-31' LIMIT 21;
    stu_set9 = Student.objects.filter(birthday__year=1998)
    print(stu_set9)

    return HttpResponse("模糊查询成功")

4.进阶查询

其实这部分感觉很死板,就是记住语法就行

学之前给之前定义的模型类加两个字段方便学习使用:

    chinese_score = models.IntegerField(db_column='chinese',default=100)
    math_score = models.IntegerField(db_column='math', default=100)

1)F查询

之前的查询都是对象的属性与常量值比较,两个属性怎么比较呢? 答:使用F对象,被定义在django.db.models中。

语法如下: F("XXX")

from django.db.models import F
# 获取从添加数据以后被改动过数据的学生
# SQL: select * from db_student where created_time=updated_time; 
student_list = Student.objects.exclude(created_time=F("updated_time"))
print(student_list) 

2)Q查询

如果需要实现逻辑或or的查询,需要使用Q()对象结合管道符|,Q对象被义在django.db.models中。语法如下:

Q(属性名__运算符=值)

Q(属性名__运算符=值) | Q(属性名__运算符=值)

例:查询年龄小于19或者大于20的学生,使用Q对象如下。

from django.db.models import Q
student_list = Student.objects.filter( Q(age__lt=19) | Q(age__gt=20) ).all()

Q对象可以使用&、|连接,&表示逻辑与,|表示逻辑或**

例:查询年龄大于20,或编号小于30的学生,只能使用Q对象实现

Student.objects.filter(Q(age__gt=20) | Q(pk__lt=30))

Q对象左边可以使用~操作符,表示非not。但是工作中,我们只会使用Q对象进行或的操作,只有多种嵌套复杂的查询条件才会使用&和~进行与和非得操作

例:查询编号不等于30的学生。

Student.objects.filter(~Q(pk=30))  

3)聚合查询

使用aggregate()过滤器调用聚合函数。聚合函数包括:Avg 平均,Count 数量,Max 最大,Min 最小,Sum 求和,被定义在django.db.models中。

例:查询学生的平均年龄。

from django.db.models import Sum,Count,Avg,Max,Min

Student.objects.aggregate(Avg('age'))

注意:aggregate的返回值是一个字典类型,格式如下:

{'属性名__聚合类小写':值}

使用count时一般不使用aggregate()过滤器。

例:查询学生总数。

Student.objects.count() # count函数的返回值是一个数字。

4)分组查询(重点、难点)

QuerySet对象.annotate()

annotate() 进行分组统计,按前面select 的字段进行 group by

annotate() 返回值依然是 queryset对象,增加了分组统计后的键值对

模型对象.objects.values("id").annotate(course=Count('course__sid')).values('id','course')

查询指定模型, 按id分组 , 将course下的sid字段计数,返回结果是 name字段 和 course计数结果

SQL原生语句中分组之后可以使用having过滤,在django中并没有提供having对应的方法,但是可以使用filter对分组结果进行过滤

所以filter在annotate之前表示where,在annotate之后代表having

同理,values在annotate之前代表分组group by的字段,在annotate之后代表数据查询结果返回的字段

5)原生查询

当我们提供的sql不够用了,可以使用sql本来的样子。

def advance_student(request):
    '''
    进阶查询
    :param request:
    :return:
    '''

    # 复习模糊查询,查询语文成绩大于80的同学
    print("--------")
    print(Student.objects.filter(chinese_score__gt=80))

    # 1)F查询 查询语文成绩大于数学成绩的同学
    print("^^^^^^^^^")
    # SELECT `db_student`.`id`, `db_student`.`name`, `db_student`.`age`, `db_student`.`sex`, `db_student`.`birthday`, `db_student`.`class`,
    # `db_student`.`description`, `db_student`.`chinese`, `db_student`.`math`
    # FROM `db_student` WHERE `db_student`.`chinese` > `db_student`.`math`
    print(Student.objects.filter(chinese_score__gt=F("math_score")))


    # 2)Q查询
    # 与的逻辑可以这么查,不借助Q查询
    print(Student.objects.filter(sex=0, age__gt=30))
    print("----------")
    print(Student.objects.filter(sex=0).filter(age__gt=30)) # filter出来的还是queryset,可以继续filter

    # 或的逻辑只能借助Q查询,与的逻辑也可以借助Q查询 与& 或| 非~
    # 年龄大于30或性别为女
    print(Student.objects.filter(Q(age__gt=30) | Q(sex=0)))
    print("----------")
    # 年龄大于30且性别为女
    print(Student.objects.filter(Q(age__gt=30) & Q(sex=0)))


    from django.db.models import Sum, Avg, Count, Max, Min
    # 3)聚合、分组查询 重点
    ret = Student.objects.aggregate(Avg("age")) #返回的是一个字典:结果:{'age__avg': 34.0}
    print(ret) # 结果:{'age__avg': 34.0}
    print("**********")
    ret2 = Student.objects.aggregate(avg_age =Avg("age")) # 可以给键自己取个名字,现在名字就是avg_age
    print(ret2) # 结果:{'avg_age': 34.0}
    print("**********")
    print(Student.objects.aggregate(max_chinese=Max("chinese_score"))) # 这里注意要用这里的字段名字而不是数据库的字段chinese

    # 4)分组查询 annotate():group by    values():对应的是group by的字段
    # 查男生女生的数学平均分
    # SELECT `db_student`.`sex`, AVG(`db_student`.`math`) AS `math_score__avg`
    # FROM `db_student` GROUP BY `db_student`.`sex` ORDER BY NULL LIMIT 21; args=()
    ret3 = Student.objects.values("sex").annotate(math_avg = Avg("math_score"))
    print(ret3) # 返回的还是queryset,里面装了多个字典:<QuerySet [{'sex': 1, 'math_score__avg': 75.25}, {'sex': 0, 'math_score__avg': 95.6667}]>
    # 查询每个班级的数学平均分
    ret4 = Student.objects.values("classmate").annotate(math_avg = Avg("math_score"))
    print(ret4)

    # 5)原生sql
    # 这里sql语句竟然严格区分字段大小写,因为Django的模型类里id小写,如果大写就不识别,报错: Raw query must include the primary key
    ret5 = Student.objects.raw("SELECT id,name FROM DB_STUDENT")
    print(type(ret5),ret5)
    for item in ret5:
        print(item,type(item))


    return HttpResponse("进阶查询成功")

5.修改记录

1)基于模型对象的save更新数据

2)基于queryset对象的update更新数据(推荐)

def update_student(request):
    '''
    修改
    :param request:
    :return:
    '''

    # 1)基于模型对象save操作
    stu = Student.objects.get(name="李四")
    stu.chinese_score = 88
    stu.save()
    # 这种方法不推荐,因为生成的sql如下,它其实是将所有的字段值都更新了一遍,性能很差:
    # UPDATE `db_student` SET `name` = '李四', `age` = 21, `sex` = 1, `birthday` = '2002-12-12', `class` = '003', `description` = 'null', `chinese` = 88, `math` = 100
    # WHERE `db_student`.`id` = 3; args=('李四', 21, 1, '2002-12-12', '003', 'null', 88, 100, 3)
    print(Student.objects.get(name="李四").chinese_score)

    # 2)基于querySet对象的update方法,update有返回值,返回更新的条数
    print(type(Student.objects.get(name="李四"))) # get方法拿到的是模型对象,不是queryset类型用不了update方法
    stu2 = Student.objects.filter(name="李四")
    stu2.update(math_score=60) # 它会更新整个queryset里的记录,所以你过滤的时候要小心自己想要更新的是什么
    print(Student.objects.get(name="李四").math_score)
    # 扩展:将年龄小于30岁的学生语文成绩都降20分
Student.objects.filter(age__lt=30).update(chinese_score = F("chinese_score")-20)
return HttpResponse("修改成功")

6.删除记录

1)基于模型类对象删除

2)基于queryset对象删除

def delete_student(request):
    # 1)基于模型类对象删除
    stu = Student.objects.get(pk=7)  # 也可以写成id=7,但是假如忘了主键是哪个字段,就可以用这个pk代替
    stu.delete()

    # 2)基于queryset删除 但是这个删就是整个queryset都给你删咯,依旧是过滤的时候小心想好要删哪些
    #  DELETE FROM `db_student` WHERE `db_student`.`age` > 30;
    stu2 = Student.objects.filter(age__gt=30)
    stu2.delete()

    return HttpResponse("删除成功")

 

 

posted on 2023-12-11 18:44  szdbjooo  阅读(57)  评论(0)    收藏  举报