参考:
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数据库。