ORM框架2-数据库操作


数据库操作

操作对象: orm/view.py # orm为创建的子应用

ORM框架2-数据库操作

from django.db import models


# 模型类必须要直接或者间接继承于 models.Model
class BaseModel(models.Model):
    created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    # SQL:created_time datetime(6) null comment="创建时间"

    updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
    # SQL:updated_time datetime(6) null comment="更新时间"

    # auto_now_add 当数据添加时设置当前时间为默认值
    # auto_now= 当数据添加/更新时, 设置当前时间为默认值

    class Meta(object):
        abstract = True  # 设置当前模型为抽象模型, 当系统运行时, 不会认为这是一个数据表对应的模型.


# Create your models here.
class Student(BaseModel):
    SEX_CHOICES = (
        (0, "女生"),
        (1, "男生"),
        (2, "保密"),
    )
    # 以类属性的方式声明表结构的字段
    # 属性名 = models.数据类型(db_column="表字段名", max_length=最大长度, db_index=是否为当前字段创建普通索引, verbose_name="中文提示")
    # 注意:db_column属性如果不写则默认使用属性名作为表的字段名进行对应
    # django中默认会自动给模型创主键名为ID,所以我们一般不需要手动创建
    # 如果要手动创建主键ID则代码如下:
    # id = models.BigAutoField(primary_key=True, verbose_name="主键")  # django会自动在创建数据表的时候生成id主键/还设置了一个调用别名 pk
    # SQL: id bigint primary_key auto_increment not null comment="主键",
    name = models.CharField(max_length=20, db_index=True, verbose_name="姓名")
    # SQL: name varchar(20) not null comment="姓名"
    # SQL: index(name),

    age = models.SmallIntegerField(verbose_name="年龄")
    # SQL: age smallint not null comment="年龄"

    sex = models.BooleanField(default=0, verbose_name="性别", help_text="辅助提示")
    # SQL: sex tinyint not null default 0 comment="性别"

    # sex = models.SmallIntegerField(choices=SEX_CHOICES, default=2, verbose_name="性别")
    # SQL = sex smallint not null default 2 comment="性别"

    classmate = models.CharField(db_column="class", max_length=5, db_index=True, verbose_name="班级")
    # SQL: class varchar(5) not null comment="班级"
    # SQL: index(class)

    description = models.TextField(blank=True, null=True, verbose_name="个性签名")
    # SQL: description longtext default "" null comment="个性签名"


    class Meta:  # 子类中,可以针对当前模型类对应的数据设置一些独立的选项
        db_table = "tb_student"  # 设置当前Student模型对象对应的数据库的表名, 如果没有指定表名,则默认为子应用目录名_模型名称,例如: student_student
        verbose_name = '学生信息表'  # 在admin站点中显示的名称
        verbose_name_plural = verbose_name  # 显示的复数名称

    # 3. 自定义数据库操作方法
    def __str__(self):
        """定义每个数据对象的显示信息"""
        return "<User %s>" % self.name

orm/models.py

1 增加数据

方法 描述 代码
save 通过创建模型类对象,执行对象的save()方法保存到数据库中。
from django.views import View
from django.http import HttpResponse
from .models import Student


class Student1(View):
    def get(self, request):
        """添加一条数据"""
        # ORM对于数据的所有操作都是有子类objects来提供的。objects,可以被修改,开发中一般叫objects为"模型管理器"
        student = Student(
            name="刘德华",
            age=17,
            xingbie=True,
            classmate=301,
            description="一杯忘情水",
        )
        # 自动执行添加数据
        student.save()
        # 添加后的模型会多出一个主键数据,可以通过id或者pk来读取
        print(student.id)  # print(student.pk)
        return HttpResponse('ok')
create 通过模型类.objects.create()保存。
# 返回值就是添加后的模型对象,会有ID主键的
student = Student.objects.create(
    name="赵本山",
    age=50,
    sex=True,
    classmate=301,
    description="一段小品"
)
print(student)# <User 赵本山>
print(student.pk) 
return HttpResponse('ok')
bulk_create 通过模型类.objects.bulk_create()批量添加数据
stu1 = Student(name="小黄1号", age=17, xingbie=True, classmate=301, description="hello")
stu2 = Student(name="小黄2号", age=17, xingbie=False, classmate=301, description="hello")
stu3 = Student(name="小黄3号", age=17, xingbie=True, classmate=301, description="hello")
stu_list = [stu1,stu2,stu3]
ret = Student.objects.bulk_create(stu_list)
print(ret)
return HttpResponse('ok')

2 基本查询

