在Django中使用CrateDB作为时序数据库遇兼容性及函数报错问题
问题
在Docker环境下尝试将CrateDB作为Django的时序数据库使用,初始部署版本:
- Django: 4.1.3
- CrateDB: 5.1.0
出现兼容性错误:
django.db.utils.NotSupportedError: PostgreSQL 11 or later is required (found 10.5)
将Django降级至3.0后,兼容性错误消失,但出现新的内部错误:
System check identified no issues (0 silenced). Exception in thread django-main-thread: Traceback (most recent call last): File "/usr/local/lib/python3.9/site-packages/django/db/backends/utils.py", line 82, in _execute return self.cursor.execute(sql) psycopg2.errors.InternalError_: Unknown function: pg_catalog.pg_table_is_visible(c.oid) CONTEXT: io.crate.exceptions.SQLExceptions.esToCrateException(SQLExceptions.java:164) io.crate.exceptions.SQLExceptions.prepareForClientTransmission(SQLExceptions.java:151) io.crate.protocols.postgres.Messages.sendErrorResponse(Messages.java:190) io.crate.protocols.postgres.PostgresWireProtocol.handleSingleQuery(PostgresWireProtocol.java:795) io.crate.protocols.postgres.PostgresWireProtocol.lambda$handleSimpleQuery$3(PostgresWireProtocol.java:748) java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187) java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2309) io.crate.protocols.postgres.PostgresWireProtocol.handleSimpleQuery(PostgresWireProtocol.java:748) io.crate.protocols.postgres.PostgresWireProtocol$MessageHandler.dispatchMessage(PostgresWireProtocol.java:335) io.crate.protocols.postgres.PostgresWireProtocol$MessageHandler.dispatchState(PostgresWireProtocol.java:325) io.crate.protocols.postgres.PostgresWireProtocol$MessageHandler.channelRead0(PostgresWireProtocol.java:293) io.crate.protocols.postgres.PostgresWireProtocol$MessageHandler.channelRead0(PostgresWireProtocol.java:277) io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99) io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:336) io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:308) io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/usr/local/lib/python3.9/threading.py", line 980, in _bootstrap_inner self.run() File "/usr/local/lib/python3.9/threading.py", line 917, in run self._target(*self._args, **self._kwargs) File "/usr/local/lib/python3.9/site-packages/django/utils/autoreload.py", line 64, in wrapper fn(*args, **kwargs) File "/usr/local/lib/python3.9/site-packages/django/core/management/commands/runserver.py", line 121, in inner_run self.check_migrations() File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 486, in check_migrations executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS]) File "/usr/local/lib/python3.9/site-packages/django/db/migrations/executor.py", line 18, in __init__ self.loader = MigrationLoader(self.connection) File "/usr/local/lib/python3.9/site-packages/django/db/migrations/loader.py", line 53, in __init__ self.build_graph() File "/usr/local/lib/python3.9/site-packages/django/db/migrations/loader.py", line 220, in build_graph self.applied_migrations = recorder.applied_migrations() File "/usr/local/lib/python3.9/site-packages/django/db/migrations/recorder.py", line 77, in applied_migrations if self.has_table(): File "/usr/local/lib/python3.9/site-packages/django/db/migrations/recorder.py", line 56, in has_table tables = self.connection.introspection.table_names(cursor) File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/introspection.py", line 52, in table_names return get_names(cursor) File "/usr/local/lib/python3.9/site-packages/django/db/backends/base/introspection.py", line 47, in get_names return sorted(ti.name for ti in self.get_table_list(cursor) File "/usr/local/lib/python3.9/site-packages/django/db/backends/postgresql/introspection.py", line 49, in get_table_list cursor.execute(""" File "/usr/local/lib/python3.9/site-packages/django/db/backends/utils.py", line 98, in execute return super().execute(sql, params) File "/usr/local/lib/python3.9/site-packages/django/db/backends/utils.py", line 66, in execute return self._execute_with_wrappers(sql, params, many=False, executor=self._execute) File "/usr/local/lib/python3.9/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers return executor(sql, params, many, context) File "/usr/local/lib/python3.9/site-packages/django/db/backends/utils.py", line 84, in _execute return self.cursor.execute(sql, params) File "/usr/local/lib/python3.9/site-packages/django/db/utils.py", line 90, in __exit__ raise dj_exc_value.with_traceback(traceback) from exc_value File "/usr/local/lib/python3.9/site-packages/django/db/backends/utils.py", line 82, in _execute return self.cursor.execute(sql) django.db.utils.InternalError: Unknown function: pg_catalog.pg_table_is_visible(c.oid) CONTEXT: io.crate.exceptions.SQLExceptions.esToCrateException(SQLExceptions.java:164) io.crate.exceptions.SQLExceptions.prepareForClientTransmission(SQLExceptions.java:151) io.crate.protocols.postgres.Messages.sendErrorResponse(Messages.java:190) io.crate.protocols.postgres.PostgresWireProtocol.handleSingleQuery(PostgresWireProtocol.java:795) io.crate.protocols.postgres.PostgresWireProtocol.lambda$handleSimpleQuery$3(PostgresWireProtocol.java:748) java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187) java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2309) io.crate.protocols.postgres.PostgresWireProtocol.handleSimpleQuery(PostgresWireProtocol.java:748) io.crate.protocols.postgres.PostgresWireProtocol$MessageHandler.dispatchMessage(PostgresWireProtocol.java:335) io.crate.protocols.postgres.PostgresWireProtocol$MessageHandler.dispatchState(PostgresWireProtocol.java:325) io.crate.protocols.postgres.PostgresWireProtocol$MessageHandler.channelRead0(PostgresWireProtocol.java:293) io.crate.protocols.postgres.PostgresWireProtocol$MessageHandler.channelRead0(PostgresWireProtocol.java:277) io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99) io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:336) io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:308) io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
解决建议
使用CrateDB官方适配的Django后端
不要直接用PostgreSQL后端连接CrateDB,CrateDB提供了专门的Django数据库后端django-crate,安装后配置数据库引擎为crate,能避免PostgreSQL特有的函数调用问题,比如pg_table_is_visible这类CrateDB不支持的PostgreSQL函数。调整Django与CrateDB版本匹配
- 若坚持用PostgreSQL后端,需找版本兼容组合:CrateDB 5.x兼容PostgreSQL 10.x协议,所以Django版本不能要求PostgreSQL 11+,比如选Django 3.2.x(LTS版本),它对PostgreSQL的最低要求是9.6;同时需确认该版本Django的数据库自省逻辑是否依赖
pg_table_is_visible,若仍依赖则需自定义自省类覆盖相关方法。 - 升级CrateDB到5.3+版本,部分高版本可能增加了对更多PostgreSQL函数的支持,需参考官方文档确认兼容说明。
- 若坚持用PostgreSQL后端,需找版本兼容组合:CrateDB 5.x兼容PostgreSQL 10.x协议,所以Django版本不能要求PostgreSQL 11+,比如选Django 3.2.x(LTS版本),它对PostgreSQL的最低要求是9.6;同时需确认该版本Django的数据库自省逻辑是否依赖
自定义数据库自省类
针对Django 3.0的PostgreSQL自省调用pg_table_is_visible的问题,可自定义自省类重写get_table_list方法,移除对该函数的调用:
from django.db.backends.postgresql.introspection import DatabaseIntrospection class CrateDBIntrospection(DatabaseIntrospection): def get_table_list(self, cursor): # 重写方法,移除pg_table_is_visible调用 cursor.execute(""" SELECT c.relname, CASE WHEN c.relkind = 'v' THEN 'v' ELSE 't' END AS table_type FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN ('r', 'v', 'm') AND n.nspname NOT IN ('pg_catalog', 'pg_toast') AND n.nspname !~ '^pg_temp_' """) return [self.TableInfo(row[0], row[1]) for row in cursor.fetchall()]
然后在数据库配置中指定该自省类:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'your_db', 'USER': 'crate', 'PASSWORD': '', 'HOST': 'crate-db', 'PORT': '5432', 'OPTIONS': { 'introspection_class': 'your_app.path.to.CrateDBIntrospection', }, } }
- 禁用Django迁移检查(临时方案)
开发阶段临时测试可跳过迁移检查,启动命令添加--skip-checks参数:
python manage.py runserver --skip-checks
注意该方案不适合生产环境。
内容的提问来源于stack exchange,提问作者sfl0r3nz05
相关产品推荐
相关产品推荐

