ORM模型介绍
随着项目越来越大,采用写原生SQL的方式在代码中会出现大量的SQL语句,那么问题就出现了:
- SQL语句重复利用率不高,越复杂的SQL语句条件越多,代码越长。会出现很多相近的SQL语句。
- 很多SQL语句是在业务逻辑中拼出来的,如果有数据库需要更改,就要去修改这些逻辑,这会很容易漏掉对某些SQL语句的修改。
- 写SQL时容易忽略web安全问题,给未来造成隐患。 SQL注入。ORM ,全称 Object Relational Mapping ,中文叫做对象关系映射,通过 ORM 我们可以通过类的方式去操作数据库,而不用再写原生的SQL语句。通过把表映射成类,把行作实例,把字段作为属性, ORM 在执行对象操作的时候最终还是会把对应的操作转换为数据库原生语句。使用 ORM 有许多优点:
- 易用性:使用 ORM 做数据库的开发可以有效的减少重复SQL语句的概率,写出来的模型也更加直观、清晰。
- 性能损耗小: ORM 转换成底层数据库操作指令确实会有一些开销。但从实际的情况来看,这种性能损耗很少(不足5%),只要不是对性能有严苛的要求,综合考虑开发效率、代码的阅读性,带来的好处要远远大于性能损耗,而且项目越大作用越明显。
- 设计灵活:可以轻松的写出复杂的查询。
- 可移植性: Django 封装了底层的数据库实现,支持多个关系数据库引擎,包括流行的 MySQL 、PostgreSQL 和 SQLite 。可以非常轻松的切换数据库。
附:
# 创建数据库test_db
create database test_db charset utf8;
# 切换到test_db数据库
use test_db;
/*
创建用户并赋权
1. root: 所有权限,只能localhost连接
# GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION;
2. onefine: 有imooc数据库的所有权限,可以通过任意IP地址连接
*/
CREATE USER 'onefine'@'%' IDENTIFIED BY 'test_db';
GRANT ALL PRIVILEGES ON test_db.* TO 'onefine'@'%';
FLUSH PRIVILEGES;
select @@version;
创建ORM模型:
ORM 模型一般都是放在 app 的 models.py 文件中。每个 app 都可以拥有自己的模型。并且如果这个模型想要映射到数据库中,那么这个 app 必须要放在 settings.py 的 INSTALLED_APP 中进行安装。以下是写一个简单的书籍 ORM 模型。示例代码如下:
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=20,null=False)
author = models.CharField(max_length=20,null=False)
pub_time = models.DateTimeField(default=datetime.now)
price = models.FloatField(default=0)
以上便定义了一个模型。这个模型继承自 django.db.models.Model ,如果这个模型想要映射到数据库中,就必须继承自这个类。这个模型以后映射到数据库中,表名是模型名称的小写形式,为 book 。在这个表中,有四个字段,一个为 name ,这个字段是保存的是书的名称,是 varchar 类型,最长不能超过20个字符,并且不能为空。第二个字段是作者名字类型,同样也是 varchar 类型,长度不能超过20个。第三个是出版时间,数据类型是 datetime 类型,默认是保存这本书籍的时间。第五个是这本书的价格,是浮点类型。
还有一个字段我们没有写,就是主键 id ,==在 django 中,如果一个模型没有定义主键,那么将会自动生成一个自动增长的 int 类型的主键,并且这个主键的名字就叫做 id ==。
映射模型到数据库中:
将 ORM 模型映射到数据库中,总结起来就是以下几步:
8. 在 settings.py 中,配置好 DATABASES ,做好数据库相关的配置。
9. 在 app 中的 models.py 中定义好模型,这个模型必须继承自 django.db.models 。
10. 将这个 app 添加到 settings.py 的 INSTALLED_APP 中。
11. 在命令行终端,进入到项目所在的路径,然后执行命令 python manage.py makemigrations
来生成迁移脚本文件。
12. 同样在命令行中,执行命令 python manage.py migrate
来将迁移脚本文件映射到数据库中。
字段类型和参数
单个模型的常用的字段:
from django.db import models
class Test(models.Model): # courses_test
"""测试学习用"""
# 自增长字段,数据表中的记录每增加一条,字段值会自动加1,默认是int型
Auto = models.AutoField() #
BigAuto = models.BigAutoField() # 数值会更大一些
# 二进制数据
Binary = models.BinaryField()
# 布尔型
Boolean = models.BooleanField() # 不允许为空
NullBoolean = models.NullBooleanField() # 允许为空的bool型
# 整型,带Positive的为存储正整数
PositiveSmallInteger = models.PositiveSmallIntegerField(db_column="age") # 5个字节的正整数
SmallInteger = models.SmallIntegerField(primary_key=False) # 6个字节的整数
PositiveInteger = models.PositiveIntegerField() # 10个字节的正整数
Integer = models.IntegerField(verbose_name="11个字节大小") # 11个字节整数
BigInteger = models.BigIntegerField(unique=True) # 20个字节整数
# 字符串类型
Char = models.CharField(max_length=100, null=True, blank=True, db_index=True) # varchar
Text = models.TextField(help_text="这个是longtext") # longtext,不需要指定长度
# 时间日期类型
Date = models.DateField(unique_for_date=True, auto_now=True) # 表示年月日
DateTime = models.DateTimeField(editable=False, unique_for_month=True, auto_now_add=True) # 表示年月日时分秒
Duration = models.DurationField() # 表示一段时间,数据表中是int, 底层通过Python timedelta实现
# 浮点型
Float = models.FloatField() #
Decimal = models.DecimalField(max_digits=4, decimal_places=2) # 11.22, 16.34 # 指定整数和小数的位数
# 其它字段
Email = models.EmailField() # 邮箱
Image = models.ImageField() # 图片
File = models.FileField() # 文件
FilePath = models.FilePathField() # 文件路径
URL = models.URLField() # 浏览器中的url地址
UUID = models.UUIDField() # uuid
GenericIPAddress = models.GenericIPAddressField() # ip地址,可以使IpV4/IpV6
多个模型的关系型字段
ORM(Object-Relational Mapping)
多个模型的关系型字段
- 一对一(OneToOneField)
- 多对一(ForeignKey)
- 多对多(ManyToManyField),默认或自定义实现中间表
# A与Test类是一对一关系
class A(models.Model):
onetoone = models.OneToOneField(Test)
# B与A实现一对多的关系
class B(models.Model):
foreign = models.ForeignKey(A)
# C与B实现多对多的关系
class C(models.Model):
manytomany = models.ManyToManyField(B)
字段参数
-
所有字段都有的参数
默认情况下数据库表名:appname_classname如app:courses,class:Test,生成的表名: courses_test
db_column 数据表生成之后的字段名
primary_key=True 设置主键,默认情况下是False
verbose_name=‘str’ 设置字段的别名/备注
unique=True 字段的唯一键属性
null=True, blank=True 设置字段允许为空,默认为False;null指的是数据库是否为空,blank 指的是前端表单提交是否为空
db_index=True 表示给字段设置索引
help_text 用于在表单中显示帮助信息的字段参数,如在表单中显示说明:help_text=“这个是longtext”
editable=False 设置字段不允许用户进行编辑,默认情况下所有字段在admin后台和form表单中都是可以更改的 -
个别字段才有的参数
CharField必须指明最大长度:max_length=100,指定最大长度为100,这里指的是utf8编码的长度为100的字符串
时间日期类型(DataField、DataTimeField、DurationField)才有的字段参数:unique_for_date=True 表示字段的日期必须唯一
unique_for_month=True 设置月份必须唯一
auto_now=True表示更新当前进入时的时间,更新
auto_now_add=True表示添加进入时的当前时间,插入
浮点型中的DecimalField必须设置两个参数:max_digits=4表示总共有4为数,decimal_places=2表示小数点有2位;比如11.22,16.14。。。
- 关系型字段具有的参数
related_name=“one” 用于外键关联中的方向查询——通过父表查询到字表的信息。
比如:
class A(models.Model):
onetoone = models.OneToOneField(Test, related_name="one")
现在字表是A,父表示Test,现在需要通过Test查询到模型A中相关的数据,可以使用到related_name=“one”。
on_delete= 表示当外键所关联的对象被删除的时候要进行什么操作。
on_delete 当一个被外键关联的对象被删除时,Django将模仿on_delete参数定义的SQL约束执行相应操作
如下6种操作
CASCADE:模拟SQL语言中的ON DELETE CASCADE约束,将定义有外键的模型对象同时删除!(该操作为当前Django版本的默认操作!)
PROTECT:阻止上面的删除操作,但是弹出ProtectedError异常
SET_NULL:将外键字段设为null,只有当字段设置了null=True时,方可使用该值。
SET_DEFAULT:将外键字段设为默认值。只有当字段设置了default参数时,方可使用。
DO_NOTHING:什么也不做。
SET():设置为一个传递给SET()的值或者一个回调函数的返回值。注意大小写。
比如:
class B(models.Model):
foreign = models.ForeignKey(A, on_delete=models.CASCADE)
on_delete=models.CASCADE 表示删除级联
A表中的一些记录被删除之后,B表中关联到A表的那些记录也会被对应删除
on_delete=models.PROTECT 表示被关联的记录被删除的时候就会报ProtectedError异常
on_delete=models.SET_NULL 删除置空,表示当父表中的记录被删除的时候,字表对应的外键的字段的值被设置为null,使用此参数的时候务必指定
null=True,blank=True
on_delete=models.SET_DEFAULT 当父表中的数据被删除的时候,给子表对应的外键字段设置一个默认值,使用使用此参数务必指定default=0,default后面跟默认值
on_delete=models.DO_NOTHING 父表中的数据被删除了,字表什么也不做,原来是什么样,现在就是什么样
on_delete=models.SET 传递一个SET值或回调函数的返回值
自关联
让一个模型类关联到自己——同一张数据表中一条记录关联到另外一条记录。
class AddressInfo(models.Model): # coures_addressinfo
"""省市县地址信息"""
address = models.CharField(max_length=200, null=True, blank=True, verbose_name="地址")
pid = models.ForeignKey('self', null=True, blank=True, verbose_name="自关联")
# 自关联的另外一种写法,写自己模型类的名称
# pid = models.ForeignKey('AddressInfo', null=True, blank=True, verbose_name="自关联")
note = models.CharField(max_length=200, null=True, blank=True, verbose_name="说明")
# 给不同的模型类对象返回一个可读的字符串,这里返回address信息
def __str__(self): # python2.x中: __unicode__(self)
return self.address