方法 描述 代码
get

查询单一结果,如果不存在或者返回多个结果会抛出异常。

查询不到, 则返回模型类.DoesNotExist异常。

查询多个, 则返回模型类.MultipleObjectsReturned异常。

    def get(self,request):
        """基本查询数据"""
        """get 获取一条数据"""
        try:
            student = Student.objects.get(name="小黄")
            print(student, type(student))
            print(student.name)
            print(student.description)
        except Student.DoesNotExist:
            print("没有查询结果!")
        except Student.MultipleObjectsReturned:
            print("当前数据不是唯一的结果!")
        return HttpResponse("OK")
first 查询一个结果, 查询不到,则返回None,查询多个,返回查询结果列表的第一个。
def get(self, request):
        student = Student.objects.first()
        # SQL: select * from tb_student limit 1;
        print(student) # <User 赵华>
        if student:  # 判断如果student不是None
            print(student.description)
        return HttpResponse('ok')
all 查询所有结果。查询不到,则返回空列表对象。默认为all
def get(self, request):
    student_list = Student.objects.all()
    print(student_list)       # 获取结果列表 <QuerySet [<Student: <User 赵华>>,
    print(len(student_list))  # 获取结果列表的长度

    # 把结果列表中的所有模型对象转化成字典结构
    student_list = Student.objects.all().values()
    print(student_list) # <QuerySet [{'id': 1,

    # 把结果列表中的所有模型对象转换成元组结构
    student_list = Student.objects.all().values_list()
    print(student_list) # <QuerySet [(1,

    """使用filter查询过滤数据,并返回所有符合条件的结果
    filter(字段名=条件值)
    filter(字段名=条件值, 字段名=条件值)  # 相当于and
    """
    student_list = Student.objects.filter(classmate=302).all()
    print(student_list)
    return HttpResponse('ok')

3 更新数据

方法 描述 代码
save 修改模型类对象的属性,然后执行save()方法同步到数据库中
    def get(self, request):
        # 先把要更新的数据查询出来,得到一个模型类对象
        student = Student.objects.filter(name="小白").first()
        # SQL: select * from tb_student where name='小白' limit1;
        if student:
            student.name = "小黑"
            student.age = 18
            student.save()  # 把当前模型的中字段值同步到数据库
        return HttpResponse('ok')
update 使用模型类.objects.filter().update(),基于update来完成更新满足条件的所有数据,结果是受影响的行数
    def get(self, request):
        """update 更新多条数据"""
        # 基于update这种操作在数据库操作中,一般称之为"乐观锁" update操作的执行效率比save要高!
        # update如果条件设置宽松,可以修改多条数据
        Student.objects.filter(name="刘德华").update(name="刘福荣")
        # SQL: update tb_student set name="刘福荣" where name="刘德华";
        return HttpResponse('ok')

4 删除数据

方法 描述 代码
模型类对象.delete() 删除一条
        student = Student.objects.filter(name="小白").first()
        if student:
            # 调用模型对象的delete方法进行删除
            student.delete()
模型类.objects.filter().delete() 删除多条
Student.objects.filter(name="小黄").delete()

5 过滤条件

ORM在内部生成SQL中的where子句时,提供3个方法可以帮我们实现where过滤操作,包括:

  • filter 过滤出符合条件的多个结果

  • exclude 排除掉符合条件的多个结果,与filter相反,与filter互斥。

  • get 过滤单一结果, 结果不是一个,会报错。

对于过滤条件的使用,上述三个方法相同,但是互斥的,只能使用任意1个。以下内容以filter为例。

5.1 语法

# 单表的过滤:
模型类.objects.filter(属性名称__运算符=值)  # 此处的运算符是django的ORM提供的英文单词的运算符,与python的运算符不一样。
# 多表的过滤
模型类.objects.filter(外键属性名称__外键模型的属性名称__运算符=值)
# 属性名称和比较运算符间使用两个英文下划线

5.2 过滤条件

过滤条件 运算符 描述 代码
相等 exact 表示判断值是否相等
    def get(self, request):
        student_list = Student.objects.filter(name__exact="吴杰").all()
        # student_list = Student.objects.filter(name="吴杰").all()  # 常用!!
        print(student_list)
        return HttpResponse('ok')
模糊查询    contains 是否包含。
        student1 = Student.objects.filter(name__contains='华')
        print(student1) # <QuerySet [<Student: <User 赵华>>, <Student: <User 龙华>>,
startswith 指定值开头
        student2 = Student.objects.filter(name__endswith='华')
        print(student2) # QuerySet [<Student: <User 赵华>>, <Student: <User 龙华>>,
