Django:模型_ORM模型基础

为什么要用ORM模型

1、在前面那章中我们介绍了如何在Django中使用原生SQL来操作数据库:直接在视图函数中执行对应的SQL语句

2、随着项目越来越大,如果一直采用原生SQL的方式,那么在代码中会出现大量的SQL语句,就会出现很多问题:
    ⑴SQL语句重复利用率不高,越复杂的SQL语句条件越多,代码越长,会出现很多相近的SQL语句
    ⑵很多SQL语句是在业务逻辑中拼接出来的,如果有数据需要更改,就需要去修改这些逻辑,这就会很容易漏掉某些SQL的修改
    ⑶写SQL时容易忽略Web安全问题,比如SQL注入等
    
    

ORM模型介绍

1、ORM,全称Object Relational Mapping,中文叫做对象关系映射,通过ORM我们可以通过类的方式去操作数据库,而不用再写原生的SQL语句。通过把表映射成类,把行作为实例,把字段作为属性,ORM在执行对象操作的时候最终还是会把对应的操作转换为数据库原生语句。使用ORM有许多优点:
    ⑴易用性:使用ORM做数据库的开发可以有效的减少重复SQL语句的概率,写出来的模型也更加直观、清晰
    ⑵性能损耗小:ORM转换成底层数据库操作指令确实会有一些开销。但从实际的情况来看,这种性能损耗很少(不足5%),只要不是对性能有严苛的要求,综合考虑开发效率、代码的阅读性,带来的好处要远远大于性能损耗,而且项目越大作用越明显。
    ⑶设计灵活:可以轻松的写出复杂的查询。
    ⑷可移植性:Django封装了底层的数据库实现,支持多个关系数据库引擎,包括流行的MySQL、PostgreSQL和SQLite。可以非常轻松的切换数据库
    
2、模型是一个用于表示数据的Python类,包含基本的数据字段和行为,在Django中,通常一个模型(类)就代表一个数据库表
    ⑴模型类必须继承于"django.db.models.Model"或其子类
    ⑵模型类的每一个属性代表一个数据表的列

创建ORM模型

ORM模型一般都是放在app的models.py文件中。每个app都可以拥有自己的模型。并且如果这个模型想要映射到数据库中,那么这个app必须要放在settings.py的INSTALLED_APP中进行安装、注册
    ⑴定义一个模型类(数据库表)一般是在对应应用程序APP下的models.py文件中
    ⑵在定义好一个模型类(数据库表)后,这个模型类要正常的映射到数据库中的话,那这个应用程序APP必须要在settings.py的INSTALLED_APP中进行安装
    ⑶O(objects):类和对象
    ⑷R(Relation):关系,关系数据库中的表格
    ⑸M(Mapping):映射

ORM框架功能

1、建立模型类和表之间的对应关系,允许我们通过面向对象的方式来操作数据库

2、根据设计的模型类生成数据库中的表格

3、通过配置就可以进行数据库的切换

例1:定义一个模型类

⑴定义模型类

注:以上便定义了一个模型

1、这个模型继承自django.db.models.Model,如果这个模型想要映射到数据库中,就必须继承自这个类或其子类
    ①如果要将一个普通的类变成一个可以映射到数据库中的ORM模型,那么必须要将父类设置为"models.Model"或者其子类(模型类必须继承于models.Model)

2、从上面例子中可以看出:其实定义一个模型类跟在Python脚本中定义一个类是差不多的,只是说为模型类时,对应的是数据库的表的结构
    ①模型类中的类名:对应的是数据库表的表名(模型类映射到数据库中后,表名是模型类类名的小写形式,为book) 
    ②模型类中的类属性名:对应着数据库表的列名
    ③模型类中的类属性的值:对应的数据库表列的属性
    ④模型类中的类属性的值调用的方法(models.方法名):表示定义数据库表中该列值的属性(值类型)。值的其他属性在方法函数中以参数的形式传入

