ORM是什么?为何要有ORM?
我们在使用Django框架开发web应用的过程中,不可避免地会涉及到数据的管理操作(增、删、改、查),而一旦谈到数据的管理操作,就需要用到数据库管理软件,例如mysql、oracle、Microsoft SQL Server等。
如果应用程序需要操作数据(比如将用户注册信息永久存放起来),那么我们需要在应用程序中编写原生sql语句,然后使用pymysql模块远程操作mysql数据
针对应用程序的数据操作,直接编写原生sql语句会存在两方面的问题,严重影响开发效率,如下
为了解决上述问题,django引入了ORM的概念,ORM全称Object Relational Mapping,即对象关系映射,是在pymysq之上又进行了一层封装,对于数据的操作,我们无需再去编写原生sql,取代代之的是基于面向对象的思想去编写类、对象、调用相应的方法等,ORM会将其转换/映射成原生SQL然后交给pymysql执行
基于图2所示,有了ORM框架,开发人员既不用再去考虑原生SQL的优化问题,也不用考虑数据库迁移的问题,ORM都帮我们做了优化且支持多种数据库,这极大地提升了我们的开发效率,下面就让我们来详细学习ORM的使用吧
在django的ORM框架中,继承自django.db.models.Model的类称之为模型类,或简称模型。
一个模型是关于你的数据,唯一的、决定性的信息源、它包含存储数据的基本字段和方法。
通常,每个模型都映射到一个数据库表。模型中的属性对应数据库表的字段
如下所示:原生SQL与ORM的模型对应关系
数据来源于数据库的表,而ORM的模型类对应数据库表,所以若我们想操作数据,必须先创建模型。
| |
| class Employee(models.Model): |
| id=models.AutoField(primary_key=True) |
| |
| name=models.CharField(max_length=16) |
| |
| gender=models.BooleanField(default=1) |
| |
| birth=models.DateField() |
| |
| department=models.CharField(max_length=30) |
| |
| salary=models.DecimalField(max_digits=10,decimal_places=1) |
但凡涉及到数据库同步操作的应用,都需要事先在settings.py的INSTALLED_APPS中完成注册
| INSTALLED_APPS = [ |
| 'django.contrib.admin', |
| 'django.contrib.auth', |
| 'django.contrib.contenttypes', |
| 'django.contrib.sessions', |
| 'django.contrib.messages', |
| 'django.contrib.staticfiles', |
| |
| |
| 'app01.apps.App01Config', |
| |
| |
| |
| 'app01', |
| |
| ] |
django的orm支持多种数据库(如PostgreSQL、MySQL、SQLite、Oracle等),如果想将上述模型转为mysql数据库中的表,需要settings.py中配置DATABASES,如下
| |
| DATABASES = { |
| 'default': { |
| 'ENGINE': 'django.db.backends.mysql', |
| 'NAME': 'db1', |
| 'USER': 'root', |
| 'PASSWORD': '', |
| 'HOST': '127.0.0.1', |
| 'PORT': 3306, |
| 'ATOMIC_REQUEST': True, |
| |
| |
| "AUTOCOMMIT":False, |
| 'OPTIONS': { |
| "init_command": "SET storage_engine=INNODB", |
| 'isolation_level': "read committed", |
| } |
| } |
| } |
| |
| """ |
| =================================》额外补充: |
| 当运行并发负载时,来自不同会话的数据库事务(例如,处理不同请求的单独线程)可能会相互交互。 这些交互受每个会话的事务隔离级别的影响。 您可以在DATABASES中的数据库配置的OPTIONS部分中使用'isolation_level'条目设置连接的隔离级别。 此条目的有效值是四个标准隔离级别: |
| |
| 'read uncommitted' |
| 'read committed' |
| 'repeatable read' |
| 'serializable' |
| 或None来使用服务器配置的隔离级别。 |
| 然而,Django最好使用read committed而不是MySQL的默认值repeatable read。 repeatable read可能导致数据丢失。 |
| """ |
强调!!!:在链接mysql数据库前,必须事先创建好数据库
mysql> create database db1; # 数据库名必须与settings.py中指定的名字对应上
如果想打印orm转换过程中的sql,需要在settings中进行配置日志:
| LOGGING = { |
| 'version': 1, |
| 'disable_existing_loggers': False, |
| 'handlers': { |
| 'console':{ |
| 'level':'DEBUG', |
| 'class':'logging.StreamHandler', |
| }, |
| }, |
| 'loggers': { |
| 'django.db.backends': { |
| 'handlers': ['console'], |
| 'propagate': True, |
| 'level':'DEBUG', |
| }, |
| } |
| } |
首先执行下述命令来创建一个迁移
| $ python manage.py makemigrations |
会抛出异常
| ...... |
| raise ImproperlyConfigured( |
| django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module. |
| Did you install mysqlclient? |
提示我们需要事先安装好mysqlclient,并且默认加载的mysqlclient是模块MySQLdb,需要安装MySQL-python,但目前python官方第三库中最新版MySQLdb 1.2.5对python解释器以及MySQL Server的支持如下,并不支持python3.0+
• 对Python解释器的支持
– CPython : 支持2.4到2.7
– PyPy(用python实现的python解释器) : 支持到最新版本
• 对MySQL Server的支持
– 3.23=< MySQL <= 5.5
为此,有人在github中创建了一个MySQLdb1分支 来支持Cpython3.0+解释器,可以参照官网进行安装https://pypi.org/project/mysqlclient/
除了使用MySQLdb作为MySQLclient之外,我们还有可以使用pymysql
• Python解释器支持:
– CPython : 2.7 and >= 3.5
– PyPy : Latest version
• MySQL Server支持:
– MySQL >= 5.5
– MariaDB >= 5.5
比起前者,pymysql直接支持Cpython3.0+,安装简单
但是因为Django默认加载的MySQLclient是MySQLdb,所以需要作出修改
此时重新执行python manage.py makemigrations仍会抛出异常
| ...... |
| File "/Users/linhaifeng/PycharmProjects/egon_test/venv-3.8/lib/python3.8/site-packages/django/db/backends/mysql/base.py", line 37, in <module> |
| raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__version__) |
| django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 0.9.3. |
需要根据上述异常提示的文件base.py路径打开文件,注释下述两行
| if version < (1, 3, 13): |
| raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__version__) |
如果重新执行makemigrations命令,扔抛出异常
| ...... |
| File “xxx\lib\site-packages\django\db\backends\mysql\operations.py”, line 146, in last_executed_query |
| query = query.decode(errors=‘replace’) |
| AttributeError: ‘str’ object has no attribute ‘decode’ |
需要根据上述异常提示的operations.py文件路径打开文件,把146行的decode修改为encode
ps:PyPI(Python Package Index)是Python官方的*第三方库*,所有人都可以下载或上传Python库到PyPI。可以访问https://pypi.org/,自行查看各个库的版本支持,
最后在命令行中执行两条数据库迁移命令,即可在指定的数据库db1中创建表 :
| $ python manage.py makemigrations |
| $ python manage.py migrate |
| |
| |
| |
| |
| |
| 如果要给迁移一个有意义的名称而不是生成的名称,则可以使用makemigrations --name选项: |
| python manage.py makemigrations --name xx app01 |
| |
| python3 manage.py sqlmigrate app01 0001_xx |
| python manage.py showmigrations |
注意:当我们直接去数据库里查看生成的表时,会发现数据库中的表与orm规定的并不一致,这完全是正常的,事实上,orm的字段约束就是不会全部体现在数据库的表中,比如我们为字段gender设置的默认值default=1,去数据库中查看会发现该字段的default部分为null
| mysql> desc app01_employee; |
| +------------+---------------+------+-----+---------+----------------+ |
| | Field | Type | Null | Key | Default | Extra | |
| +------------+---------------+------+-----+---------+----------------+ |
| | id | int(11) | NO | PRI | NULL | auto_increment | |
| | name | varchar(16) | NO | | NULL | | |
| | gender | tinyint(1) | NO | | NULL | | |
| | birth | date | NO | | NULL | | |
| | department | varchar(30) | NO | | NULL | | |
| | salary | decimal(10,1) | NO | | NULL | | |
| +------------+---------------+------+-----+---------+----------------+ |
,虽然数据库没有增加默认值,但是我们在使用orm插入值时,完全为gender字段插入空,orm会按照自己的约束将空转换成默认值后,再提交给数据库执行
在表生成之后,如果需要增加、删除、修改表中字段,需要这么做
| |
| |
| publish = models.CharField(max_length=12,default='人民出版社',null=True) |
| |
| |
| |
| |
| |
| |
| |
| |
| |
在开发django项目时,如果我们想跳过django的启动与操作流程,只测试某一部分的代码的功能,需要首先引入django的配置环境才可以
在任意路径下新建一个py文件(文件名任意),内容如下
| import sys |
| import os |
| import django |
| |
| |
| BASE_DIR = os.path.dirname(传入项目所在的根目录) |
| sys.path.append(BASE_DIR) |
| |
| |
| os.environ.setdefault('DJANGO_SETTINGS_MODULE', '项目名.settings') |
| django.setup() |
| |
| |
| |
| if __name__ == "__main__": |
| |
| from app01.models import Employee |
| |
| all = Employee.objects.all().values() |
| print(all) |
增加一条记录
| obj=Employee(name="Egon",gender=0,birth='1997-01-27',department="财务部",salary=100.1) |
| obj.save() |
查询记录
| obj = Employee.objects.filter(name="Egon").first() |
| print(obj.id, obj.name, obj.birth) |
修改记录
| Employee.objects.filter(name="Egon").update(name="EGON") |
删除记录
| Employee.objects.filter(name="EGON").delete() |
定一个Person模型,有两个属性first_name与last_name
| from django.db import models |
| |
| class Person(models.Model): |
| first_name = models.CharField(max_length=30) |
| last_name = models.CharField(max_length=30) |
first_name与last_name是这个模型的字段,每个字段都被指定为类属性,每个属性都映射到数据库列。
上面的Person模型将创建如下一个数据库表
| CREATE TABLE myapp_person ( |
| "id" serial NOT NULL PRIMARY KEY, |
| "first_name" varchar(30) NOT NULL, |
| "last_name" varchar(30) NOT NULL |
| ); |
一些注意事项:
1、数据库中生成的表会加上"app名_"作为前缀,如myapp_persion,也可以重新为别的名称,详见第四节 2、会自动添加一个id字段作为主键字段,但也可以重写此行为,详见第3.3小节 3、本例中的CREATE TABLE SQL是使用PostgreSQL语法格式化的,但值得注意的是Django使用的SQL是根据设置文件中指定的数据库后端定制
一旦你定义了模型,你就需要告诉djano你将要使用这些模型。如何做到呢?
需要通过编辑配置文件settings.py,在配置项INSTALLED_APPS注册你包含模型文件
models.py所在的app
例如,如果你的app的模型在myapp.models里,INSTALLED_APPS配置如下
| INSTALLED_APPS = [ |
| |
| 'myapp', |
| |
| ] |
然后运行命令
| python manage.py makemigrations |
| python manage.py migrate |
Django 提供了基于 web 的管理工具用来管理我们的模型。如何使用呢
• 1、通常我们在生成项目时就会在 urls.py 中自动设置好,如下
| urlpatterns = [ |
| path('admin/', admin.site.urls), |
| ] |
• 2、启动Django ,管理工具就可以运行了,在浏览器中访问 http://127.0.0.1:8000/admin/,得到如下界面:
• 3、你可以通过命令 *python manage.py createsuperuser* 来创建超级用户,如下所示:
| |
| Username (leave blank to use 'root'): egon |
| Email address: admin@runoob.com |
| Password: |
| Password (again): |
| Superuser created successfully. |
之后输入用户名密码登录,界面如下:
• 4、为了让 admin 界面管理某个数据模型,我们需要先注册该数据模型到 admin。比如,我们之前在 app01/models.py中已经创建了模型 Person 。修改 app01/admin.py:
| from django.contrib import admin |
| |
| |
| |
| from app01.models import Person |
| admin.site.register(Person) |
刷新页面即可看到Person数据表,可以在模型类中新增str方法来控制记录的显示内容。