endswith 指定值结尾
        student3 = Student.objects.filter(name__startswith="江")
        print(student3) # <QuerySet [<Student: <User 江宸轩>>,
空查询  isnull 字段值是否为null。空查询
        student = Student.objects.filter(description__isnull=True)
        print(student)
范围查询  in 是否包含在范围内
        student = Student.objects.filter(classmate__in=[301, 302, 303]).all().values("id", "name", "classmate")
        print(student) # <QuerySet [{'id': 2, 'name': '程星云', 'classmate': '301'},
取值范围 range 设置开始值与结束值范围,进行数值判断,符合范围的数据被查询出来。也可以设置时间范围。
        # SQL: SELECT ... WHERE id BETWEEN 51 and 67;
        student_list = Student.objects.filter(id__range=(51, 67)).values("id", "name")
        print(student_list) # <QuerySet [{'id': 51, 'name': '董晴'},
比较查询  gt(e) 大于(等于)
    def get(self, request):
        # # 年龄大于20的
        # # age__gt=20  ---->>>  where age > 20
        student_list = Student.objects.filter(age__gt=22).values("name", "age")  # 后面任何方法,默认补充all()
        print(student_list) # <QuerySet [{'name': '欧阳博', 'age': 23},

        # # 年龄小于19的
        # # age__lt=19  ---->>>  where age < 19
        student_list = Student.objects.filter(age__lt=19).all().values("name","age")
        print(student_list) # <QuerySet [{'name': '张小玉', 'age': 18},
        
        # 年龄不等于19的
        # 使用exclude把符合条件的排除掉。不等于的运算符,使用exclude()过滤器。
        student_list = Student.objects.exclude(age=19).all().values("name","age")
        print(student_list)
        return HttpResponse('ok')
lt(e) 小于(等于)
日期查询    year 对日期时间类型的属性进行运算
from django.views import View
from django.http import HttpResponse
from .models import Student
from datetime import datetime, timedelta

class Student1(View):
    def get(self, request):
        student_list = Student.objects.filter(
            created_time__year=2022,
            created_time__month=7,
            created_time__day=20
        ).values("name", "created_time")
        print(student_list)
        # 精确时间查询
        from datetime import datetime
        add_time = "2021-08-18 16:19:38"
        timestamp = datetime.strptime(add_time, "%Y-%m-%d %H:%M:%S")
        student_list = Student.objects.filter(
            created_time=timestamp,
        ).all()
        print(student_list)
        # 查询时间范围在 "2021-08-18 16:19:38" ~ "2021-08-18 16:21:56" 的数据
        start_time = "2021-08-18 16:19:38"
        end_time = "2021-08-18 16:21:56"
        end_time_timestamp = datetime.strptime(end_time, "%Y-%m-%d %H:%M:%S")
        end_time_next = end_time_timestamp + timedelta(seconds=1)
        student_list = Student.objects.filter(
            created_time__gte=start_time,
            created_time__lt=end_time_next,
        ).all()
        print(student_list)
        return HttpResponse('ok')
month
day
weekday
 hour
minute
second
F对象 用于在SQL语句中针对字段之间的值进行比较的查询
"""F对象,字段间的值比较查询"""
from django.db.models import F
# 查询出入学以后,数据没有被修改过的学生信息
student = Student.objects.filter(created_time=F("updated_time")).values("name","created_time","updated_time")
print(student)
Q对象 多个过滤器逐个调用表示逻辑与关系,同sql语句中where部分的and关键字。
        """Q对象,复杂逻辑查询,针对多条件进行与或非处理"""
        from django.db.models import Q
        # 多个与 and  Q(条件) & Q(条件)
        # 查询出301班的男生
        # student = Student.objects.filter(Q(classmate=301) & Q(xingbie=1)).values("name","xingbie","classmate")
        # 上面也可以简写成
        # student = Student.objects.filter(classmate=301, xingbie=1).values("name", "xingbie", "classmate")
        """单纯的多个条件并立的情况下,没必要使用到Q对象进行处理,直接编写多个条件,使用逗号串联即可"""

        # NOT
        student = Student.objects.filter(~Q(classmate=301)).values("name", "age", "sex")

        # 多个或,or Q(条件) | Q(条件)
        # 查询出301班的男生 或者 302班的男生
        # student = Student.objects.filter(Q(classmate=301, xingbie=1) | Q(classmate=302, xingbie=1)).values("name",
        # 上面也可以简写成
        # student = Student.objects.filter(classmate__in=[301, 302], xingbie=1).values("name", "xingbie", "classmate")
        # print(student)

 

