參考:
https://stackoverflow.com/questions/54294114/django-inspectdb-ora-00904-identity-column
背景:單位運行一批系統,數據庫是oracle11g,java開發,比較老的框架,代碼看不懂。我想到用django作為補丁語言,開發一些微服務接口。
一、技術調查
1. django繼承數據遺產(legacy database),用 python manage.py inspectdb [tablename] ,反向生成model.py
2.Django==1.11.22 可以用inspectdb,反向生成oracle11g的model.py
django2.x以上,若想對oracle migrate操作,只能針對oracle12c以上。不能作用於oracle11g
django1.11.22 migrate oracle11g
django 2.x migrate oracle12c
3. ubuntu linux上,訪問oracle需要2點
(1)cx_oracle
(2) oracle instant client
4.最終的解決方案
(1)安裝2套virtualenv, 一個是django==1.11.22,專門用於反向oracle11g 的table,生成model.py
另一套venv是生產環境,django>2.x
(2)單位oracle是11g,用django2.x不能migrate, 因為dj2.0是按oracle12c定制的backends, migrate會產生
(ORA-02000: missing ALWAYS keyword)錯誤。
那么這些legacy data,
DDL:手工操作,create table ,add column 等。
CRUD data: insert table , delete ,update table 等, queryset是支持的。
(3) 建設multiple database ,django運行的表(user, session等)用sqlite, 數據遺產不做migrate,主要用於業務查詢和crud
參考:https://docs.djangoproject.com/en/3.0/topics/db/multi-db/
二、環境安裝設置記錄
環境問題復雜,不同軟件是否適配需要按官方說明或自己測試。 我按以下測試通過
os:ubuntu 16.04
django==2.2.4(produce) #用於服務運行
django====1.11.22(tmp) #用於inspectdb oracle 11g
1. 安裝cx_oracle
pip install cx-Oracle==6.0 (最新版本也可以cx_oracle==7.3)
2. 安裝Oracle Instant Client RPM
參考:
https://help.ubuntu.com/community/Oracle%20Instant%20Client
https://oracle.github.io/odpi/doc/installation.html#linux
下載:oracle-instantclient19.6-basic-19.6.0.0.0-1.x86_64.rpm
https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html
3.安裝,設置
sudo apt-get install alien
sudo alien -i oracle-instantclient19.6-basic-19.6.0.0.0-1.x86_64.rpm
安裝完成后,在/usr/lib/oracle/19.6/client64/lib/network/admin目錄下,制作tnsnames.ora文件,內容如下:
lxg@lxg-opt:/usr/lib/oracle/19.6/client64/lib/network/admin$ cat tnsnames.ora 234 = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 202.205.180.234)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = orcl) ) )
注意內容要可讀
lxg@lxg-opt:/usr/lib/oracle/19.6/client64/lib/network/admin$ sudo chmod -R 777 oracle
設置oracle_home
sudo gedit /etc/profile
export ORACLE_HOME=/usr/lib/oracle/19.6/client64
export LD_LIBRARY_PATH=/usr/lib/oracle/19.6/client64/lib:$LD_LIBRARY_PATH
生效
source /etc/profile
三、程序運行測試
1.普通python連接oracle
import cx_Oracle def test1(): print('開始。。。。。') connection = cx_Oracle.connect("jssq/js@234") print (connection.version) connection.close() if __name__ == '__main__': test1()
輸出:
開始。。。。。
11.2.0.1.0
2.django測試
(1)setting.py
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }, 'oracle': { 'ENGINE': 'django.db.backends.oracle', 'NAME': 'orcl',(數據庫的service_name,默認是orcl) 'USER': 'jssq', 'PASSWORD': 'js', 'HOST': 'xxx.xxx.xxx.234',(數據庫服務器ip) 'PORT': '1521', } }
(2) oracle數據庫
CREATE TABLE "JSSQ"."JS_MINZU"
( "BM" VARCHAR2(10),
"MC" VARCHAR2(60),
CONSTRAINT "PK1" PRIMARY KEY ("BM"))
這是pl/sql工具查看的結果
用django1.11.22,反向生成model.py
(py3.6env) lxg@lxg-opt:~/98receive/pystudy/g_oracle$ python manage.py inspectdb js_minzu > js_minzu.py
生成的model如下,簡單改一下model名稱變成Product(為了使用原來的跑在sqlite的程序)
要在django中正常使用反向生成的model,要求oracle table有且有唯一column的primary key ,不支持聯合主鍵。
from django.db import models class Product(models.Model): bm = models.CharField(primary_key=True, max_length=10) mc = models.CharField(max_length=60, blank=True, null=True) class Meta: managed = False db_table = 'js_minzu'
managed= False ,Django 將不會為當前 model 創建或者刪除數據庫表。
通常在表示某個 通過其他方法創建的現有數據表時這會非常有用。這是當 managed=False 時 僅有 的不同之 處。
以下,migrate oracle數據庫會失敗,因為django2針對oracle12c 有效。我用的orcle11g。
所以,我用雙數據庫,ddl用sqlite,對業務數據oracle只進行crud,不migrage.
(py3.6env) lxg@lxg-opt:~/98receive/pystudy/g_oracle$ python manage.py migrate --database=oracle System check identified some issues: cx_Oracle.DatabaseError: ORA-02000: missing ALWAYS keyword
view.py
class ProductList(ListView): paginate_by = 6 model = MYTABLE def get_template_names(self): template_name = '{}/{}List.html'.format(APPNAME,MYTABLE.__name__) return template_name def get_queryset(self): # user_id = self.request.user.id queryset = MYTABLE.objects.using('oracle').all() self.filter = ProductFilter(self.request.GET, queryset=queryset) return self.filter.qs def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['filter'] = self.filter context['model'] = self.model return context
using('oracle') ,表示使用oracle數據庫,否則會用default數據庫。
