一 说明

django中内置了一个基于面向对象的ORM框架,用来对数据库进行操作。我们不需要直接编写SQL语句进行数据库操作,可以通过定义模型类,操作模型类来完成对数据库中数据的增删改查和数据表的创建删除等操作ORM框架还会帮我们把类对象和数据表进行了一对一的映射,让我们可以通过类对象来操作对应的数据表还可以根据我们设计的类自动帮我们生成数据库中的数据表,也就是使用代码生成数据库建表语句,省去了我们自己建表的过程。

 

O:Object,也就类对象的意思。

R:Relational,关系,也就是关系型数据库中数据表的意思。

M:mapping,映射的意思,表示类对象和数据表之间的映射关系。

 

二 配置数据库连接

1 安装mysql数据库的驱动程序

pip install PyMySQL

# 如果上面命令安装失败,则可以使用以下命令安装:
conda install -c conda-forge pymysql

  

 2 在Django的主应用目录的__init__.py文件中添加如下语句

from pymysql import install_as_MySQLdb

install_as_MySQLdb()  # 作用是让Django的ORM能以mysqldb的方式来调用PyMySQL。

  

3 在Django的主应用目录的settings.py文件修改DATABASES配置信息,来匹配数据库的连接信息。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',  # ORM的底层对接pymysql的核心引擎类
        'NAME': 'school',          # 数据库名
        'PORT': 3306,              # 端口
        'HOST': '127.0.0.1',       # 数据库IP
        'USER': 'root',            # 账号
        'PASSWORD': '123',         # 密码
        'POOL_OPTIONS': {  # pool表示数据库连接池配置,主要为了节省连接数据库的开销,临时存储数据库连接对象
            'POOL_SIZE': 10,     # 默认情况下,打开的数据库连接对象的数量 [1,2,3,4,5,6,7,8,9,10]
            'MAX_OVERFLOW': 30,  # 负载情况下,允许溢出的连接数量  [11,12,13,14,15,16,17,18,19,20]
        }
    }
}

  

4 在mysql中创建数据库,手动添加测试数据

create database school;

  

5 创建子应用student,注册子应用并引入子应用路由

python manage.py startapp student

 

6 在Django的主应用目录的settings.py文件注册子应用

INSTALLED_APPS = [
    # ...添加子应用
    'student',
]

 

7 在Django的主应用目录的urls.py总路由文件添加路由器

urlpatterns = [
    # ...添加子路由
    path("student/", include("student.urls")),
]

  

8 在子路由student/urls.py文件添加子路由信息

from django.urls import path
from . import views
urlpatterns = [
    # Student1为view.py文件中定义的类
    path("student1/", views.Student1.as_view()),
]

 

9 在student/models.py 文件中定义模型类

from django.db import models

# 模型类必须要直接或者间接继承于 models.Model
class Student(models.Model):
    # 对数据库的操作     
    pass

  

10 在student/views.py 视图文件中调用模型获取数据表中的数据

from django.http import HttpResponse
from django.views import View
from .models import Student

class Student1(View):
    def get(self,request):
        pass
    def put(self,request):
        pass

 

三 定义模型类

操作对象:student/models.py

1 数据库表名

通过Meta.db_table 指明数据库表名。模型类如果未指明表名Meta.db_table,Django默认以 小写子应用目录名_小写模型类名 为数据库表名。

2 主键

django会为模型自动声明一个自动增长的主键列,每个模型只能有一个主键列。如果使用选项设置某个字段的约束属性为主键列(primary_key)后,django不会再创建自动增长的主键列。默认创建的主键列属性名为id,也可以使用pk代替id,pk为primary key

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

3 属性命名规范

属性名 = models.字段类型(约束选项, verbose_name="注释")

# 在mysql数据表中的字段名如果在python是一个关键字/保留字。则选项中需要通过db_column()来进行关联绑定
class_name = models.SmallIntegerField(db_column="class", verbose_name="班级")
# class就是实际上在mysql数据表中的真实字段名
# class_name 就是python中将来用于操作数据的属性名 
  • 不能是python的关键字或保留字。

  • 不允许使用连续的2个下划线,这是因为django的查询方式提供了很多以__xx开头的方法或属性,所以__xx是django的模型内部的关键字,不能使用!

  • 定义属性时需要指定字段类型

4 字段类型