6 结果排序

方法 描述 代码
order_by

order_by(“第一排序字段”,”第二排序字段”,….)

当前第一字段的值一样时,参考第二字段进行排序,第二字段的值一样时,参考第三字段进行排序..

# order_by("id")   # 表示按id字段的值进行升序排序,id数值从小到大
# order_by("-id")  # 表示按id字段的值进行降序排序,id数值从大到小
# 调用了order_by以后,如果没有后续声明返回结果的all方法时,默认使用all()进行结果查询
student = Student.objects.order_by("-id")
print(student)

 

7 限制查询

7.1 说明

ORM中针对查询结果的数量限制,提供了一个查询集对象[QuerySet].这个QuerySet,是ORM中针对查询结果进行临时保存数据的一个容器对象,我们可以通过对QuerySet进行使用,达到查询优化的目的,也或者限制查询结果数量的作用。

 

7.2 查询集 QuerySet

1 查询集,也称查询结果集、QuerySet,表示从数据库中获取的对象集合。当调用如下ORM提供的过滤器方法时,Django会返回查询集(而不是简单的列表):

  • all():返回所有数据。

  • filter():返回满足条件的数据。filter会默认调用all方法。

  • exclude():返回满足条件之外的数据。exclude会默认调用all方法

  • order_by():对结果进行排序。order_by会默认调用all方法

 

2 判断某一个查询集中是否有数据:

方法 描述 代码
exists() 判断查询集中是否有数据,如果有则返回True,没有则返回False。
        student_list = Student.objects.filter(classmate="301")
        student_list = student_list.order_by("-age")
        student_list = student_list.filter(sex=1)
        ret = student_list.exists()
        print(ret) # True
values() 把结果集中的模型对象转换成字典,并可以设置转换的字段列表,达到减少内存损耗,提高性能。
   student_list = Student.objects.filter(classmate="301")
        student_list = student_list.order_by("-age")
        student_list = student_list.filter(sex=1)
        ret1 = student_list.values()  # 默认把所有字段全部转换并返回
        ret2 = student_list.values_list()  # 默认把所有字段全部转换并返回
        ret3 = student_list.values_list("id", "name", "age")  # 可以通过参数设置要转换的字段并返回
        print(ret1, type(ret1))  # <QuerySet [{'id': 102,..}]><class 'django.db.models.query.QuerySet'>
        print(ret2, type(ret2))  # <QuerySet [(102,..)]> <class 'django.db.models.query.QuerySet'>
        print(ret3, type(ret3))  # <QuerySet [(102, '赵本山', 50),..]> <class 'django.db.models.query.QuerySet'>
values_list() 把结果集中的模型对象转换成列表,并可以设置转换的字段列表(元祖),达到减少内存损耗,提高性能

 

3 QuerySet的两大特性

     
惰性执行

QuerySet查询集在创建时是不会访问数据库执行SQL语句,

直到模型对象被调用输出或者调用模型对象的属性时,才会真正的访问数据库执行SQL语句,

调用模型的情况包括循环迭代、序列化、与if合用,print的时候。

    def get(self, request):
        '''惰性执行,可以让重复的查询操作,只执行一次'''
        # 创建了一个查询集对象student_list,此时没有执行SQL语句的
        student_list = Student.objects.all()
        # 继续执行遍历迭代、或打印操作之后操作后,才进行了数据库的查询
        for student in student_list:
            pass
        return HttpResponse('ok')
缓存结果 使用同一个查询集,第一次使用时会发生数据库的查询,然后Django会把结果缓存下来,再次使用这个查询集时会使用缓存的数据,减少了数据库的查询次数
        """两个查询集,无法重用缓存,每次查询都会与数据库进行一次交互,增加了数据库的负载"""
        [student.id for student in Student.objects.all()]  # 因为没有缓存查询集到变量中,所以此处第一次执行了SQL语句
        [student.id for student in Student.objects.all()]  # 因为没有缓存查询集到变量中,所以此处第一次执行了SQL语句

        '''经过存储后,可以重用查询集,第二次使用缓存中的数据'''
        student_list = Student.objects.all()
        [student.id for student in student_list]  # 因为上面保存到查询到变量中,所以此处执行了SQL语句
        [student.id for student in student_list]  # 此处调用了之前的缓存数据

 

4 限制结果数量

django中可以对查询集QuerySet进行取下标或切片操作,等同于SQL中的limit和offset子句。对查询集QuerySet进行切片后返回一个新的查询集,但还是不会立即执行查询。

