1111from django .core import exceptions
1212from django .db import connection , transaction
1313from django .db import models
14- from django .db .models import Index
1514from django .db .models .query import QuerySet
1615
1716from django_pgviews .db import get_fields_by_name
@@ -100,6 +99,55 @@ def _concurrent_index_name(view_name, concurrent_index):
10099 return view_name + "_" + "_" .join ([s .strip () for s in concurrent_index .split ("," )]) + "_index"
101100
102101
102+ def _create_concurrent_index (cursor , view_name , concurrent_index ):
103+ cursor .execute (
104+ "CREATE UNIQUE INDEX {index_name} ON {view_name} ({concurrent_index})" .format (
105+ view_name = view_name ,
106+ index_name = _concurrent_index_name (view_name , concurrent_index ),
107+ concurrent_index = concurrent_index ,
108+ )
109+ )
110+
111+
112+ def _ensure_indexes (connection , cursor , view_cls , schema_name_log ):
113+ """
114+ This function gets called when a materialized view is deemed not needing a re-create. That is however only a part
115+ of the story, since that checks just the SQL of the view itself. The second part is the indexes.
116+ This function gets the current indexes on the materialized view and reconciles them with the indexes that
117+ should be in the view, dropping extra ones and creating new ones.
118+ """
119+ view_name = view_cls ._meta .db_table
120+ concurrent_index = view_cls ._concurrent_index
121+ indexes = view_cls ._meta .indexes
122+ vschema , vname = _schema_and_name (connection , view_name )
123+
124+ cursor .execute ("SELECT indexname FROM pg_indexes WHERE tablename = %s AND schemaname = %s" , [vname , vschema ])
125+
126+ existing_indexes = set (x [0 ] for x in cursor .fetchall ())
127+ required_indexes = set (x .name for x in indexes )
128+
129+ if view_cls ._concurrent_index is not None :
130+ concurrent_index_name = _concurrent_index_name (view_name , concurrent_index )
131+ required_indexes .add (concurrent_index_name )
132+ else :
133+ concurrent_index_name = None
134+
135+ for index_name in existing_indexes - required_indexes :
136+ cursor .execute (f"DROP INDEX { index_name } " )
137+ log .info ("pgview dropped index %s on view %s (%s)" , index_name , view_name , schema_name_log )
138+
139+ for index_name in required_indexes - existing_indexes :
140+ if index_name == concurrent_index_name :
141+ _create_concurrent_index (cursor , view_name , concurrent_index )
142+ log .info ("pgview created concurrent index on view %s (%s)" , view_name , schema_name_log )
143+ else :
144+ for index in indexes :
145+ if index .name == index_name :
146+ connection .schema_editor ().add_index (view_cls , index )
147+ log .info ("pgview created index %s on view %s (%s)" , index .name , view_name , schema_name_log )
148+ break
149+
150+
103151@transaction .atomic ()
104152def create_materialized_view (connection , view_cls , check_sql_changed = False ):
105153 """
@@ -155,6 +203,7 @@ def create_materialized_view(connection, view_cls, check_sql_changed=False):
155203 _drop_mat_view (cursor , temp_viewname )
156204
157205 if definitions [0 ] == definitions [1 ]:
206+ _ensure_indexes (connection , cursor , view_cls , schema_name_log )
158207 return "EXISTS"
159208
160209 if view_exists :
@@ -165,13 +214,7 @@ def create_materialized_view(connection, view_cls, check_sql_changed=False):
165214 log .info ("pgview created materialized view %s (%s)" , view_name , schema_name_log )
166215
167216 if concurrent_index is not None :
168- cursor .execute (
169- "CREATE UNIQUE INDEX {index_name} ON {view_name} ({concurrent_index})" .format (
170- view_name = view_name ,
171- index_name = _concurrent_index_name (view_name , concurrent_index ),
172- concurrent_index = concurrent_index ,
173- )
174- )
217+ _create_concurrent_index (cursor , view_name , concurrent_index )
175218 log .info ("pgview created concurrent index on view %s (%s)" , view_name , schema_name_log )
176219
177220 if view_cls ._meta .indexes :
0 commit comments