3、在这个模型类中定义了五个类属性,即对应着数据库表中的五列
    ①第一个类属性名为"id":表示数据库表的一列,列名为id。其属性为自动增长(models.AutoField:自动增长),且其为表的主键(primary_key=True)
    ②第二个类属性名为"name":表示数据库表的一列,列名为name。其属性为字符串型(models.CharField),且值最大长度为100(max_length=100),且值不能为空(null=False)
    ③第三个类属性名为"author":表示数据库表的一列,列名为author。其属性为字符串型(models.CharField),且值最大长度为100(max_length=100),且值不能为空(null=False)
    ④第四个类属性名为"price":表示数据库表的一列,列名为price。其属性为浮点型(models.FloatField),且默认值为0(default=0),且值不能为空(null=False)
    ⑤第五个类属性名为"time":表示数据库表的一列,列名为time。其属性为日期时间型(models.DateTimeField),且默认值为当前时间(default=default=datetime.now())

4、通过上面这个例子我们就定义好了一个模型类,但是仅仅是定义一个模型类还是不够的,接下来还需要将模型类映射到数据库中

映射模型到数据库中

将ORM模型映射到数据库中,总结起来就是以下几步:
    ⑴在settings.py中,配置好DATABASES,做好数据库相关的配置
    ⑵在app中的models.py中定义好模型,这个模型必须继承自django.db.models
    ⑶将这个app添加到settings.py的INSTALLED_APPS中
    ⑷在命令行终端,进入到项目所在的路径,然后执行命令python manage.py makemigrations来生成迁移脚本文件
    ⑸同样在命令行中,执行命令python manage.py migrate来将迁移脚本文件映射到数据库中

例2:因为前面介绍过了在settings.py中安装APP和配置数据库了,因此这里就直接从第4步开始
⑴生成迁移脚本文件:python manage.py makemigrations

⑵生成数据库表:python manage.py migrate

注:
1、通过数据库客户端可以看到我们通过模型类映射到数据库后生成的数据库表:
    ⑴实际生成的表的名称为"polls_book":即"应用程序名_模型类名小写",这是Django默认生成的数据库表名。如果只是想将类名设置为表名的话,还需要在模型类中单独定义一下
    ⑵表的名称是自动生成的,如果你要自定义表名,需要在model的Meta类中指定db_table参数,强烈建议使用小写表名,特别是使用MySQL作为后端数据库时
    ⑶这个例子中在执行"migrate"命令后还生成了一些其他的表:这些表是Django内置的一些APP中定义的模型类,它们会在我们第一次生成数据库表是一起生成

2、Django在定义模型类时如果没有定义一个名为"id"的类属性(列名)且未设置数据库主键时,就会自动生成一个"id"字段作为数据库的主键。当然我们也可以自己定义"id"列并且将主键定义成非"id"列

3、每次新增、修改模型类后,都需要执行makemigrations和migrate命令

例2_1:
⑴定义一个模型类:未设置主键

⑵查看生成的数据库表
    执行makemigrations和migrate命令就不截图了,但是还是要执行

注:从这个例子可以看出
在定义模型类时如果没有定义一个名为"id"的类属性(列名)且未设置数据库主键时,就会自动生成一个"id"字段作为数据库的主键。当然我们也可以自己定义"id"列并且将主键定义成非"id"列

ORM模型基本的增删改查

前面介绍了使用原生SQL语句来向数据库插入数据以及从数据库中取数据并显示在前端网页上,其实自己感觉前端、视图函数、数据库三者之间的关系就是:
    ⑴前端->视图函数->数据库:前端获取数据后传递给视图函数,视图函数再执行对应SQL语句向数据库中插入数据
    ⑵数据库->视图函数->前端:(前端发出请求后)视图函数从数据库中获取数据,然后传递给前端,前端进行显示

插入数据

例3:插入数据
⑴编辑视图

⑵访问URL:模板很简单就不截图了

⑶查看数据库

注:
1、上面例子的过程大概是:前端访问URL,Django根据URL映射找到对应的数据函数,然后执行视图函数,其中就有执行SQL插入操作和返回指定的HTML模板。所以一定要访问URL才会执行
    ⑴当然前端提交数据或请求数据都必须先访问URL才行:前端访问URL,Django根据URL映射找到对应的数据函数,然后执行视图函数

2、从上面这个例子可以看出:使用ORM模型进行数据库操作时,完全没有用到SQL语句,而是直接通过操作类来操作数据库
    ⑴首先调用模型类并传入对应参数值并将其赋值给一个变量:这里感觉跟Python语言中的实例化类一样,只是说这里传入的是类属性的值(Python中类实例化时是传入实例属性,而类属性时在类中定义好的)
    ⑵然后模型类变量调用save()方法将数据提交到数据库:这里感觉就是Django将SQL语句和commit()方法封装成了save()方法