注意:QuerySet毕竟不是真正的列表,所以它不支持负数索引。

    def get(self, request):
        '''如果获取一个对象,直接使用[0],等同于[0:1].get(),
        但是如果没有数据,[0]引发IndexError异常,
        [0:1].get()如果没有数据引发DoesNotExist异常。'''
        # 查询集结果数量的下标和切片操作
        qs = Student.objects.all()
        # print(qs[0])  # 第1条数据, ORM会自动识别这个操作并转化成SQL语句的 limit 1
        # print(qs[2])  # 第3条数据, ORM会自动识别这个操作并转化成SQL语句的 limit 1 offset 2【表示跳过2条数据,从第3条数据开始取,取1条数据,也就是取3这条数据。】
        # print(qs[:2])   # 前2条数据, ORM会自动识别这个操作并转化成SQL语句的 limit 2
        # print(qs[1:4])  # 第1,2,3 数据, ORM会自动识别这个操作并转化成SQL语句的 limit 3 offset 1。
        # print( qs[-1] )   # 报错!!!不能使用负数
        return HttpResponse('ok')

 

8 聚合函数

django中,可以使用aggregate()过滤器调用聚合函数。聚合函数包括:Avg 平均,Count 总数,Max 最大,Min 最小,Sum 求和,导入from django.db.models import Max, Min, Count

中。

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

        # 查询301班年龄最大的学生
        ret = Student.objects.filter(classmate=301).aggregate(Max("age"))
        # print(ret)  # {'age__max': 23}

        # 查新301班入学最早的学生[也就是ID最小的]
        ret = Student.objects.filter(classmate=301).aggregate(c=Min("id"))
        print(ret)  # {'id__min': 2}  ==> {'c': 2}

        # 调用count方法即可,不需要经过aggregate的调用
        ret = Student.objects.filter(classmate=301).count()
        print(ret)  # 11

 

9 分组函数 

django中,可以使用annotate()调用分组函数。等同于SQL中的select GROUP BY语句,对一个或多个列对结果集进行分组。

注意:SQL原生语句中分组之后可以使用having过滤,在django中并没有提供having对应的方法,但是可以使用filter对分组结果进行过滤。所以filter在annotate之前,表示where,在annotate之后代表having。同理,values在annotate之前,代表分组的字段,在annotate之后代表数据查询结果返回的字段列

        # 针对多个字段进行分组  values("classmate","xingbie").annotate(total=Count("id")) 按 班级和性别 统计人数
        # ret = Student.objects.values("classmate","sex").annotate(total=Count("id"))
        # print(ret)
        '''<QuerySet [{'classmate': '307', 'sex': True, 'total': 3},
            {'classmate': '301', 'sex': True, 'total': 8},
            {'classmate': '301', 'sex': False, 'total': 2},..'''
        # 查询出女生数量在2个以上的班级
        ret = Student.objects.filter(sex=True).values("classmate").annotate(total=Count("id")).values("classmate","total").filter(total__gte=2)
        print(ret)
        '''<QuerySet [{'classmate': '307', 'sex': True, 'total': 3},
            {'classmate': '301', 'sex': True, 'total': 8},..'''

 

10 原生查询

在django中,可以引入pymysql执行SQL,也可以调用ORM提供的raw方法来执行SQL语句。如果使用raw方法执行SQL语句,则返回结果是QuerySet,这个返回结果在操作字段时,会有额外性能损耗。

# 查询所有学生的班级、年龄、姓名和性别
sql = "SELECT id,name,sex,age,class FROM `db_student`"
ret = Student.objects.raw(sql)
# 针对原生SQL语句中已经查询出来的字段,只会查询一遍,
# 但是如果SQL语句没有查询出来的字段,而在模型中调用,则会由ORM再次调用数据库查询,把数据临时查询出来。
for student in ret:
    print(student)
    print(student.description)

 

11 多库共存

在django中,settings.py配置的DATABASES配置项允许注册多个数据库,可以支持在项目中随时切换操作不同的数据库。

        """ORM模型操作切换数据连接"""
        # 类名,可以使用django.db.models.Model 或者Model的子类,当然最好是为当前操作的数据库中的数据表简历一个对应的模型
        ret = Student.objects.using("数据库名").raw("SELECT * FROM mf_user")
        for row in ret:
            print(row.__dict__)

 

 

原创文章,作者:端木书台,如若转载,请注明出处:https://blog.ytso.com/276594.html

(0)
上一篇 2022年7月24日
下一篇 2022年7月24日

相关推荐

发表回复

登录后才能评论