5.django-模型ORM
Django中内嵌了ORM框架,不需要直接编写SQL语句进行数据库的操作,通过定义模型类来完成对数据库中表的操作
O:Object,也就是类对象的意思 R:Relation,关系数据库中表的意思 M:Mapping:映射
模型类:映射的是sql语句中的table表 类对象:映射表中的某一行数据 类成员:映射表的字段
ORM的优点:
定义模型类,更加容易维护
不必编写复杂的SQL语句,开发效率高
兼容多种数据库,可以自由切换数据库
ORM的缺点:
ORM不是轻量级工具,需要花费较大的精力学习
性能相对原生的SQL差一些
ORM的使用主要以下四步
1. 配置数据库的连接
2. 在model.py中定义模型类
3. 生成数据库迁移文件并执行迁移文件
4. 通过模型类对象提供的方法操作数据库
1.配置数据库连接
1.1 配置单个mysql数据库
django中默认配置的数据库是sqlite,如果想切换为mysql,操作如下
- 安装pymysql模块
pip install pymysql
- 在工程项目的
__init__.py
文件中添加如下语句
from pymysql import install_as_MySQLdb
install_as_MySQLdb() # 让pymysql以MySQLDB的运行模式和Django的ORM对接运行 - 修改setting.py中的配置如下
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 指定mysql引擎
'HOST': '127.0.0.1', # 数据库主机
'PORT': 3306, # 数据库端口
'USER': 'root', # 数据库用户名
'PASSWORD': '123', # 数据库用户密码
'NAME': 'student' # 数据库名字
}
} - 在mysql中创建对应的数据库
ORM 无法主动创建数据库,我们需要在连接的数据库中先创建数据库
create database student default charset=utf8mb4; # mysql8.0之前的版本
- 如果想打印orm转换过程中的sql,需要在settings.py中进行如下配置
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
1.2 配置多个mysql数据库
jango支持连接多个mysql数据库
主要在配置文件中添加如下(在上面的基础上)
DATABASES = {
"default": {
'ENGINE': 'dj_db_conn_pool.backends.mysql',
'NAME': 'day05db', # 数据库名字
'USER': 'root',
'PASSWORD': 'root123',
'HOST': '127.0.0.1', # ip
'PORT': 3306,
'POOL_OPTIONS': {
'POOL_SIZE': 10, # 最小
'MAX_OVERFLOW': 10, # 在最小的基础上,还可以增加10个,即:最大20个。
'RECYCLE': 24 * 60 * 60, # 连接可以被重复用多久,超过会重新创建,-1表示永久。
'TIMEOUT': 30, # 池中没有连接最多等待的时间。
}
},
"bak": {
'ENGINE': 'dj_db_conn_pool.backends.mysql',
'NAME': 'day05bak', # 数据库名字
'USER': 'root',
'PASSWORD': 'root123',
'HOST': '127.0.0.1', # ip
'PORT': 3306,
'POOL_OPTIONS': {
'POOL_SIZE': 10, # 最小
'MAX_OVERFLOW': 10, # 在最小的基础上,还可以增加10个,即:最大20个。
'RECYCLE': 24 * 60 * 60, # 连接可以被重复用多久,超过会重新创建,-1表示永久。
'TIMEOUT': 30, # 池中没有连接最多等待的时间。
}
},
}
2.定义模型类
模型类的定义
模型类一般定义在子项目的models.py
模型类必须直接或者间接的继承django.db.midels.Model类
2.1 单张表的模型类创建
2.1.1 表名设置
模型类如果未指明表名db_table,Django默认以 小写app应用名_小写模型类名 为数据库表名。
可通过db_table 指明数据库表名。
2.1.2 关于主键
django会为表创建自动增长的主键列,每个模型只能有一个主键列。
如果使用选项设置某个字段的约束属性为主键列(primary_key)后,django不会再创建自动增长的主键列。
一般情况下我们不需要主动创建,默认创建的主键属性明为id,或者pk
class Student(models.Model):
# django会自动在创建数据表的时候生成id主键/还设置了一个调用别名 pk
id = models.AutoField(primary_key=True, null=False, verbose_name="主键") # 设置主键
2.1.3 属性命名规范
不能是python的保留关键字
不允许使用两个连续的下划线,这是由django的查询方式决定的,__是关键字
定义属性时需要指定字段类型,通过字段类型的参数自定选项,如下:
属性名 = models.字段类型(约束选项, verbose_name="注释")
2.1.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,对上传的内容进行校验,确保是有效的图片 |
2.1.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 |
max_length | 字段的最大长度 |
verbose_name | 字段别名,以后展示在前端可以使用 |
2.2 关联表的模型类创建
关联模型类的关系有以下三种
一对一
一对多
多对多
2.2.1 一对一
两张表是一对一的关系,也就是一张表的一条数据只能对应另外一张表的一条数据
OneToOneField()
如:学生和学生详细信息
class Student(models.Model):
name = models.CharField(max_length=32, verbose_name='学生姓名') class StudentDetail(models.Model):
telephone = models.CharField(max_length=16, verbose_name='学生电话')
address = models.CharField(max_length=16, verbose_name="学生住址")
# 一对一
student = models.OneToOneField(to=Student, verbose_name='学生ID', on_delete=models.CASCADE)
2.2.2 一对多
两张表是一对多的关系,也就是一张表的一条数据对应另外一张表的多条数据
ForeignKey
如:学生表和班级表,一个班级可以有很多学生
class Cls(models.Model):
title = models.CharField(max_length=32, verbose_name='班级名称') class Student(models.Model):
name = models.CharField(max_length=32, verbose_name='学生姓名')
# 一对多
cls = models.ForeignKey(to=Cls, verbose_name='班级名称', on_delete=models.CASCADE)
注意:在一对多中,必须指定on_delete删除模式,就比如删除了班级表中的数据,学生表中相关的数据该做何处理,常见的处理如下
- CASCADE, # 级联删除,即关联的表删除某一项数据,此表关联的数据都会被删除
- DO_NOTHING, # 删除关联数据,什么也不做
- SET_NULL, # 删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
- SET_DEFAULT # 设置为默认值,仅在该字段设置了默认值时可用
SET(值), # 删除关联数据, 与之关联的值设置为指定值
2.2.3 多对多
多对多就是一张表的一行数据可以对应另一张表的多行数据,反之亦然
ManyToManyField
或者自定义第三个类
如学生表和选修课程表,这需要通过第三张表的引入,来关联二者的关系
方式一(推荐)
class Course(models.Model):
title = models.CharField(max_length=32, verbose_name='课程名称') class Student(models.Model):
name = models.CharField(max_length=32, verbose_name='学生姓名')# 多对多
course = models.ManyToManyField(to=Course, verbose_name='课程')
方式二
class Course(models.Model):
title = models.CharField(max_length=32, verbose_name='课程名称') class Student(models.Model):
name = models.CharField(max_length=32, verbose_name='学生姓名')class Student_Course(models.Model):
student = models.ForeignKey(to=Student, on_delete=models.CASCADE)
course = models.ForeignKey(to=Course, on_delete=models.CASCADE)
3.数据迁移
在django中数据迁移分为两步
- 生成迁移文件
python manage.py makemigrations
- 同步到数据库
python manage.py migrate
4.操作数据库
4.1 单张表的数据库基本操作
4.1.1 添加记录
- save()方法
通过创建模型类对象,执行对象的save()方法保存到数据库中role = Role(title='销售顾问')
role.save() - create()方法
过模型类.objects.create()保存,返回生成的模型类对象
obj = Role.objects.create(title='总经理')
4.1.2 修改记录
- 使用save更新数据【不建议】
会将对象的所有值都更新一遍student = Student.objects.filter(name='刘德华').first()
student.age = 19
student.classmate = "303"
student.save() - update更新(推荐)
# update是全局更新,只要符合更新的条件,则全部更新,因此强烈建议加上条件!!!
student = Student.objects.filter(name="赵华",age=22).update(name="刘芙蓉",sex=True)
4.1.3 删除记录
模型类对象.delete
student = Student.objects.get(id=13)
student.delete()- 模型类.objects.filter().delete()
Student.objects.filter(id=14).delete()
4.1.4 基础查询
ORM中针对查询结果的限制,提供了一个查询集[QuerySet].这个QuerySet,是ORM中针对查询结果进行保存数据的一个类型,我们可以通过了解这个QuerySet进行使用,达到查询优化,或者限制查询结果数量的作用。
- all()
查询所有对象,返回一个queryset对象,所有对象的集合queryset = Role.objects.all()
# <QuerySet [<Role: Role object (1)>, <Role: Role object (2)>]> - filter()
筛选条件相匹配的对象,返回queryset对象。
queryset = Role.objects.filter(title='总经理')
# <QuerySet [<Role: Role object (1)>]> - get()
返回与所给筛选条件相匹配的对象,返回结果有且只有一个, 如果符合筛选条件的对象超过一个或者没有都会抛出错误。
try:
student = Student.objects.get(name="kunmzhao")
print(student)
print(student.description)
except Student.MultipleObjectsReturned:
print("查询得到多个结果!")
except Student.DoesNotExist:
print("查询结果不存在!") - first()/last()
分别为查询集的第一条记录和最后一条记录,返回一个对象
stu01 = Student.objects.first()
stu02 = Student.objects.last() - exclude()
筛选条件不匹配的对象,返回queryset对象。
# 查询张三以外的所有的学生
students = Student.objects.exclude(name="张三") - order_by()
对查询结果排序,返回一个queryset对象
# order_by("字段") # 按指定字段正序显示,相当于 asc 从小到大
# order_by("-字段") # 按字段倒序排列,相当于 desc 从大到小
# order_by("第一排序","第二排序",...) # 查询所有的男学生按年龄从高到低展示
# students = Student.objects.all().order_by("-age","-id")
students = Student.objects.filter(sex=1).order_by("-age", "-id") - count()
查询集中对象的个数
# 查询所有男生的个数
count = Student.objects.filter(sex=1).count()
print(count) - exists()
判断查询集中是否有数据,如果有则返回True,没有则返回False
# 查询Student表中是否存在学生
Student.objects.exists() - values()/values_list()
value()
把结果集中的模型对象转换成字典,并可以设置转换的字段列表,达到减少内存损耗,提高性能values_list()
: 把结果集中的模型对象转换成列表,并可以设置转换的字段列表(元祖),达到减少内存损耗,提高性能
# values 把查询结果中模型对象转换成字典
student_list = student_list.order_by("-age")
ret1 = student_list.values() # 默认把所有字段全部转换并返回
ret2 = student_list.values("id","name","age") # 可以通过参数设置要转换的字段并返回
ret3 = student_list.values_list() # 默认把所有字段全部转换并返回
ret4 = student_list.values_list("id","name","age") # 可以通过参数设置要转换的字段并返回 - distinct()
从返回结果中剔除重复纪录。返回queryset。
# 查询所有学生出现过的年龄
print(Student.objects.values("age").distinct())
4.1.5 模糊查询
基于双下划线查询
- contains
例:查询姓名包含'华'的学生。tudent.objects.filter(name__contains='华')
- startswith、endswith
查询姓名以'文'结尾的学生
Student.objects.filter(name__endswith='文')
- isnull
查询个性签名不为空的学生
student_list = Student.objects.filter(description__isnull=True)
- in
查询编号为1或3或5的学生
Student.objects.filter(id__in=[1, 3, 5])
gt 大于 (greater then)
gte 大于等于 (greater then equal)
lt 小于 (less then)
lte 小于等于 (less then equal)
比较查询
查询编号大于3的学生
Student.objects.filter(id__gt=3)
- 日期查询
year、month、day、week_day、hour、minute、second:对日期时间类型的属性进行运算。
查询2010年被添加到数据中的学生。Student.objects.filter(born_date__year=1980)
例:查询2016年6月20日后,2017年6月21号之前添加的学生信息
from django.utils import timezone as datetime
Student.objects.filter(created_time__gte=datetime.datetime(2016,6,20),created_time__lt=datetime.datetime(2017,6,21)).all()
4.1.6 进阶查询
4.1.6.1 F查询
之前的查询都是对象的属性与常量值比较,两个属性怎么比较呢? 答:使用F对象,被定义在django.db.models中。
语法如下:
"""F对象:2个字段的值比较"""
# 获取从添加数据以后被改动过数据的学生
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)
6.1.6.2 Q 查询
多个过滤器逐个调用表示逻辑与关系,同sql语句中where部分的and关键字。
例:查询年龄大于20,并且编号小于30的学生
Student.objects.filter(age__gt=20,id__lt=30)
或
Student.filter(age__gt=20).filter(id__lt=30)
如果需要实现逻辑或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()
6.1.6.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函数的返回值是一个数字。
6.1.6.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之前,代表分组的字段,在annotate之后代表数据查询结果返回的字段
6.1.6.5 原生查询
执行原生SQL语句,也可以直接跳过模型,才通用原生pymysql.
ret = Student.objects.raw("SELECT id,name,age FROM db_student") # student 可以是任意一个模型
# 这样执行获取的结果无法通过QuerySet进行操作读取,只能循环提取
print(ret,type(ret))
for item in ret:
print(item,type(item))
4.2 关联查询
基于双下划线查询(join查询)
ret = Student.objects.filter(name="张三").values("age") # (1) 查询年龄大于22的学生的姓名以及所在名称班级 # 方式1 : Student作为基表
ret = Student.objects.filter(age__gt=22).values("name","clas__name")
# 方式2 :Clas表作为基表
ret = Clas.objects.filter(student_list__age__gt=22).values("student_list__name","name") # (2) 查询计算机科学与技术2班有哪些学生
ret = Clas.objects.filter(name="计算机科学与技术2班").values("student_list__name") # (3) 查询张三所报课程的名称
ret = Student.objects.filter(name="张三").values("courses__title")
print(ret) # <QuerySet [{'courses__title': '近代史'}, {'courses__title': '篮球'}]> # (4) 查询选修了近代史这门课程学生的姓名和年龄
ret = Course.objects.filter(title="近代史").values("students__name","students__age") # (5) 查询李四的手机号
ret = Student.objects.filter(name='李四').values("stu_detail__tel") # (6) 查询手机号是110的学生的姓名和所在班级名称
# 方式1
ret = StudentDetail.objects.filter(tel="110").values("stu__name","stu__clas__name")
print(ret) # <QuerySet [{'stu__name': '张三', 'stu__clas__name': '计算机科学与技术2班'}]>
# 方式2:
ret = Student.objects.filter(stu_detail__tel="110").values("name","clas__name")
print(ret) # <QuerySet [{'name': '张三', 'clas__name': '计算机科学与技术2班'}]>
4.3 关联添加
- 一对多与一对一
stu = Student.objects.create(name="王五", clas_id=9, stu_detail_id=6)
- 多对多
# 添加多对多方式1
c1 = Course.objects.get(title="思修")
c2 = Course.objects.get(title="逻辑学")
stu.courses.add(c1,c2) # 添加多对多方式2
stu = Student.objects.get(name="张三")
stu.courses.add(5,7) # 添加多对多方式3
stu = Student.objects.get(name="李四")
stu.courses.add(*[6,7])
4.4 关联删除
# 删除多对多记录
stu = Student.objects.get(name="李四")
stu.courses.remove(7) # 清空多对多记录:clear方法
stu = Student.objects.get(name="rain")
stu.courses.clear() #重置多对多记录:set方法
stu = Student.objects.get(name="李四")
stu.courses.set([5,8])
5.django-模型ORM的更多相关文章
- Django 模型ORM
from django.db import models # Create your models here. class Book(models.Model): nid = models.AutoF ...
- 057.Python前端Django模型ORM多表查询
一 基于对象的查询 1.1 一对多查询 设计路由 from django.contrib import admin from django.urls import path from app01 im ...
- 055.Python前端Django模型ORM
由于前面在centos实验的过程中,pymql一直有属性错误,很难排查出问题,重新做了一个ubuntu的桌面系统同时使用pycharm开发工具作为学习开发工具,具体原因是因为在项目命名出现问题,和自己 ...
- 056.Python前端Django模型ORM多表基本操作
一 准备工作 1.1 新建一个项目 root@darren-virtual-machine:~# cd /root/PycharmProjects/ root@darren-virtual-machi ...
- python的Web框架,Django的ORM,模型基础,MySQL连接配置及增删改查
Django中的ORM简介 ORM概念:对象关系映射(Object Relational Mapping,简称ORM): 用面向对象的方式描述数据库,去操作数据库,甚至可以达到不用编写SQL语句就能够 ...
- 如何在Django模型中管理并发性 orm select_for_update
如何在Django模型中管理并发性 为单用户服务的桌面系统的日子已经过去了 - 网络应用程序现在正在为数百万用户提供服务,许多用户出现了广泛的新问题 - 并发问题. 在本文中,我将介绍在Django模 ...
- Django 06 Django模型基础1(ORM简介、数据库连接配置、模型的创建与映射、数据的增删改查)
Django 06 Django模型基础1(ORM简介.数据库连接配置.模型的创建与映射.数据的增删改查) 一.ORM系统 #django模型映射关系 #模型类-----数据表 #类属性-----表字 ...
- Django模型系统——ORM
一.概论 1.ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术. 简单的说,ORM是通过使用描 ...
- Django模型层之ORM
Django模型层之ORM操作 一 ORM简介 我们在使用Django框架开发web应用的过程中,不可避免地会涉及到数据的管理操作(如增.删.改.查),而一旦谈到数据的管理操作,就需要用到数据库管理软 ...
- django 模型层(orm)05
目录 配置测试脚本 django ORM基本操作 增删改查 Django 终端打印SQL语句 13条基本查询操作 双下滑线查询 表查询 建表 一对多字段数据的增删改查 多对多字段数据的增删改查 基于对 ...
随机推荐
- Html飞机大战(六):移动飞机
好家伙,这篇移动主角 我们先来看看一个好东西, addEventListener() 方法 (他真的很好用) 我们直译一下,就叫他添加事件监听器方法 而可监听的对象就有很多啦 我们来了解一 ...
- yield功能分析
下面是示例 输出结果: starting... 4 ******************** res: None 4
- Vben Admin 源码学习:状态管理-角色权限
前言 本文将对 Vue-Vben-Admin 角色权限的状态管理进行源码解读,耐心读完,相信您一定会有所收获! 更多系列文章详见专栏 Vben Admin 项目分析&实践 . 本文涉及到角 ...
- Java开发学习(三十一)----Maven属性与版本管理
一.属性 1.1 问题分析 如下图所示 你会发现,如果现在想更新Spring的版本,你会发现依然需要更新多个jar包的版本,这样的话还是有可能出现漏改导致程序出问题,而且改起来也是比较麻烦. 问题清楚 ...
- .Net之接口小知识
目的 通过一个简单的项目,在原来的文章基础上完善一下常用的几种WebApi编写方式以及请求方式,一方面是用于给我一个前端朋友用来学习调用接口,另一方面让我测试HttpClient的一些效果. 本文示例 ...
- 升级Windows 2003域控制器到Windows 2012 R2
由于Windows 2003包括R2的扩展支持在今年7月14日就会过期.如果在扩展周期结束之前没有和微软签订昂贵服务协议,那么系统将得不到任何补丁和技术支持. 我这里准备了两台测试用的机器做这个实验. ...
- Job for redis-server.service failed because the control process exited with error code(Centos 7 设置Redis开机自启报错)
报错信息如下: Job for redis-server.service failed because the control process exited with error code. See ...
- 使用KVM的命令行方式安装centos7虚拟机
前提条件 1.宿主机上已经安装KVM软件,参考网址:https://www.cnblogs.com/sanduzxcvbnm/p/15538881.html 2.已经上传centos7镜像到宿主机里 ...
- Elasticsearch:Elasticsearch SQL介绍及实例 (一)
转载自:https://blog.csdn.net/UbuntuTouch/article/details/105658911
- SpringBoot入门项目CRM学习过程中的报错记录(更新ing)
在用mybatis自动生成实体类和mapper时报错..... is unrecognized or represents more than one time zone. You must conf ...