Refs #29722 -- Added introspection of materialized views for PostgreSQL.
This commit is contained in:
parent
45ef3df7d0
commit
bf8b625a3b
|
@ -44,11 +44,11 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
|
|||
SELECT c.relname, c.relkind
|
||||
FROM pg_catalog.pg_class c
|
||||
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
||||
WHERE c.relkind IN ('f', 'r', 'v')
|
||||
WHERE c.relkind IN ('f', 'm', 'r', 'v')
|
||||
AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
|
||||
AND pg_catalog.pg_table_is_visible(c.oid)
|
||||
""")
|
||||
mapping = {'f': 't', 'r': 't', 'v': 'v'}
|
||||
mapping = {'f': 't', 'm': 'v', 'r': 't', 'v': 'v'}
|
||||
return [
|
||||
TableInfo(row[0], mapping[row[1]])
|
||||
for row in cursor.fetchall() if row[0] not in self.ignored_tables
|
||||
|
@ -59,18 +59,27 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
|
|||
Return a description of the table with the DB-API cursor.description
|
||||
interface.
|
||||
"""
|
||||
# As cursor.description does not return reliably the nullable property,
|
||||
# we have to query the information_schema (#7783)
|
||||
# Query the pg_catalog tables as cursor.description does not reliably
|
||||
# return the nullable property and information_schema.columns does not
|
||||
# contain details of materialized views.
|
||||
cursor.execute("""
|
||||
SELECT column_name, is_nullable, column_default
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = %s""", [table_name])
|
||||
SELECT
|
||||
a.attname AS column_name,
|
||||
NOT (a.attnotnull OR (t.typtype = 'd' AND t.typnotnull)) AS is_nullable,
|
||||
pg_get_expr(ad.adbin, ad.adrelid) AS column_default
|
||||
FROM pg_attribute a
|
||||
LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum
|
||||
JOIN pg_type t ON a.atttypid = t.oid
|
||||
JOIN pg_class c ON a.attrelid = c.oid
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE c.relkind IN ('f', 'm', 'r', 'v')
|
||||
AND c.relname = %s
|
||||
AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
|
||||
AND pg_catalog.pg_table_is_visible(c.oid)
|
||||
""", [table_name])
|
||||
field_map = {line[0]: line[1:] for line in cursor.fetchall()}
|
||||
cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name))
|
||||
return [
|
||||
FieldInfo(*line[0:6], field_map[line.name][0] == 'YES', field_map[line.name][1])
|
||||
for line in cursor.description
|
||||
]
|
||||
return [FieldInfo(*line[0:6], *field_map[line.name]) for line in cursor.description]
|
||||
|
||||
def get_sequences(self, cursor, table_name, table_fields=()):
|
||||
cursor.execute("""
|
||||
|
|
|
@ -402,10 +402,12 @@ PostgreSQL
|
|||
^^^^^^^^^^
|
||||
|
||||
* Models are created for foreign tables.
|
||||
* Models are created for materialized views if
|
||||
:option:`--include-views` is used.
|
||||
|
||||
.. versionchanged:: 2.2
|
||||
|
||||
Support for foreign tables was added.
|
||||
Support for foreign tables and materialized views was added.
|
||||
|
||||
.. django-admin-option:: --database DATABASE
|
||||
|
||||
|
|
|
@ -179,6 +179,9 @@ Management Commands
|
|||
|
||||
* :djadmin:`inspectdb` now creates models for foreign tables on PostgreSQL.
|
||||
|
||||
* :option:`inspectdb --include-views` now creates models for materialized views
|
||||
on PostgreSQL.
|
||||
|
||||
Migrations
|
||||
~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -310,6 +310,30 @@ class InspectDBTransactionalTests(TransactionTestCase):
|
|||
with connection.cursor() as cursor:
|
||||
cursor.execute('DROP VIEW inspectdb_people_view')
|
||||
|
||||
@skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific SQL')
|
||||
def test_include_materialized_views(self):
|
||||
"""inspectdb --include-views creates models for database materialized views."""
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(
|
||||
'CREATE MATERIALIZED VIEW inspectdb_people_materialized_view AS '
|
||||
'SELECT id, name FROM inspectdb_people'
|
||||
)
|
||||
out = StringIO()
|
||||
view_model = 'class InspectdbPeopleMaterializedView(models.Model):'
|
||||
view_managed = 'managed = False # Created from a view.'
|
||||
try:
|
||||
call_command('inspectdb', table_name_filter=inspectdb_tables_only, stdout=out)
|
||||
no_views_output = out.getvalue()
|
||||
self.assertNotIn(view_model, no_views_output)
|
||||
self.assertNotIn(view_managed, no_views_output)
|
||||
call_command('inspectdb', table_name_filter=inspectdb_tables_only, include_views=True, stdout=out)
|
||||
with_views_output = out.getvalue()
|
||||
self.assertIn(view_model, with_views_output)
|
||||
self.assertIn(view_managed, with_views_output)
|
||||
finally:
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute('DROP MATERIALIZED VIEW IF EXISTS inspectdb_people_materialized_view')
|
||||
|
||||
@skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific SQL')
|
||||
def test_foreign_data_wrapper(self):
|
||||
with connection.cursor() as cursor:
|
||||
|
|
Loading…
Reference in New Issue