从 sql 到 ORM 应该说也是编程体系逐步演化的结果, 通过类和对象更好的组织开个过程中遇到的各种业务问题, 面向对象的解耦和内聚作为一套有效的方法论, 对于复杂的企业应用而言确实能够解决实践过程中很多问题.
1. 早期 No ORM 的做法
这里先跟笔者回忆一下历史, 在没有普及使用对象映射层之前, 做企业业务系统开发通常是怎么做的呢? 首先是不变的当然是需求分析, 需求基本确定下来后, 就是依据原始业务单据进行数据库表设计了, 因为大量的企业信息化系统首先要干的第一件事情就是保存表单 \ 保存表单 \ 保存表单, 笔者多年来干过的大量的事情就是保存表单 :( , 实现业务单据无纸化, 单据数据保存到数据库表里, 便于将来的查询, 检索和统计分析.
1.1. 表结构设计
数据库表结构字段的设计和一些基础数据信息的设计, 通常叫做数据字典. 举例来说呢, 比如系统要构建一个 User 的表, 来存放用户基本信息, 表设计如下图:
我们通过数据管理工具 Navicat 连接前面 demo 创建的 db.sqlite3 文件数据库, 运行创建 User 表的 SQL 我们就会看到表里面增加了一张 User 表, 浏览表会看到还没有数据是一张空表.
- CREATE TABLE "user" (
- "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
- "first_name" varchar(30),
- "last_name" varchar(150),
- "is_active" bool,
- "remark" varchar(255)
- );
接下来为了便于演示, No ORM 开发模式, 执行下面的 SQL 表里插入一条记录.
INSERT INTO "main"."user"("id", "first_name", "last_name", "is_active", "remark") VALUES (1, 'ch', 'wu', '1','a test user' );
这里我们先演示通过 SQL 直接获取数据的方式来演示早期的编程模式, 后面便于与 ORM 映射模式进行对比 (没有对比就没有伤害)!Django 同样也是可以通过 SQL 来 load data 的.
1.2. User View 查看用户详情
首先, 我们构建一个基于 Django 模板的 UserView url 来查看某个 User Id 的数据详情, UserView.html 代码如下:
- <HTML>
- <head>
- <title>User Veiw</title>
- </head>
- <body>
- <div>User Id: <strong>{{Id}}</strong></div>
- <div>first Name : <strong>{{FirstName}}</strong></div>
- <div>Last Name: <strong>{{LastName}}</strong></div>
- <div>remark: <strong>{{Remark}}</strong></div>
- </body>
- </HTML>
然后, 在 views 文件里添加函数 userView 返回 UserView.HTML 模板, userView 代码如下:
- from django.http import HttpRequest
- def userView(request):
- assert isinstance(request, HttpRequest)
- return render(request,'Collector/UserView.html',\
- {'Id':'','FirstName':'','LastName':'','Remark':'',})
接着, 项目 urls 发布 userView, 我们就可以再浏览器看到这个模板运行的效果, django web 开发效率确实会快很多.
- urlpatterns = [
- # Uncomment the next line to enable the admin:
- #path('admin/', admin.site.urls)
- path('getTank4C9Data/', views.getTank4C9Data),
- path('getCollectorData/', views.getCollectorData),
- path('pushCollectorData/', views.pushCollectorData),
- path('userView/', views.userView),
- ]
浏览器运行效果:
2. SQL 访问数据方式
这一步, 我们改进代码演示如何 url 如何传入 UserId 参数然后采用 sql 从数据表读取这条记录, 并通过 django template 系统渲染到 UserView.HTML 模板上, 让页面变成一个动态加载的页面效果. 上代码:
- from django.http import HttpRequest
- import sqlite3
- def userView(request):
- assert isinstance(request, HttpRequest)
- userId=request.GET.get('UserId') #获取 UserId 参数
- if userId!=None:
- # 连接到数据库
- db = sqlite3.connect('D:\my tfs\demo\source\CollectorSvr\db.sqlite3')
- cursor = db.cursor() #创建一个游标
- cursor.execute('select *from User where id={0}'.format(userId)) #执行 SQL
- rows =cursor.fetchall() #获取数据
- row = rows[0]
- db.close()
- model={'Id':row[0] ,'FirstName':row[1],'LastName':row[2],'Remark':row[4] if row[4]!=None else '',}
- else:
- model={'Id':'','FirstName':'','LastName':'','Remark':'',}
- return render(request,'Collector/UserView.html',model)
代码解读: 通过 url 请求的 GET 参数 UserId 拼写本次要执行的 SQL 语句, 然后通过数据游标返回执行 SQL 的结果, 接着处理游标并封装到字典里, 最后通过 django 模板渲染, 最后运行效果如下 http://127.0.0.1:8090/userView/?UserId=1
userView 运行效果实现了通过传入参数的方式, 实现了从数据获取数据并显示再 UI 上的效果, 但是过程中我们就的拼写 SQL, 如果是修改或者插入数据都需要编码拼写相应的 SQL 语句. 编码过程中就有大量的编码工作是把 UI 提交的 GET POST 参数拼写成不同的 SQL 语句最后提交到数据库, 由于不同的数据库有着不同的 SQL 语法, 这导致了开发系统与数据库版本形成了强依赖关系, 如果想把系统数据库从 SQL SERVER 迁移到 Oracle 就需要大量的测试和重新适配工作.
这样过了很多年, ORM 出现了...
3. Django Model
Django ORM 对应的 Django 模型, Object Relational Mapping(对象关系映射), 就是在面向对象模式编程中, 把对象的模型跟数据库中表的对应起来. 举例来说, 一个业务对象类对应着一张表, 类型属性对应表相应的字段. 这个对象类的一个实例, 对应着表中的一条记录, 表里的一条条记录映射成对象后就是程序里一个个的对象. Django 通过 model 来管理对象类和表之间的关系, 并提供了一标准 CRUD 操作来满足数据操作的需求.
终于, 可以哈哈一笑了
3.1. 数据库配置
Django model 我们可以使用强大的数据 - 模型语句, 来描述我们的业务数据模型, 下面我们开始来体验 ORM 的到底带来了什么. 第一步首先是确认一下我们的 demo project 的数据库连接配置是否正确的指向了 db.sqlite3 数据库文件, 查看 project settings 文件的配置是否如下代码:
- # Database
- # https://docs.djangoproject.com/en/2.1/ref/settings/#databases
- DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.sqlite3',
- 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
- }
- }
如果没有修改过应该时工程创建时的默认值.
VS 2019 IDE 环境中可以打开 Open Django Shell 命令窗口, 执行下面的命令确认数据配置是否正确.
- >>> from django.db import connection
- >>> cursor = connection.cursor()
- >>>
如果没有显示什么错误信息, 那么数据库配置是正确的.
这里我们可以通过游标执行一下前面的 SQL 语句, 看看再 Django Shell 的执行效果, 先提一下 Django Shell 会给我们带来很多便利尤其再做一些探索性的编程和调试时, Django Shell 也是笔者使用 Python 的最佳实践体会之一.
- >>> from django.db import connection
- >>> cursor = connection.cursor()
- >>> cursor.execute('select *from User where id=1')
- <django.db.backends.sqlite3.base.SQLiteCursorWrapper object at 0x03D5B240>
- >>>
3.2. Model 设计
Django 模型放在 App 的 models 文件里, 现在我们在 Collector/models 定义模型吧, User 模型的属性与数据表字段对照, 为了更好的说明属性与表字段的对照关系, 我们在模型里采用了 column 定义语法, User 模型代码如下:
- from django.db import models
- # Create your models here.
- class User(models.Model):
- Id=models.AutoField(primary_key=True,db_column='id')
- FirstName = models.CharField(null=False,max_length=30,db_column='first_name')
- LastName = models.CharField(null=False,max_length=150,db_column='last_name')
- IsActive = models.NullBooleanField(null=True,db_column='is_active')
- Remark = models.CharField(null=False,max_length=255,db_column='remark')
- class Meta:
- db_table = 'user'
接下来我们就可以在 Django Shell 里操作我们定义好的模型了, 如下面的通过 UserId=1 获取一个 User 对象.
- >>> from Collector.models import User
- >>> model =User.objects.get(Id=1)
- >>> print(model.LastName+model.FirstName)
- wuch
- >>>
新增一个 User 对象
- >>> user1=User()
- >>> user1.FirstName ='xiaomin'
- >>> user1.LastName='wang'
- >>> user1.save()
- >>>
重新获取数据库表里 Id=3 对象
- >>> user2=User.objects.get(Id=3)
- >>> user2.FirstName
- 'xiaomin'
更多丰富的查询接口...
- >>> User.objects.all()
- <QuerySet [<User: User object (1)>, <User: User object (3)>]>
- >>> User.objects.get(LastName__startswith='Wu')
- <User: User object (1)>
- >>> User.objects.get(LastName__contains='wang')
- <User: User object (3)>
3.3. 重构 userView 函数
现在采用 Django 模型的方式来重构我们的 UserView 函数, 从新的代码中你会看到返回值的赋值方式也从 row[1] 改成了 user.FirstName, 对象属性的赋值方式大大的提高了代码的可读性和降低了赋值错误出错的概率.
- from django.http import HttpRequest
- from Collector.models import User
- def userView(request):
- assert isinstance(request, HttpRequest)
- userId=request.GET.get('UserId') #获取 UserId 参数
- if userId!=None:
- user = User.objects.get(Id=userId)
- model={'Id':user.Id ,'FirstName':user.FirstName,'LastName':user.LastName,'Remark':user.Remark if user.Remark!=None else '',}
- else:
- model={'Id':'','FirstName':'','LastName':'','Remark':'',}
- return render(request,'Collector/UserView.html',model)
现在调试运行效果一样了, 没有 SQL 代码却简单很多, 可读性也是
4. Model to Dict
为了进一步的提高编程效率, 直接把 model 转换成 JSON 返回格式的方式就进一步有效的降低代码量. 这里采用 model_to_dict 来进行 model 到 dict 的转换, 呵呵, 你会觉得 django 怎么会这样简单啊, 代码好少的说? 代码越少可读性就越强, 维护和扩展就越方便!
- from django.http import HttpRequest
- from Collector.models import User
- from django.forms.models import model_to_dict
- def userView(request):
- assert isinstance(request, HttpRequest)
- userId=request.GET.get('UserId') #获取 UserId 参数
- if userId!=None:
- user = User.objects.get(Id=userId)
- else:
- user=User()
- model = model_to_dict(user)
- return render(request,'Collector/UserView.html',model)
最后我们再秀以下那个后台已经天翻地覆, UI 端不变的显示界面.
5. 小节
本章节是一个新的系列文章的开始, 我们同样采用实战案例的方式和一些关键技术支出穿插的方式来介绍企业开发过程中常遇到的问题和实践经验总结. 开篇第一章就介绍 ORM 和 SQL 获取数据的不同方式, 主要是笔者近些年来使用 Django 的 ORM 实实在在的带来了开发效率的提升和业务变更的方便性, 尤其企业开发过程中遇到的林林总总的 "奇怪" 需求面前, Python 开发体系已经给笔者很多惊喜....
来源: https://www.cnblogs.com/haozi0804/p/12857020.html