1

安装 cx_oracle 并运行 inspectdb。似乎没有任何输出?有人可以帮忙吗?在 Oracle 中使用 inspectdb 是否存在已知问题?

下面是命令和settings.py。

python manage.py inspectdb --database xxx_db

# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
#     * Rearrange models' order
#     * Make sure each model has one field with primary_key=True
# Feel free to rename the models, but don't rename db_table values or field names.
#
# Also note: You'll have to insert the output of 'django-admin.py sqlcustom [appname]'
# into your database.

from django.db import models

设置.py

DATABASES = {
    'xxx_db': {
        'ENGINE': 'django.db.backends.oracle',
        'NAME': 'abc',
        'USER': 'abc_read',
        'PASSWORD': 'abc_read',
        'HOST': 'apps.domain.com',
        'PORT': 'xxxx'
        },
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'aaaa',
        'USER': 'aaaa',
        'PASSWORD': 'xxxx',
        'HOST': '/tmp/mysql.sock',
        'PORT': ''
        }
}
4

5 回答 5

1

两件事情:

  1. Inspectdb 不正式支持 oracle(请参阅Django Docs - inspectdb)悲伤时期。
  2. Django 对 Oracle 模式的支持不是很强(请参阅未解决的 Django 票证 6148)因此,如果您能够使用模式的主用户进行连接,那么您可能会有更好的运气,从而使您希望内省的模式成为默认模式。

通过更改 introspection.py 中的选择,我能够获得基本的模型文件输出。对我来说,我将 django/db/backends/oracle/introspection.py (第 40 行左右)中的 get_table_list 函数从:

def get_table_list(self, cursor):
    "Returns a list of table names in the current database."
    cursor.execute("SELECT TABLE_NAME FROM USER_TABLES")
    return [row[0].lower() for row in cursor.fetchall()]

def get_table_list(self, cursor):
    "Returns a list of table names in the current database."
    cursor.execute("SELECT TABLE_NAME FROM ALL_TABLES WHERE OWNER = 'SCHEMA_TO_QUERY'")
    return [row[0].lower() for row in cursor.fetchall()]

但是当我读到 Oracle 中对模式的整体支持很差时放弃了 django

于 2012-02-20T20:51:04.180 回答
0

这个对我有用。

您是否检查了用户有权查看 Oracle 中的所有表?

无论如何,我很好奇inspectdb 使用的SQL 是什么。

于 2011-06-17T16:39:55.547 回答
0

@Plecebo 在正确的轨道上。get_table_list 方法是问题的根源,但给出的 Select 语句不起作用。

我暂时对表名进行了硬编码以快速获得我需要的内容:

(django/db/backends/oracle/introspection.py 第 40 行)

def get_table_list(self, cursor):
    "Returns a list of table names in the current database."
    #cursor.execute("SELECT TABLE_NAME FROM USER_TABLES")
    return ['table_name1', 'table_name2']
    #return [row[0].lower() for row in cursor.fetchall()]
于 2012-07-09T20:34:09.730 回答
0

由于这出现在我最近搜索inspectdbDjango 2.0 和我正在使用的 Oracle 11g 遗留数据库中,我已经开始修复内省,到目前为止,我已经设法在修改后获得基本表的输出/lib/python3.6/site-packages/django/db/backends/oracle/introspection.py: (基本上将所有 user_* 表替换为 all_* 表)

我目前的工作解决方案是下面的文件内容(introspection.py)。

import warnings
from collections import namedtuple

import cx_Oracle

from django.db import models
from django.db.backends.base.introspection import (
    BaseDatabaseIntrospection, FieldInfo as BaseFieldInfo, TableInfo,
)
from django.utils.deprecation import RemovedInDjango21Warning

FieldInfo = namedtuple('FieldInfo', BaseFieldInfo._fields + ('is_autofield',))


