structure returned by databaseintrospection gettablelist structure returned by the dbapi cursor description interface pep 249 encapsulate backendspecific introspection utilities datatypesreverse def initself connection self connection connection def getfieldtypeself datatype description return self datatypesreversedatatype def identifierconverterself name return name def tablenamesself cursornone includeviewsfalse def getnamescursor return sorted ti name for ti in self gettablelistcursor if includeviews or ti type t if cursor is none with self connection cursor as cursor return getnamescursor return getnamescursor def gettablelistself cursor raise notimplementederror subclasses of basedatabaseintrospection may require a gettablelist method def gettabledescriptionself cursor tablename raise notimplementederror subclasses of basedatabaseintrospection may require a gettabledescription method def getmigratablemodelsself from django apps import apps from django db import router return model for appconfig in apps getappconfigs for model in router getmigratablemodelsappconfig self connection alias if model meta canmigrateself connection def djangotablenamesself onlyexistingfalse includeviewstrue tables set for model in self getmigratablemodels if not model meta managed continue tables addmodel meta dbtable tables update f m2mdbtable for f in model meta localmanytomany if f remotefield through meta managed tables listtables if onlyexisting existingtables setself tablenamesincludeviewsincludeviews tables t for t in tables if self identifierconvertert in existingtables return tables def installedmodelsself tables tables setmapself identifierconverter tables return m for m in self getmigratablemodels if self identifierconverterm meta dbtable in tables def sequencelistself sequencelist with self connection cursor as cursor for model in self getmigratablemodels if not model meta managed continue if model meta swapped continue sequencelist extend self getsequences cursor model meta dbtable model meta localfields for f in model meta localmanytomany if this is an m2m using an intermediate table we don t need to reset the sequence if f remotefield through meta autocreated sequence self getsequencescursor f m2mdbtable sequencelist extend sequence or table f m2mdbtable column none return sequencelist def getsequencesself cursor tablename tablefields raise notimplementederror subclasses of basedatabaseintrospection may require a getsequences method def getrelationsself cursor tablename raise notimplementederror subclasses of basedatabaseintrospection may require a getrelations method def getprimarykeycolumnself cursor tablename columns self getprimarykeycolumnscursor tablename return columns0 if columns else none def getprimarykeycolumnsself cursor tablename retrieve any constraints or keys unique pk fk check index across one or more columns return a dict mapping constraint names to their attributes where attributes is a dict with keys columns list of columns this covers primarykey true if primary key false otherwise unique true if this is a unique constraint false otherwise foreignkey table column of target or none check true if check constraint false otherwise index true if index false otherwise orders the order ascdesc defined for the columns of indexes type the type of the index btree hash etc some backends may return special constraint names that don t exist if they don t name constraints of a certain type e g sqlite structure returned by databaseintrospection get_table_list structure returned by the db api cursor description interface pep 249 encapsulate backend specific introspection utilities hook for a database backend to use the cursor description to match a django field type to a database column for oracle the column data_type on its own is insufficient to distinguish between a floatfield and integerfield for example apply a conversion to the identifier for the purposes of comparison the default identifier converter is for case sensitive comparison return a list of names of all tables that exist in the database sort the returned table list by python s default sorting do not use the database s order by here to avoid subtle differences in sorting order between databases return an unsorted list of tableinfo named tuples of all tables and views that exist in the database return a description of the table with the db api cursor description interface return a list of all table names that have associated django models and are in installed_apps if only_existing is true include only the tables in the database return a set of all models represented by the provided list of table names return a list of information about all db sequences for all models in all apps if this is an m2m using an intermediate table we don t need to reset the sequence return a list of introspected sequences for table_name each sequence is a dict table table_name column column_name an optional name key can be added if the backend supports named sequences return a dictionary of field_name field_name_other_table other_table representing all foreign keys in the given table return the name of the primary key column for the given table return a list of primary key columns for the given table retrieve any constraints or keys unique pk fk check index across one or more columns return a dict mapping constraint names to their attributes where attributes is a dict with keys columns list of columns this covers primary_key true if primary key false otherwise unique true if this is a unique constraint false otherwise foreign_key table column of target or none check true if check constraint false otherwise index true if index false otherwise orders the order asc desc defined for the columns of indexes type the type of the index btree hash etc some backends may return special constraint names that don t exist if they don t name constraints of a certain type e g sqlite
from collections import namedtuple