类型说明
AutoField 自动增长的IntegerField,通常不用指定,不指定时Django会自动创建属性名为id的自动增长属性,并且提供了一个pk属性与主键进行关联。django3.0以后,系统默认采用了BigAutoField,从原来的int类型变成了bigint类型,其他不变。
BooleanField 布尔字段,值为True或False,实际上在数据库中存储的是smallint,采用0/1表示False/True
NullBooleanField 支持None、True、False三种值
CharField 字符串,参数max_length表示最大字符个数,对应mysql中的varchar
TextField 大文本字段,一般大段文本(超过4000个字符)才使用,对应mysql中的bigtext。
IntegerField 整数,对应mysql中的int
BigIntegerField 大整型,对应mysql的bitint
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 继承于CharField,上传文件字段类型,但是django在FileField中内置了文件上传保存类, django可以通过模型的字段存储自动保存上传文件, 但是在数据库中本质上保存的仅仅是文件在项目中的存储路径!! 参数upload_to,表示设置当前上传文件的存储路径位置,当前路径如果不存在,django会自动生成路径。
ImageField 继承于FileField,对上传的内容进行校验,确保是有效的图片
EmailField 继承于CharField,邮件字段类型,但是增加字段值的验证是否为邮件格式
UUIDField 继承于CharField,UUID字段类型,保存内容格式字符串,但是会验证当前字段是否是UUID格式。

5 约束选项


选项
说明
null 如果为True,表示允许为空,默认值是False。相当于python的None。null是数据库范畴的概念
blank 如果为True,则该字段允许为空白,默认值是False。 相当于python的空字符串。blank是表单验证范畴的
db_column 数据表中真实的字段名称,如果未指定,则使用模型类属性的名称。
db_index 若值为True, 则在表中会为此字段创建索引,默认值是False。 相当于SQL语句中的key
default 默认值,当不填写数据时,使用该选项的值作为字段的默认值。
primary_key 如果为True,则该字段在表欧中设置为主键,默认值是False,一般不用设置,系统默认设置。
unique 如果为True,则该字段在表中创建唯一索引,默认值是False。相当于SQL语句中的unique
choices 设置当前字段值的候选值选项,必须是二维序列,子序列必须是2个成员。
help_text 设置当前字段的辅助提示文本信息,是一个人类可读的文本信息。
verbose_name 设置当前字段的提示字段名,是一个人类可读的名称。

6 外键约束

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

CASCADE 级联 删除主表数据时连通一起删除外键表中数据
PROTECT 保护 通过抛出ProtectedError异常,来阻止删除主表中被外键应用的数据
SET_NULL 设置为NULL,仅在该字段null=True允许为null时可用
SET_DEFAULT 设置为默认值,仅在该字段设置了默认值时可用
SET() 设置为特定值或者调用特定方法
DO_NOTHING 不做任何操作,如果数据库前置指明级联性,此选项会抛出IntegrityError异常

7 示例

示例1:

代码:

from django.db import models

class GoodsCategory(models.Model):
    name = models.CharField(max_length=50, verbose_name="分类名称")
    class Meta:
        db_table = "tb_goods_category"  # 设置当前Student模型对象对应的数据库的表名, 如果没有指定表名,则默认为子应用目录名_模型名称
        verbose_name = "商品分类"  # 在admin站点中显示的名称
        verbose_name_plural = verbose_name  # # 显示的复数名称


class GoodsInfo(models.Model):
    name = models.CharField(max_length=255, verbose_name="商品名称")
    category = models.ForeignKey(GoodsCategory, on_delete=models.DO_NOTHING, verbose_name="商品分类")  # category_id

    class Meta:
        db_table = "tb_goods_info"
        verbose_name = "商品信息"
        verbose_name_plural = verbose_name

'''
1. 当商品信息表模型的外键字段的on_delete=CASCADE, 删除蔬菜(id=1),则在外键category_id=1的商品id1和3就被删除。
2. 当商品信息表模型的外键字段的on_delete=PROTECT,删除蔬菜(id=1),mysql自动检查商品信息表,有没有category_id=1的记录,有则提示必须先移除掉商品信息表中cid=1的所有记录以后才能删除商品分类中的蔬菜。
3. 当商品信息表模型的外键字段的on_delete=SET_NULL,删除蔬菜(id=1),对应商品信息表category_id=1的数据全部被改成category_id=null
4. 当商品信息表模型的外键字段的on_delete=SET_DEFAULT,删除蔬菜(id=1),对应商品信息表,category_id=1的数据记录被设置category_id=默认值。
5. 当商品信息表模型的外键字段的on_delete=DO_NOTHING,删除蔬菜(id=1),对应商品信息表所有数据不管有没有category_id=1的数据,都不会发生变化。
'''
外键约束

 

