Проблема миграции материализованного представления Django при обновлении

Мы создаем библиотеку на Django (v4.0) с Postgres (v13). Эта библиотека будет ядром нашей компании, поэтому мы должны быть осторожны с миграциями. Проблема заключается в миграциях Django, когда мы обновляем базовую таблицу для любого материализованного представления.

  1. Модели:
  • Базис
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"
  1. Миграции:
  • "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),
        ),
    ]
  1. Ошибка
  • Возникает, когда мы делаем 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

Все, что вам нужно:

  1. Get view_definitions (Если вы сейчас не запрашиваете материализованные представления):
SELECT *
FROM information_schema.views
WHERE table_schema = 'information_schema'
AND table_name = 'views';
  1. Используйте django-materialized-view в соответствии с описанием
  2. .
  3. Пользуйтесь материализованными представлениями в своем проекте.
Вернуться на верх