从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 tfsdemosourceCollectorSvrdb.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开发体系已经给笔者很多惊喜...。