示例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
student/models.py:对学生信息表的操作

 

from django.http import HttpResponse
from django.views import View
from .models import Student

# Create your views here.


class Student1View(View):
    def get(self,request):
        """获取所有学生数据"""
        # # 模型类名.objects.all()  # 获取模型对应的数据表的模型类对象
        # student_list = Student.objects.all()
        # print(student_list)

        # # all() 方法的返回值是一个伪列表对象QuerySet,提供了列表操作给我们使用
        # print(student_list[0], type(student_list[0]))

        # # st 单个对象,对应的是数据表中的一行记录!!!
        # st = student_list[0]
        # print(st.name)
        # print(st.age)
        # print(st.sex)
        # print(st.get_sex_display()) # 获取choices的文本提示,必须以 模型对象.get_<字段名>_display()来获取

        # values() 是QuerySet的方法,可以把返回的模型类对象列表转换成字典值的格式
        student_list = Student.objects.all().values()
        # print(student_list)

        st = student_list[0]
        print(st)
        print(st["id"])
        print(st["name"])
        print(st["age"])
        print(st["sex"], Student.SEX_CHOICES[st["sex"]][1])
        return HttpResponse("OK")
student/view.py:调用模型获取数据表中的数据

通过浏览器直接访问视图对应的URL,http://127.0.0.1:8000/student/student1/

四 数据迁移

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

1 生成迁移文件

python manage.py makemigrations # 迁移文件, 类似模型类的迁移类,主要是描述了数据表结构的类文件.相当于生成SQL语句

2 同步到数据库

python manage.py migrate # 相当于执行SQL语句

3 回滚迁移操作

django中针对数据迁移的每一次操作都会在数据库中的django_migrations表中有历史记录。django_migrations表中的app字段表示本次数据迁移的子应用目录名,name字段本次同步数据的迁移文件。

 

 

 

python manage.py migrate <app> <name>  # 把当前项目中的数据迁移历史记录回滚指定版本。
python manage.py migrate <app> zero         # 把指定子应用目录下所有数据迁移进行历史回滚,再把在当前子应用目录下删除掉数字开头的迁移文件即可。

4 数据迁移的利弊

好处:

  1. 不需要我们自己编写表结构的SQL语句,数据迁移过程中自动生成

  2. 数据迁移内部自动根据模型类的改动而自动生成SQL语句,并且产生一个记录文件。

    因为这个记录文件,所以开发者可以清晰的看到数据模型的改变过程。在数据库中还会生成一张django_migrations的迁移记录表。

  3. 数据迁移提供了简单的终端命令来实现,所以开发者不存在大的学习成本。只有2个命令,分别是python manage.py makemigationspython manage.py migrate

坏处:

  1. 数据迁移毕竟是程序内部根据模型类来生成建表的SQL语句,所以有时候生成的SQL语句有语法问题,反而让开发者因为迁移文件的错误而产生维护成本,当然多数存在于模型与模型之间的多对多外键关系中,随着django的不断完善,现在基本没有这个问题了,但是低版本的django中还可能存在的。

  2. 数据迁移节省了编写SQL语句的时间,但是复杂的表结构,数据迁移也可能无法实现,无法迁移到数据库中。

  3. 如果有专门的数据库管理员(DBA),则这个功能则完全没有任何作用了,但是ORM会继续运行而产生性能消耗问题。如果使用了原生SQL语句进行数据库的建表操作,那么数据迁移记录就混乱,甚至会报错,所以一旦决定要使用数据迁移,那么后续的全部数据库相关的表结构操作都要使用数据迁移才行。

五 ORM的优缺点

优点

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

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

  • ORM迫使开发使用 MVC 架构,ORM 就是天然的 Model,最终使代码结构更清晰易懂。

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

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

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

缺点

  • ORM 库不是轻量级工具,需要花很多精力学习和设置,甚至不同的项目框架会存在不同操作的ORM。

  • 对于复杂的业务查询,ORM表达起来比原生的SQL要更加困难和复杂。ORM并非万能的,总有些功能需要我们使用原生SQL语句,当然ORM也提供执行原生SQL语句的功能

  • ORM操作数据库的性能要比使用原生pymysql执行SQL差。[ORM内部要拼接SQL语句]

  • ORM 抽象掉了数据库层,开发者无法了解底层的数据库操作,也无法定制一些特殊的 SQL。【自己使用pymysql另外操作即可,用了ORM并不表示当前项目不能使用别的数据库操作工具。】

 

posted on 2022-07-20 23:39  大明花花  阅读(216)  评论(0编辑  收藏  举报