Проблема миграции материализованного представления Django при обновлении
Мы создаем библиотеку на Django (v4.0) с Postgres (v13). Эта библиотека будет ядром нашей компании, поэтому мы должны быть осторожны с миграциями. Проблема заключается в миграциях Django, когда мы обновляем базовую таблицу для любого материализованного представления.
- Модели:
- Базис
class Customer(models.Model):
external_id = models.CharField(max_length=50, unique=True)
class Meta:
db_table = "customer"
- Материализованный вид
class CustomerDetailView(models.Model):
id = models.CharField(max_length=50, primary_key=True)
external_id = models.CharField(max_length=50, unique=True)
class Meta:
managed = False
db_table = "customer_detail_view"
- Миграции:
- "0001_initial.py" Создан Django
class Migration(migrations.Migration):
...
operations = [
migrations.CreateModel(
name='CustomerDetailView',
fields=[
('id', models.CharField(max_length=50, primary_key=True, serialize=False)),
('external_id', models.CharField(blank=True, max_length=50)),
],
options={
'db_table': 'customer_detail_view',
'managed': False,
},
),
migrations.CreateModel(
name='Customer',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('external_id', models.CharField(blank=True, max_length=50, null=True, unique=True))
],
options={
'db_table': 'customer',
},
),
- "0002_customer_detail_view.py" Создан вручную
class Migration(migrations.Migration):
dependencies = [
("customers", "0001_initial"),
]
operations = [
migrations.RunSQL(
"""
DROP MATERIALIZED VIEW IF EXISTS customer_detail_view;
CREATE MATERIALIZED VIEW customer_detail_view AS
SELECT
cus.id,
cus.external_id,
FROM
customer cus;
CREATE UNIQUE INDEX customer_detail_view_pk ON customer_detail_view(external_id);
""",
"DROP MATERIALIZED VIEW customer_detail_view"
)
]
- "0003_alter_customer_external_id.py" Создан Django после фиктивной модификации базовой таблицы
class Migration(migrations.Migration):
dependencies = [
('customers', '0002_customer_detail_view'),
]
operations = [
migrations.AlterField(
model_name='customer',
name='external_id',
field=models.CharField(blank=True, max_length=60, null=True, unique=True),
),
]
- Ошибка
- Возникает, когда мы делаем
python manage.py migrate
Running migrations:
Applying customers.0003_alter_customer_external_id...Traceback (most recent call last):
File "/usr/app/venv/lib/python3.10/site-packages/django/db/backends/utils.py", line 89, in _execute
return self.cursor.execute(sql, params)
psycopg2.errors.FeatureNotSupported: cannot alter type of a column used by a view or rule
DETAIL: rule _RETURN on materialized view customer_detail_view depends on column "external_id"
Мы думаем, что проблема связана с миграцией "0001_initial.py", когда Django (несмотря на то, что материализованное представление является таблицей с managed=False
) создает миграцию, а затем, когда база обновляется, невозможно создать миграцию для материализованного представления.
Django по умолчанию не поддерживает миграции материализованных представлений. Поэтому миграции нужно писать вручную. Вы не сможете изменить модель Customer
с полем external_id
(в случае, если материализованное представление создано на основе модели Customer
и поля external_id
), пока не удалите материализованное представление. Вы можете создать пользовательскую миграцию для удаления материализованного представления и применить изменения модели, а затем снова создать материализованные представления. Но есть простое решение для управления этим:
Вы можете интегрировать свои материализованные представления в Django с помощью этой библиотеки django-materialized-view
Все, что вам нужно:
- Get view_definitions (Если вы сейчас не запрашиваете материализованные представления):
SELECT *
FROM information_schema.views
WHERE table_schema = 'information_schema'
AND table_name = 'views';
- Используйте django-materialized-view в соответствии с описанием .
- Пользуйтесь материализованными представлениями в своем проекте.