python工業網際網路應用實戰1—SQL與ORM
從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開發體系已經給筆者很多驚喜...。
&n