使用Oracle数据库的Django inspectdb问题

时间:2011-02-06 17:04:20

标签: python database django oracle inspectdb

安装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

settings.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': ''
        }
}

5 个答案:

答案 0 :(得分:1)

两件事:

  1. Inspectdb没有正式支持oracle(请参阅Django Docs - inspectdb)悲伤的时候。
  2. Django对Oracle模式没有很强的支持(参见unresolved Django ticket 6148)如果你能够使用主要用户连接模式,那么你可能会有更好的运气,使你希望反省的模式默认架构。
  3. 我可以通过更改introspection.py中的select来获得基本模型文件输出。对我来说,我改变了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

答案 1 :(得分:0)

它对我有用。

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

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

答案 2 :(得分:0)

Haaaa ..正好面对这个问题,发现了一个愚蠢的理由!!

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

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

答案 3 :(得分: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()]

答案 4 :(得分:0)

在我最近的搜索中,我用Django 2.0和我正在使用的Oracle 11g遗留数据库来解决inspectdb,我已经采取了修复内省的方法,到目前为止我和# 39;修改/lib/python3.6/site-packages/django/db/backends/oracle/introspection.py后,设法获得基本表的输出: (基本上替换all_ *表的所有user_ *表)

我目前正在使用的解决方案是下面的文件内容(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 instead of 12c?获取的内容,这促使我查看1.11代码库以查看更改内容(https://github.com/django/django/blob/stable/1.11.x/django/db/backends/oracle/introspection.py

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