TableInfo = namedtuple("TableInfo", ["name", "type"])


FieldInfo = namedtuple(
    "FieldInfo",
    "name type_code display_size internal_size precision scale null_ok "
    "default collation",
)


class BaseDatabaseIntrospection:
    

    data_types_reverse = {}

    def __init__(self, connection):
        self.connection = connection

    def get_field_type(self, data_type, description):
        
        return self.data_types_reverse[data_type]

    def identifier_converter(self, name):
        
        return name

    def table_names(self, cursor=None, include_views=False):
        

        def get_names(cursor):
            return sorted(
                ti.name
                for ti in self.get_table_list(cursor)
                if include_views or ti.type == "t"
            )

        if cursor is None:
            with self.connection.cursor() as cursor:
                return get_names(cursor)
        return get_names(cursor)

    def get_table_list(self, cursor):
        
        raise NotImplementedError(
            "subclasses of BaseDatabaseIntrospection may require a get_table_list() "
            "method"
        )

    def get_table_description(self, cursor, table_name):
        
        raise NotImplementedError(
            "subclasses of BaseDatabaseIntrospection may require a "
            "get_table_description() method."
        )

    def get_migratable_models(self):
        from django.apps import apps
        from django.db import router

        return (
            model
            for app_config in apps.get_app_configs()
            for model in router.get_migratable_models(app_config, self.connection.alias)
            if model._meta.can_migrate(self.connection)
        )

    def django_table_names(self, only_existing=False, include_views=True):
        
        tables = set()
        for model in self.get_migratable_models():
            if not model._meta.managed:
                continue
            tables.add(model._meta.db_table)
            tables.update(
                f.m2m_db_table()
                for f in model._meta.local_many_to_many
                if f.remote_field.through._meta.managed
            )
        tables = list(tables)
        if only_existing:
            existing_tables = set(self.table_names(include_views=include_views))
            tables = [
                t for t in tables if self.identifier_converter(t) in existing_tables
            ]
        return tables

    def installed_models(self, tables):
        
        tables = set(map(self.identifier_converter, tables))
        return {
            m
            for m in self.get_migratable_models()
            if self.identifier_converter(m._meta.db_table) in tables
        }

    def sequence_list(self):
        
        sequence_list = []
        with self.connection.cursor() as cursor:
            for model in self.get_migratable_models():
                if not model._meta.managed:
                    continue
                if model._meta.swapped:
                    continue
                sequence_list.extend(
                    self.get_sequences(
                        cursor, model._meta.db_table, model._meta.local_fields
                    )
                )
                for f in model._meta.local_many_to_many:
                    
                    
                    if f.remote_field.through._meta.auto_created:
                        sequence = self.get_sequences(cursor, f.m2m_db_table())
                        sequence_list.extend(
                            sequence or [{"table": f.m2m_db_table(), "column": None}]
                        )
        return sequence_list

    def get_sequences(self, cursor, table_name, table_fields=()):
        
        raise NotImplementedError(
            "subclasses of BaseDatabaseIntrospection may require a get_sequences() "
            "method"
        )

    def get_relations(self, cursor, table_name):
        
        raise NotImplementedError(
            "subclasses of BaseDatabaseIntrospection may require a "
            "get_relations() method."
        )

    def get_primary_key_column(self, cursor, table_name):
        
        columns = self.get_primary_key_columns(cursor, table_name)
        return columns[0] if columns else None

    def get_primary_key_columns(self, cursor, table_name):
        
        for constraint in self.get_constraints(cursor, table_name).values():
            if constraint["primary_key"]:
                return constraint["columns"]
        return None

    def get_constraints(self, cursor, table_name):
        
        raise NotImplementedError(
            "subclasses of BaseDatabaseIntrospection may require a get_constraints() "
            "method"
        )