Django接金仓数据库:我踩过的坑和填坑指南
开头的故事

去年做一个内部管理系统,后端用Django,客户后来要求数据库换成金仓。当时我心里挺没底的------Django的ORM很依赖数据库方言支持,金仓不是Django官方支持的数据库,万一跑不起来怎么办?
查了一圈资料,发现金仓官方提供了Django方言包。赶紧搭了个demo验证,从环境配置到跑通CRUD,前后折腾了小半天。中间踩了几个坑,今天就把过程完整的记下来。
一、准备工作:方言包和驱动
1.1 两个东西都得装
Django要连金仓,需要两样东西:
- ksycopg2:金仓的Python驱动,底层负责和数据库通信
- Kingbase方言包:告诉Django的ORM怎么生成金仓能认的SQL
方言包依赖ksycopg2,所以得先装驱动。另外要注意,ksycopg2对Python版本有要求,我用的是Python 3.8,Django 2.2.14,这个组合官方测试过,比较稳。
1.2 找到Django的安装路径
方言包要放到Django的backends目录下。可以用pip查看Django装在哪:
bash
pip show django
输出里找Location那一行,比如:
bash
Location: /usr/local/lib/python3.8/site-packages
然后把方言包整个目录拷贝到:
bash
/usr/local/lib/python3.8/site-packages/django/db/backends/kingbase
如果backends目录下没有kingbase文件夹,新建一个就行。
1.3 装对ksycopg2版本
方言包是Python写的,不分平台。但ksycopg2不同操作系统版本不一样,Linux用ksycopg2,Windows用ksycopg2-win64。
bash
# Linux
pip install ksycopg2
# Windows
pip install ksycopg2-win64
装完之后验证一下能不能导入:
python
python -c "import ksycopg2; print(ksycopg2.__version__)"
不报错就说明驱动装好了。
二、配置数据库连接
2.1 settings.py配置
python
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.kingbase',
'NAME': 'testdb', # 数据库名
'USER': 'system', # 用户名
'PASSWORD': '123456', # 密码
'HOST': '192.168.1.100', # IP
'PORT': '54321', # 端口
'OPTIONS': {
'threaded': True, # 多线程环境要开
},
}
}
几个关键点:
ENGINE必须写成django.db.backends.kingbase,大小写不能错- 数据库要提前建好,Django不会帮你建
- 如果用连接池或高并发,
threaded: True建议加上
2.2 提前建库
Django不会自动创建数据库,需要手动连上去建:
sql
CREATE DATABASE testdb;
建库的时候注意字符集,Django默认用UTF-8,不配也行。
2.3 用DSN字符串代替HOST和PORT
如果不想分开写HOST和PORT,可以把HOST和PORT都留空,在NAME里写完整连接串:
python
# 简单格式
'NAME': '192.168.1.100:54321/testdb'
# 完整DSN格式(连RAC时用)
'NAME': '(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.1.100)(PORT=54321))(CONNECT_DATA=(SERVICE_NAME=testdb)))'
三、迁移时的注意事项
3.1 表名和字段名的长度限制
金仓对名称长度限制是30个字符。Django自动生成的表名有时会超,比如appname_very_long_model_name。方言包的处理方式是:超长就截断,最后4个字符用MD5哈希值替换。
如果不想让Django改你的表名,可以用db_table强制指定:
python
class LongModelName(models.Model):
name = models.CharField(max_length=50)
class Meta:
# 用双引号包起来,Django就不做转换了
db_table = '"my_custom_table_name"'
3.2 字段名避开关键字
Django会自己处理SQL注入问题,但如果字段名刚好是金仓的关键字,还是会翻车。特别要注意这几个:date、timestamp、number、float。
python
# 容易出问题的写法
class Event(models.Model):
date = models.DateField() # date是关键字
timestamp = models.DateTimeField() # timestamp也是关键字
改成别的名字,或者手动指定db_column:
python
class Event(models.Model):
event_date = models.DateField(db_column='event_date')
3.3 NULL和空字符串踩坑
Django习惯用空字符串''表示空值,但金仓把空字符串和NULL当成两回事。方言包的处理逻辑是:
- 建表时,字段如果允许空字符串,会隐式加上
null=True - 读数据时,如果字段值是NULL,自动转成空字符串
大多数时候不用操心,但如果有业务逻辑依赖区分None和'',在Django里可能不好实现。一个绕过的方式是给字段设个默认值,比如default='unknown'。
3.4 TextField的限制
金仓的TextField在底层是NCLOB类型,这东西有几个限制:
- 不能做主键
- 不能建索引
- 不能用在DISTINCT查询里
如果模型里有TextField,又调用了distinct(),会直接报错。绕过去的方法是查的时候先去掉TextField:
python
# 会报错
User.objects.values('name', 'bio').distinct()
# 先defer掉TextField字段
User.objects.defer('bio').values('name').distinct()
四、连接池和性能
4.1 持久连接配置
Django默认每次请求结束就关连接。如果并发量上来,频繁开关连接开销不小。可以考虑开启持久连接:
python
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.kingbase',
'NAME': 'testdb',
'USER': 'system',
'PASSWORD': '123456',
'HOST': '192.168.1.100',
'PORT': '54321',
'CONN_MAX_AGE': 600, # 连接保持600秒
}
}
CONN_MAX_AGE按秒算,设成600就是10分钟。如果数据库服务器有空闲连接超时策略,这个值要设得比数据库的小一些。
注意:Django自带开发服务器每个请求新建一个线程,持久连接反而会建很多连接,开发环境别开。
4.2 多线程配置
在生产环境跑Django,比如用uWSGI或Gunicorn,每个worker是一个独立进程。要加上threaded: True:
python
'OPTIONS': {
'threaded': True,
}
这个参数告诉驱动使用线程安全的连接模式。不加的话,高并发时候可能会崩。
五、代码示例
配置文件没问题之后,Django的ORM用法和连PostgreSQL/MySQL完全一样。下面是一个完整的例子。
python
from django.db import models
class User(models.Model):
name = models.CharField(max_length=50, unique=True)
age = models.IntegerField()
email = models.EmailField()
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'app_user' # 表名不要超30字符
ordering = ['-created_at']
def __str__(self):
return f"{self.name}({self.age})"
执行迁移:
bash
python manage.py makemigrations
python manage.py migrate
如果迁移时报错说表已存在,检查一下Meta.db_table是不是和已有表重了。
增删改查操作:
python
# 插入
user = User(name='张三', age=25, email='zhangsan@example.com')
user.save()
# 批量插入
User.objects.bulk_create([
User(name='李四', age=30, email='lisi@example.com'),
User(name='王五', age=28, email='wangwu@example.com'),
])
# 查询
users = User.objects.filter(age__gte=18).order_by('-age')
for u in users:
print(f"{u.name} - {u.age}岁")
# 更新
User.objects.filter(name='张三').update(age=26)
# 删除
User.objects.filter(age__lt=18).delete()
原生SQL(方言包支持):
python
from django.db import connection
with connection.cursor() as cursor:
cursor.execute("SELECT * FROM app_user WHERE age > %s", [18])
rows = cursor.fetchall()
for row in rows:
print(row)
六、常见问题排查
驱动装不上
- Linux下报错缺少
pg_config,实际上金仓驱动需要的是ksycopg2,不是psycopg2。确认是不是装错了包名。 - Windows下报编译错误,直接装预编译的
ksycopg2-win64,别从源码编译。
迁移时报"relation already exists"
可能是之前迁移失败留下残留表。手动删了表再重新迁移,或者python manage.py migrate --fake标记为已完成。
连接数太多
持久连接+多线程+高并发,每个线程都会保持一个连接。适当调低CONN_MAX_AGE,或者限制Django Worker数量。
时区问题
Django的USE_TZ=True默认打开。如果金仓数据库存的是无时区时间,查出来会和预期差8小时。要么统一用带时区字段,要么设置USE_TZ = False。
七、总结
金仓对Django的适配做得比较完整,方言包覆盖了大部分常用场景:
- 迁移和ORM基本都能正常工作
- 持多线程和连接池
- 表名、字段名的转换规则比较灵活
- 对金仓特有的类型(如CLOB)做了一层封装
需要注意的地方主要是长度限制、TextFiled的限制、NULL和空字符串的差异。把这些提前了解清楚,基本不会卡住。
如果你的团队用Django做Web开发,数据库要换金仓,不用太担心。找个测试环境先跑一遍,把项目里超长的表名和关键字字段名处理一下,其他大概率能平滑迁移。