在一个 Web 框架中,前端负责提交和接收数据,展示页面,后端负责数据的处理与存储。
常见的数据库有 PostgreSQL、Mariadb、MySQL、Oracle、Sqlite3等,Web 开发中对数据库的操作是必不可少的,然而数据库种类多种多样,每种数据库的操作方式以及用法不尽相同,常见的数据库操作,就是去编写SQL语句。
在 Web 开发中,通常不需要去编写SQL语句去操作数据库,例如用SQL查出的数据,还需要对数据类型进行转化,并对数据进行一定的处理,这大大降低了开发这的工作效率。 Web 框架提供了丰富的模块,避免程序员在开发的过程中重复“造轮子”。
ORM (Object Realtional Mapping)即对象关系映射,是一种基于关系型数据库的程序技术,这种程序技术的底层主要是通过映射机制实现的。ORM 把类映射成数据库中的表,把类的一个实例对象映射成数据库中的数据行,把类的属性映射成表中的字段,通过对象的操作对应到数据库表的操作,实现了对象到 SQL、SQL 到对象转换过程。
ORM 使用类和对象对数据库进行操作,大大提高了对数据库的控制,避免了直接使用 SQL 语句对数据库进行操作。
ORM 适配多种常用的关系型数据库,例如 PostgreSQL、Mariadb、MySQL、Oracle、Sqlite3 等,由于 Django 中 ORM 的存在,为我们操作不同种类的数据库提供了统一的方法。
Django 把表模型定义为 Model,他需要继承自 django.db.models
中的 Model 类,只要是与数据表相关的操作,都需要继承这个类。
可以这样理解,Django 中模型类就相当于 ORM 模块。模型类本质上属于一个 Python 类,只不过在 Django 中称之为做模型类 ,它是由 django.db.models.Model 派生出的子类,在 Django 中模型类是数据交互的接口,一个模型类代表数据库中的一张数据表,模型类中每一个类属性都代表数据表中的一个字段。
针对数据库中的字段类型,Django ORM 都有对应的 “xxxField” 来表述:
字段 | 说明 | 字段属性 |
---|---|---|
AutoFiled | 默然自增主键(Primary_key=Ture),Django 默认建立id字段为主键。 | |
CharFiled | 字符类型 | 字符长度需要明确,Max_length=,最大长度256 |
IntgerFiled | 整型 int | |
DateFiled | 年月日时间类型 | auto_now=True,数据被更新就会更新时间 ;auto_now_add=True,数据第一次参数时产生。 |
DateTimeFiled | 年月日小时分钟秒时间类型 | auto_now=True,数据被更新就会更新时间; auto_now_add=True,数据第一次参数时产生。 |
DecimalFiled | 混合精度的小数类型 | max_digits=3,限定数字的最大位数(包含小数位);decimal_places=2,限制小数的最大位数。 |
BooleanFiled | 布尔字段,对应数据库 tinyint 类型数据长度只有1位。 | 值为True或False,默认值是None |
TextFiled | 用于大文本 |
Model 中添加的字段都是 Field 类型的实例,不同的 Field 类型可能会支持不同的字段选项,但是也有很多字段选项是通用的,即可以用在任何一种 Field 类型中。
blank: 默认值是 False,设置为 True 时,字段可以为空。设置为 False 时,字段是必须填写的。如果是字符型字段 CharField 和 TextField,它们是用空字符串来存储空值的。
null: 默认为 False,如果此选项为 False 建议加入 default 选项来设置默认值;如果设置为 True,表示该列值允许为空。日期型、时间型以及数字型字段不接受空字符串。所以当设置 IntegerField,DateTimeField 型字段可以为空时,需要将 blank 与 null 均设为 True 才可以。
default: 用于给字段设置默认值。可以是一个值或者是个可调用的对象,不能是一个可更改的对象(模型实例、list、set 等)。比如 BooleanFiled 布尔类型 default 值为Ture 或者 False。主要的使用场景是当一个字段的值被用户省略时,后台服务器自动为该字段的设置默认值。
unique: 默认值是 False,它是一个数据库级别的选项,规定该字段在表中必须是唯一的。如果设置为 True,这个字段必须在整个表中保持值唯一。数据库层面对待对待唯一性约束会创建唯一性索引,所以,如果一个字段设置了 unique=True,就不需要对这个字段加上索引选项了。
db_index: 默认值是 False,如果设置为 True,Django 则会为该字段创建数据库索引,如果该字段经常作为查询的条件,那么就需要设置 db_index 选项,从而加快数据的检索速度。
db_column: 此字段用于设置数据库表字段的名称。如果没有指定,Django 默认使用 Model 中字段的名字。
primary_key: 默认值是 False,如果设置为 True,表示该字段为主键,在 Django 中 默认 id 为主键,也就是说即使你的数据表中没有创建 id 字段,Django 也会自动为你创建 id 字段并将其设置为主键。如果你在表中设置了其他字段为主键的时,那么 Django 将取消为 id 字段设置主键。
verbose_name: 设置此字段在 admin 后台管理系统界面上的显示名称,如果没有给定详细名称,Django 会使用字段的属性名自动创建,并将下划线转换为空格。
choices: 用于给字段设置可以选择的值。它是一个可迭代对象,即列表或者元组,其中每一个元素都是一个二元组(a,b)的形式,a 是用来选择的对象,b 是对 a 的描述信息。比如我们对某个人性别定义:
class UserInfo(models.Model):
# 定义chocies参数的对应关系,以元组(或者列表)的形式进行表述:
choices = (
(male, '男性'),
(female, '女性'),
)
sex = models.CharField(max_length=2,choices = choices,default='male')
在执行python manage.py startapp
命令创建应用的时候,在应用目录下,就生成了一个 models.py 文件,通过这个文件,我们去创建模型,定义数据表
from django.db import models
# Create your models here.
# 测试交流会模型
class Event(models.Model): # 继承django自带的模型基类
# 交流会主题
name = models.CharField(max_length=256,null=False)
# 交流会地点
address = models.CharField(max_length=256)
# 交流会开始时间
start_time = models.DateTimeField(null=True)
# 交流会状态
status = models.BooleanField(default=True)
# 交流会限制人数
limits = models.IntegerField(default=200)
在进行迁移前,需要确定setting文件中应用配置和数据库配置。
确保INSTALLED_APPS
中应已注册该应用
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'demo',
'sgin', # 添加应用
]
如果是连接的mysql(mariadb),要注意数据库信息的配置,否则会迁移不成功
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', #数据库引擎
'NAME': 'DjangoSite', #数据库名称
'USER': 'bobo', #用户名
'PASSWORD': '123456', #密码
'HOST': '192.168.134.132', #数据库IP
'PORT': '3306', #数据库端口
}
}
在终端执行命令python manage.py makemigrations
生成迁移文件,这个文件可以在应用目录下的目录下找到
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Event',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=256)),
('address', models.CharField(max_length=256)),
('start_time', models.DateTimeField(null=True)),
('status', models.BooleanField(default=True)),
('limits', models.IntegerField(default=200)),
],
),
]
执行命令python manage.py migrate
应用数据库迁移,在数据库中生成数据表
可以发现,数据库中生成了一个名为sgin_event
的数据表,该表是以应用名+模型名
命名的。除了这张表,还生成了很多其他表,这些表是因为在 setting.py文件中,INSTALLED_APPS
中存在Django自带的应用,这些应用也会生成数据表
拓展: django报错Did you install mysqlclient?
django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.
Did you install mysqlclient?
若出现如上方类似的报错提示,这个报错的意思是没有安装mysqlclient
,如果能安装mysqlclient
,可以安装,Windows下可能安装失败,推荐安装PyMySQL
pip install PyMySQL
修改项目目录下的__init__.py
(与settings.py同一个目录)
添加以下信息:
import pymysql
pymysql.install_as_MySQLdb()
生成了数据表后,数据表的内容是空的,需要插入数据,可以通过 ORM 来操作数据,使用类名(即数据表名)来操作数据
打开pycharm的Terminal
终端,输入命令python manage.py shell
打开Django自带命令行,输入指令from sgin.models import Event
导入sgin应用下的Event模型类
输入指令Event.objects.all()
查询所有数据。该命令就相当于SQL:select * from sgin_event
。返回的是QuerySet对象,这里因为表是空的,所以没有数据,查出来的是一个空列表。
这里objects
指向的是一个模型管理器(Manager),模型管理器的作用是管理当前数据模型对应表的增删改查操作。Django提供的模型管理器,通过模型类的objects属性调用,只能通过类调用,不能通过实例调用,如Event.objects
而不是Event().objects
ORM 的增删改查称为 CURD 操作
语法:Model.objects.create(**kws)
使用create
方法插入数据,也就是增删改查操作中的 C
,在pycharm的Terminal
终端,输入指令
Event.objects.create(name='安全测试交流会',address='直播间888号房')
Event.objects.create(name='性能测试交流会',address='直播间666号房')
语法:
Model.objects.all()
输入指令Event.objects.all()
查询所有数据。该命令就相当于SQL:select * from sgin_event
。返回的是QuerySet对象
<QuerySet [<Event: Event object (2)>]>
语法:
Model.objects.filter(**kw)
Model.objects.values(**kw)
输入指令 Event.objects.filter(name='安全测试交流会')
查询数据,返回的是QuerySet对象。
输入指令 Event.objects.filter(name='安全测试交流会').values()
查询数据,返回QuerySet对象的是以键值对的形式展示,可以使用list函数将其转变成列表。
>>> Event.objects.filter(name='安全测试交流会')
]>
>>> Event.objects.filter(name='安全测试交流会').values()
>>> list(Event.objects.filter(name='安全测试交流会').values())
[{'id': 1, 'name': '安全测试交流会', 'address': '直播间888号房', 'start_time': None, 'status': True, 'limits': 200}]
该命令就相当于SQL:select * from sgin_event where name='安全测试交流会'
。
语法:
Model.objects.get(**kw)
输入指令 Event.objects.all().get(name='安全测试交流会')
查询数据,返回单个模型数据对象,如果结果超出或者少于1个的时候,会抛出异常
<Event: Event object (1)>
该命令就相当于SQL:select * from sgin_event where name='安全测试交流会' limits=1
。
可以发现QuerySet对象与get返回的单个模型数据对象,区别在于QuerySet对象多了一层[]
,单个模型数据对象相当于QuerySet对象列表里的单个数据。
对于以上查询,返回的QuerySet对象不利于查看数据,这里可以对模型添加魔术方法。
__str__
方法是 Python 中的 "魔术” 方法,它是为 print 这样的打印函数设计的,它属于 python 的 object 基类的一个方法,也就是说 python 所有的类都有该方法,当然 Django 的 modle 类也有。如果没有这个方法定义,打印对象会显示对象的内存地址,但是这样的显示方式不够友好,且不利于调试,而用__str__
方法后,可以在 print 时得到易于人阅读的信息
在 models.py 文件中添加__str__
魔术方法
from django.db import models
# Create your models here.
# 测试交流会模型
class Event(models.Model): # 继承django自带的模型基类
# 交流会主题
name = models.CharField(max_length=256,null=False)
# 交流会地点
address = models.CharField(max_length=256)
# 交流会开始时间
start_time = models.DateTimeField(null=True)
# 交流会状态
status = models.BooleanField(default=True)
# 交流会限制人数
limits = models.IntegerField(default=200)
# 覆盖对象对外的字符串表现形式
def __str__(self):
return self.name
添加__str__
魔术方法并不会影响model中的表结构,所以不需要重新生成迁移文件,也不需要迁移到数据库表这一操作。
输入exit()
退出shell后,输入命令python manage.py shell
重新激活一下,输入指令from sgin.models import Event
导入sgin应用下的Event模型类,此时再输入指令Event.objects.all()
查询所有数据
<QuerySet [<Event: 安全测试交流会>, <Event: 性能测试交流会>]>
这样查看数据,可能会稍微好看一点
修改数据首先要拿到数据对象,使用一个变量event_obj
去接收查询的单个模型数据对象
event_obj = Event.objects.all().get(name='性能测试交流会')
然后通过对属性进行赋值的方式,进行修改
event_obj.address = '直播间1111号房'
修改只是将修改的内容保存到了内存中,并没有同步到数据库,最后还需要通过save方法,将修改保存到数据库
event_obj.save()
这时候去查看数据表中的内容,可以发现性能测试交流会
这条数据的 address
由直播间666号房
变为直播间1111号房
如果变量接收的是一个QuerySet对象,可以通过获取其列表里的第一个元素,再进行操作
event_obj2 = Event.objects.filter(name='安全测试交流会').first()
event_obj2.address = '直播间2222号房'
event_obj2.save()
使用一个变量event_obj3
去接收查询的QuerySet对象
event_obj3 = Event.objects.filter(name='性能测试交流会')
然后通过update方法进行修改
event_obj3.update(name='测试开发交流会')
这种方式相对于save方法,稍微没有那么麻烦,update后就直接更新到数据库了
删除的内容,是一条语句,对于Django来说,就是删除单个模型数据对象
event_obj4 = Event.objects.all().get(name='测试开发交流会')
event_obj4.delete()
删除成功后,数据表中就没有测试开发交流会
这条数据了
通过ORM增删改查操作,与数据库进行实时交互,那么,如果要将数据库中的数据展示到前端页面,该如何处理?
from django.shortcuts import render
def event(request):
eventlist = [
'功能测试',
'性能测试',
'自动化测试'
]
return render(request,'events.html',{'event_list':eventlist})
之前返回给前端的数据,是在视图函数里写死了,这里需要修改成从数据库里取数据。
from django.shortcuts import render
from sgin.models import Event # 导入sgin应用下的Event模型类
# Create your views here.
def event(request):
eventlist = Event.objects.all() # 通过模型管理器获取数据
return render(request,'events.html',{'event_list':eventlist})
通过Event.objects.all()
查询的内容,是一个QuerySet对象,其本质是一个可迭代对象,前端页面有一个 for 循环读取处理,在 models.py 文件中添加了__str__
魔术方法,数据对外展示的时候,以name
的方式进行展示,所以到了前端,展示的内容就是数据表中 name
的数据
启动项目,在浏览器中输入http://127.0.0.1:8000/events/
,可以发现,这块内容变成了从数据库中获取的数据了
ORM 模块确实有诸多的优势,比如:
与此同时 ORM 也存在一点不足之处:
相比直接用 SQL 语句操作数据库会有性能损失,因为在映射的过程中 ORM 需要与 SQL 之间进行转换,根据对象的操作转换成 SQL 语句,根据查询结果转换成对象,所以在映射的过程存在性能损失。
但是 ORM 的不足带来的这点性能损失是微不足道的,ORM 的优势还是非常突出的,这种对象模型和关系型数据库之间的转换方式,给开发者带来了极大的便捷。