3、上面这个例子中如果在访问URL(执行插入操作)时,报错"(1366, "Incorrect string value: '\\xE5\\xBC\\xA0\\xE4\\xB8\\x89' for column",这是因为数据库表编码的问题,需要单独设置下
    ⑴感觉可以在Django中直接设置表的编码,不过百度了一下,不建议这么做。百度建议直接在数据库里面执行SQL语句来改变数据库表的编码"ALTER TABLE 表名 CONVERT TO CHARACTER SET utf8"

查询数据

查询数据分为两种方式:根据主键查询和根据其他条件查询

例4:根据主键查询且未重写类
⑴编辑视图

⑵编辑模板

⑶访问


注:
1、查询语句中的"objects"可以先不管,现在只需要知道:在没有定义自定义查找方式时,那么默认所有的查询工作都是通过"objects"对象来完成的。至于自定义查询方式,后面会介绍

2、get()方法:获取一条数据,有数据时返回数据,没有数据时返回一个异常
    ⑴根据表的主键来查询,那么肯定只会返回一条数据,那么就可以使用get()方法来查询
    ⑵在使用get()方法来进行查询时可以传递一个名为"pk"的参数,其值为需要查询的主键值
    ⑶"pk"表示"primary key":这个参数可以不管实际的表主键名是什么,都可以使用pk来进行查询
    ⑷当然这个"pk"参数名也可以是具体的主键名,只是说如果实际的主键名变了,那这个get()方法中的主键名参数也必须跟着变,但是使用"pk"的话,那不管实际主键名怎么变,get()方法中的都不用变
    ⑸使用这种方式来查询的话感觉还是有限制的:比如这种就只适合只有一个列是主键的,对于那种由几列组合成主键的表,就不大使用了,因为只能传入一个值

3、在视图函数和模板中我们都分别打印和显示了查询出来的数据(方式不一样),在视图函数中直接打印返回数据的实例名返回的是一个polls.models.Book类,而在模板中通过"实例名.列名"的方法获取具体一个列的值
    ⑴在视图函数中直接打印实例名:这是因为前面我们在插入数据时说过"使用模型类方法时感觉是Python中的实例化一个类(将其赋值给一个变量)"。在Python中直接打印一个实例类的话,输出的就是一个类的信息、地址等。而不是一个适合用户看的字符串信息
    ⑵如果我们想要在视图函数中更好的显示实例信息的话,可以重写下类:使用Python中的__str__方法,将一个实例类输出信息变成一个可读的字符串,这个返回的字符串可自定义输出哪些内容
    ⑶当然也可以在视图函数中不重写类,因为这并不影响在模板和视图函数中的获取和显示某一列具体的值:只要在模板或视图函数中获取列的值的方法为"实例名.列名"。只是说如果需要在视图函数中打印完整的返回数据的信息的话,那就需要重写类了(比如打印日志什么的)


例4_1:根据主键查询且重写类
⑴重写模型类

⑵访问

注:
1、上面例子中就使用了__str__方法来重写类的输出:将输出结果变为一个可读的字符串。字符串的内容由我们自己定义

2、需要注意的是:前面说过模型类中的列名相当于Python中的类属性。但是在Python中调用类属性是"类名.属性名",但是在Django模型类中是"self.属性名"

3、在获取一个数据后,我们就需要在视图函数或模板中进行处理,并显示在网页上,获取返回数据中某个列的值的具体方法为:对象实例名.列名

4、可以看出在模型类中重写了类,并不会影响到返回数据在模板中的获取和显示的:实例名.列名

5、在模型类中是否重写了类只会影响到在视图函数中的打印:在打印实例名时的返回

6、当然在模型类中重写类时定义的__str__方法返回的字符串也不会影响到在模板中获取和显示列值


例5:根据其他条件来查询filter()
⑴重新定义下__str__方法

⑵编辑视图

⑶编辑模板

⑷访问

注:从上面几个例子中可以看出

1、其实不管在不在模型类中重写__str__方法都不会影响到在视图函数或模板中获取具体某列的值,在视图函数或模板中获取某列具体的值,可以使用:实例名.列名