class DatabaseIntrospection(BaseDatabaseIntrospection):
    # Maps type objects to Django Field types.
    data_types_reverse = {
        cx_Oracle.BLOB: 'BinaryField',
        cx_Oracle.CLOB: 'TextField',
        cx_Oracle.DATETIME: 'DateField',
        cx_Oracle.FIXED_CHAR: 'CharField',
        cx_Oracle.FIXED_NCHAR: 'CharField',
        cx_Oracle.NATIVE_FLOAT: 'FloatField',
        cx_Oracle.NCHAR: 'CharField',
        cx_Oracle.NCLOB: 'TextField',
        cx_Oracle.NUMBER: 'DecimalField',
        cx_Oracle.STRING: 'CharField',
        cx_Oracle.TIMESTAMP: 'DateTimeField',
    }

    cache_bust_counter = 1

    def get_field_type(self, data_type, description):
        if data_type == cx_Oracle.NUMBER:
            precision, scale = description[4:6]
            if scale == 0:
                if precision > 11:
                    return 'BigAutoField' if description.is_autofield else 'BigIntegerField'
                elif precision == 1:
                    return 'BooleanField'
                elif description.is_autofield:
                    return 'AutoField'
                else:
                    return 'IntegerField'
            elif scale == -127:
                return 'FloatField'

        return super().get_field_type(data_type, description)

    def get_table_list(self, cursor):
        """Return a list of table and view names in the current database."""
        # cursor.execute("SELECT TABLE_NAME, 't' FROM USER_TABLES UNION ALL "
        #                "SELECT VIEW_NAME, 'v' FROM USER_VIEWS")
        cursor.execute("SELECT TABLE_NAME, 't' FROM ALL_TABLES WHERE OWNER = 'V500' ")
        return [TableInfo(row[0].lower(), row[1]) for row in cursor.fetchall()]

    def get_table_description(self, cursor, table_name):
        """
        Return a description of the table with the DB-API cursor.description
        interface.
        """
        cursor.execute("""
            SELECT
                column_name,
                data_default,
                CASE
                    WHEN char_used IS NULL THEN data_length
                    ELSE char_length
                END as internal_size,
                0 as is_autofield
            FROM ALL_TAB_COLUMNS
            WHERE table_name = UPPER(%s)""", [table_name])
        field_map = {
            column: (internal_size, default if default != 'NULL' else None, is_autofield)
            for column, default, internal_size, is_autofield in cursor.fetchall()
        }
        self.cache_bust_counter += 1
        cursor.execute("SELECT * FROM {} WHERE ROWNUM < 2 AND {} > 0".format(
            self.connection.ops.quote_name(table_name),
            self.cache_bust_counter))
        description = []
        for desc in cursor.description:
            name = desc[0]
            internal_size, default, is_autofield = field_map[name]
            name = name % {}  # cx_Oracle, for some reason, doubles percent signs.
            description.append(FieldInfo(*(
                (name.lower(),) +
                desc[1:3] +
                (internal_size, desc[4] or 0, desc[5] or 0) +
                desc[6:] +
                (default, is_autofield)
            )))
        return description

    def table_name_converter(self, name):
        """Table name comparison is case insensitive under Oracle."""
        return name.lower()

    def get_sequences(self, cursor, table_name, table_fields=()):
        # Tables don't exist in 11g (this function added in django 2
        # cursor.execute("""
        #     SELECT
        #         user_tab_identity_cols.sequence_name,
        #         user_tab_identity_cols.column_name
        #     FROM
        #         user_tab_identity_cols,
        #         user_constraints,
        #         user_cons_columns cols
        #     WHERE
        #         user_constraints.constraint_name = cols.constraint_name
        #         AND user_constraints.table_name = user_tab_identity_cols.table_name
        #         AND cols.column_name = user_tab_identity_cols.column_name
        #         AND user_constraints.constraint_type = 'P'
        #         AND user_tab_identity_cols.table_name = UPPER(%s)
        # """, [table_name])
        # # Oracle allows only one identity column per table.
        # row = cursor.fetchone()
        # if row:
        #     return [{'name': row[0].lower(), 'table': table_name, 'column': row[1].lower()}]
        # # To keep backward compatibility for AutoFields that aren't Oracle
        # # identity columns.
        # for f in table_fields:
        #     if isinstance(f, models.AutoField):
        #         return [{'table': table_name, 'column': f.column}]
        return []

    def get_relations(self, cursor, table_name):
        """
        Return a dictionary of {field_name: (field_name_other_table, other_table)}
        representing all relationships to the given table.
        """
        table_name = table_name.upper()
        cursor.execute("""
    SELECT ca.column_name, cb.table_name, cb.column_name
    FROM   ALL_CONSTRAINTS, ALL_CONS_COLUMNS ca, ALL_CONS_COLUMNS cb
    WHERE  ALL_CONSTRAINTS.table_name = %s AND
           ALL_CONSTRAINTS.constraint_name = ca.constraint_name AND
           ALL_CONSTRAINTS.r_constraint_name = cb.constraint_name AND
           ca.position = cb.position""", [table_name])

        relations = {}
        for row in cursor.fetchall():
            relations[row[0].lower()] = (row[2].lower(), row[1].lower())
        return relations

    def get_key_columns(self, cursor, table_name):
        cursor.execute("""
            SELECT ccol.column_name, rcol.table_name AS referenced_table, rcol.column_name AS referenced_column
            FROM ALL_CONSTRAINTS c
            JOIN ALL_CONS_COLUMNS ccol
              ON ccol.constraint_name = c.constraint_name
            JOIN ALL_CONS_COLUMNS rcol
              ON rcol.constraint_name = c.r_constraint_name
            WHERE c.table_name = %s AND c.constraint_type = 'R'""", [table_name.upper()])
        return [tuple(cell.lower() for cell in row)
                for row in cursor.fetchall()]

    def get_indexes(self, cursor, table_name):
        warnings.warn(
            "get_indexes() is deprecated in favor of get_constraints().",
            RemovedInDjango21Warning, stacklevel=2
        )
        sql = """
    SELECT LOWER(uic1.column_name) AS column_name,
           CASE ALL_CONSTRAINTS.constraint_type
               WHEN 'P' THEN 1 ELSE 0
           END AS is_primary_key,
           CASE ALL_INDEXES.uniqueness
               WHEN 'UNIQUE' THEN 1 ELSE 0
           END AS is_unique
    FROM   ALL_CONSTRAINTS, ALL_INDEXES, ALL_IND_COLUMNS uic1
    WHERE  ALL_CONSTRAINTS.constraint_type (+) = 'P'
      AND  ALL_CONSTRAINTS.index_name (+) = uic1.index_name
      AND  ALL_INDEXES.uniqueness (+) = 'UNIQUE'
      AND  ALL_INDEXES.index_name (+) = uic1.index_name
      AND  uic1.table_name = UPPER(%s)
      AND  uic1.column_position = 1
      AND  NOT EXISTS (
              SELECT 1
              FROM   ALL_IND_COLUMNS uic2
              WHERE  uic2.index_name = uic1.index_name
                AND  uic2.column_position = 2
           )
        """
        cursor.execute(sql, [table_name])
        indexes = {}
        for row in cursor.fetchall():
            indexes[row[0]] = {'primary_key': bool(row[1]),
                               'unique': bool(row[2])}
        return indexes

    def get_constraints(self, cursor, table_name):
        """
        Retrieve any constraints or keys (unique, pk, fk, check, index) across
        one or more columns.
        """
        constraints = {}
        # Loop over the constraints, getting PKs, uniques, and checks
        cursor.execute("""
            SELECT
                ALL_CONSTRAINTS.constraint_name,
                LISTAGG(LOWER(cols.column_name), ',') WITHIN GROUP (ORDER BY cols.position),
                CASE ALL_CONSTRAINTS.constraint_type
                    WHEN 'P' THEN 1
                    ELSE 0
                END AS is_primary_key,
                CASE
                    WHEN ALL_CONSTRAINTS.constraint_type IN ('P', 'U') THEN 1
                    ELSE 0
                END AS is_unique,
                CASE ALL_CONSTRAINTS.constraint_type
                    WHEN 'C' THEN 1
                    ELSE 0
                END AS is_check_constraint
            FROM
                ALL_CONSTRAINTS
            LEFT OUTER JOIN
                ALL_CONS_COLUMNS cols ON ALL_CONSTRAINTS.constraint_name = cols.constraint_name
            WHERE
                ALL_CONSTRAINTS.constraint_type = ANY('P', 'U', 'C')
                AND ALL_CONSTRAINTS.table_name = UPPER(%s)
            GROUP BY ALL_CONSTRAINTS.constraint_name, ALL_CONSTRAINTS.constraint_type
        """, [table_name])
        for constraint, columns, pk, unique, check in cursor.fetchall():
            constraints[constraint] = {
                'columns': columns.split(','),
                'primary_key': pk,
                'unique': unique,
                'foreign_key': None,
                'check': check,
                'index': unique,  # All uniques come with an index
            }
        # Foreign key constraints
        cursor.execute("""
            SELECT
                cons.constraint_name,
                LISTAGG(LOWER(cols.column_name), ',') WITHIN GROUP (ORDER BY cols.position),
                LOWER(rcols.table_name),
                LOWER(rcols.column_name)
            FROM
                ALL_CONSTRAINTS cons
            INNER JOIN
                ALL_CONS_COLUMNS rcols ON rcols.constraint_name = cons.r_constraint_name AND rcols.position = 1
            LEFT OUTER JOIN
                ALL_CONS_COLUMNS cols ON cons.constraint_name = cols.constraint_name
            WHERE
                cons.constraint_type = 'R' AND
                cons.table_name = UPPER(%s)
            GROUP BY cons.constraint_name, rcols.table_name, rcols.column_name
        """, [table_name])
        for constraint, columns, other_table, other_column in cursor.fetchall():
            constraints[constraint] = {
                'primary_key': False,
                'unique': False,
                'foreign_key': (other_table, other_column),
                'check': False,
                'index': False,
                'columns': columns.split(','),
            }
        # Now get indexes
        cursor.execute("""
            SELECT
                ind.index_name,
                LOWER(ind.index_type),
                LISTAGG(LOWER(cols.column_name), ',') WITHIN GROUP (ORDER BY cols.column_position),
                LISTAGG(cols.descend, ',') WITHIN GROUP (ORDER BY cols.column_position)
            FROM
                ALL_IND_COLUMNS cols, ALL_INDEXES ind
            WHERE
                cols.table_name = UPPER(%s) AND
                NOT EXISTS (
                    SELECT 1
                    FROM ALL_CONSTRAINTS cons
                    WHERE ind.index_name = cons.index_name
                ) AND cols.index_name = ind.index_name
            GROUP BY ind.index_name, ind.index_type
        """, [table_name])
        for constraint, type_, columns, orders in cursor.fetchall():
            constraints[constraint] = {
                'primary_key': False,
                'unique': False,
                'foreign_key': None,
                'check': False,
                'index': True,
                'type': 'idx' if type_ == 'normal' else type_,
                'columns': columns.split(','),
                'orders': orders.split(','),
            }
        return constraints

注意到 Django 1.11 是正式支持 11g 的最后一个版本,这是我从How to make Django 2.0 to use Oracle 11g syntax 而不是 12c? ,这促使我查看 1.11 代码库以了解发生了什么变化(https://github.com/django/django/blob/stable/1.11.x/django/db/backends/oracle/introspection.py

目前我正在从只读数据库连接读取数据,因此修复迁移不是我目前的动力。在编写 inspectdb 函数时,将 6000 多个表作为 python 代码输出,没有任何重大问题。

于 2018-06-07T09:50:14.930 回答
-1

Haaaa .. 只是面对这个问题,找到了一个愚蠢的理由!

无需像其中一篇文章那样编辑任何底层文件。当您在该数据库中没有表时,您将面临此问题。呵呵..

Create a few tables and then try. 它就像一个魅力。

于 2012-04-19T20:02:19.107 回答