2、在重写__str__方法时,不管怎么定义返回的字符串信息,都不会影响到查询出来的数据的实际值,只是会影响到怎么以字符串的形式来显示查询返回的数据,只要能让人看得懂就行了。只是说重写了__str__方法对我们在进行调试、打印日志时有很好的帮助

3、根据表主键来查询时,由于查询出来的数据只有一条,因此可以使用get()方法来获取:但是使用get()来获取后,就不能在视图函数中使用for循环或在模板中使用for标签了,因此此时返回的数据不是一个可迭代对象。不过此时只有一条数据也不需要遍历了,直接使用"实例名.列名"来获取列的值就可以了
4、根据表其他条件来查询(非主键)时,查询出来的数据可能会有多条:
    ⑴从上面例子中可以看出:如果查询出来的数据有多条的话,返回的数据会是一个QuerySet对象且查询出来的所有数据会在一个列表中,数据具体怎么显示就是我们重写的__str__方法决定的了
    ⑵查询出多条数据(返回数据是一个QuerySet对象)时,可以使用for循环或for标签来对其进行迭代,然后再使用"遍历后的变量.列名"来获取某个列具体的值
    ⑶QuerySet对象是一个类似于列表的数据类型:因此在获取整个查询返回数据后,可以使用"frist()"方法来获取返回数据中的第一个数据,或使用"[索引值]"来获取全部返回数据中指定的某个数据

5、至于QuerySet对象和ORM方法后面会单独介绍,这里只是用例子来简单的说明一下,对ORM模型有个基本的了解

 

删除数据

在使用ORM模型来删除数据时,会比直接使用SQL语句来删除多一步:那就是需要先把数据查询出来

例6:删除数据
    模板和访问页面没有什么特别的就没截图:只需要访问URL执行视图函数就可以了
⑴编辑视图

注:
1、在进行删除数据操作时,必须先查询数据,然后使用"实例名.delete()"方法来将查询出来的数据删除即可

2、不管查询出来的数据是一条或多条,只要查询出来了,在执行delete()方法时都会把查询出来的数据删除

更新数据

在使用ORM模型来更新数据时也会比使用SQL语句来更新多一步:那就是需要先把数据查询出来

例7:更新数据
⑴编辑视图

注:
1、在进行更新数据操作时,必须先查询数据,然后先使用"实例名.列名=值"方法来将查询出来的数据进行更新,最后调用save()方法进行保存几个

2、自己在试的时候发现:
    ⑴如果返回数据是一个QuerySet对象的话,就不能使用save()方法来保存更新。(get方法返回的是一个数据库表:class 表名,filter()方法返回的是一个QuerySet对象)
    ⑵使用get()方法来查询,如果查询结果是多条数据的话,也不能使用save()方法来进行保存。应该还有其他方法,只是目前还没接触到

Python类补充

例8:

class Cat():
    name = "jree"

cat = Cat()
print(cat)#直接输出一个类实例

class Dog():
    address = "china"

    def __init__(self,name):
        self.name = name
    def __str__(self):
        return "狗类%s" % Dog.address#类中调用类属性

    def From(self):
        return "名字为%s,来自%s"% (self.name,Dog.address)#在类中调用实例属性和类属性

dog = Dog("tom")
print(Dog)#打印抽象类
print(dog)#打印类实例
print(dog.address)#类外调用类属性
print(Dog.address)#类外调用类属性
print(dog.name)#类外调用实例属性

"""
<__main__.Cat object at 0x000001C12E300048>
<class '__main__.Dog'>
狗类china
china
china
tom
"""

注:
1、当使用print输出实例对象的时候,且没有定义__str__方法时,那么就输出的是一个实例对象的地址等信息,如果定义了__str__(self)方法,那么就会打印从在这个方法中return的数据。这个return中可以自定义返回那些数据

2、调用类属性:
    ⑴类中:类名.类属性名
    ⑵类外:类名.类属性名或实例名.类属性名

2、调用实例属性
    ⑴类中:self.实例属性名
    ⑵类外:实例名.实例属性名

3、调用类方法
    ⑴类中:self.方法名
    ⑵类外:实例名.方法名

发布了9 篇原创文章 · 获赞 0 · 访问量 224

猜你喜欢

转载自blog.csdn.net/zh18380113164/article/details/105186396
今日推荐