{"id":9681,"library":"django-pgviews-redux","title":"Django PGViews Redux","description":"django-pgviews-redux is a Django library designed to manage PostgreSQL views, including materialized views, as part of your Django models and migrations. It provides tools to define SQL views in your Django applications, track their schema changes, and integrate them seamlessly into your database lifecycle. The current version is 1.2.0, with an active but irregular release cadence.","status":"active","version":"1.2.0","language":"en","source_language":"en","source_url":"https://github.com/xelixdev/django-pgviews-redux","tags":["django","postgresql","views","materialized views","database","migrations"],"install":[{"cmd":"pip install django-pgviews-redux","lang":"bash","label":"Install with pip"}],"dependencies":[{"reason":"Core framework dependency for Django applications.","package":"Django","optional":false},{"reason":"PostgreSQL adapter for Python, required to interact with PostgreSQL.","package":"psycopg2-binary","optional":false}],"imports":[{"symbol":"PostgresView","correct":"from pgviews.view import PostgresView"},{"symbol":"MaterializedView","correct":"from pgviews.materialized_view import MaterializedView"},{"note":"Contains utility functions like refresh_materialized_view.","symbol":"functions","correct":"from pgviews import functions"}],"quickstart":{"code":"import os\nfrom django.db import models\nfrom pgviews.view import PostgresView\nfrom pgviews.materialized_view import MaterializedView\n\n# Assuming 'myapp' is an installed Django app\n# Add 'pgviews' to INSTALLED_APPS in settings.py:\n# INSTALLED_APPS = [\n#    ...,\n#    'myapp',\n#    'pgviews',\n# ]\n\n# Define a base model for demonstration\nclass Author(models.Model):\n    name = models.CharField(max_length=100)\n\n    def __str__(self):\n        return self.name\n\nclass Book(models.Model):\n    title = models.CharField(max_length=200)\n    author = models.ForeignKey(Author, on_delete=models.CASCADE)\n\n    def __str__(self):\n        return self.title\n\n# Define a regular PostgreSQL View\nclass AuthorBookCountView(PostgresView, models.Model):\n    # The SQL query must define an 'id' field, as Django models require a primary key.\n    view_definition = \"\"\"\n        SELECT\n            a.id AS id,\n            a.name AS author_name,\n            COUNT(b.id) AS book_count\n        FROM myapp_author a\n        LEFT JOIN myapp_book b ON a.id = b.author_id\n        GROUP BY a.id, a.name\n    \"\"\"\n\n    id = models.IntegerField(primary_key=True)\n    author_name = models.CharField(max_length=100)\n    book_count = models.IntegerField()\n\n    class Meta:\n        # Important: Django should not manage the creation/deletion of the view itself.\n        managed = False\n        db_table = 'author_book_count_view' # Explicitly set db_table for clarity\n\n# Define a Materialized PostgreSQL View\nclass MaterializedAuthorBookCountView(MaterializedView, models.Model):\n    view_definition = \"\"\"\n        SELECT\n            a.id AS id,\n            a.name AS author_name,\n            COUNT(b.id) AS book_count\n        FROM myapp_author a\n        LEFT JOIN myapp_book b ON a.id = b.author_id\n        GROUP BY a.id, a.name\n    \"\"\"\n\n    id = models.IntegerField(primary_key=True)\n    author_name = models.CharField(max_length=100)\n    book_count = models.IntegerField()\n\n    class Meta:\n        managed = False\n        db_table = 'materialized_author_book_count_view'\n\n    def refresh_data(self):\n        self.refresh() # Method provided by MaterializedView mixin\n\n# To use:\n# 1. Add 'pgviews' and 'myapp' to INSTALLED_APPS in settings.py.\n# 2. python manage.py makemigrations myapp\n# 3. python manage.py migrate\n# 4. To query: AuthorBookCountView.objects.all()\n# 5. To refresh materialized view: MaterializedAuthorBookCountView.objects.first().refresh_data() or MaterializedAuthorBookCountView.objects.refresh()\n","lang":"python","description":"This quickstart demonstrates how to define both a regular and a materialized PostgreSQL view in a Django application. Views are defined as Django models that inherit from `PostgresView` or `MaterializedView` mixins. Remember to set `managed = False` in the `Meta` class and include an `id` field as a primary key. Materialized views require explicit refreshing to update their data."},"warnings":[{"fix":"Consult the changelog and the latest documentation. Update calls to any affected functions and adjust code that relies on previous internal structures.","message":"Version 1.0.0 introduced significant breaking changes, including extensive codebase refactoring, removal of unused code, and conversion of some parameters to keyword-only. Review the v1.0.0 changelog thoroughly before upgrading from pre-1.0 versions.","severity":"breaking","affected_versions":">=1.0.0"},{"fix":"If you require the old behavior (attempting to detect and apply minimal changes) or have specific data retention requirements, you may need to explicitly set `MATERIALIZED_VIEWS_CHECK_SQL_CHANGED = False` in your Django settings or implement custom migration logic to handle data migration during view recreation.","message":"Starting with v1.0.0, the default behavior for `MATERIALIZED_VIEWS_CHECK_SQL_CHANGED` was changed from `False` to `True`. This means materialized views are now recreated from scratch when their definition changes, which can lead to data loss or performance issues if not explicitly handled or if you expected incremental updates.","severity":"breaking","affected_versions":">=1.0.0"},{"fix":"Ensure your view model includes `id = models.IntegerField(primary_key=True)` (or another suitable primary key) and `class Meta: managed = False`.","message":"When defining views as Django models, you must include a primary key (e.g., `id = models.IntegerField(primary_key=True)`) and set `managed = False` in the model's `Meta` class. Failing to do so will cause Django ORM issues or attempt to create a table instead of a view.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Implement a strategy for refreshing materialized views, such as periodic tasks (e.g., using Celery), database triggers, or manual calls via Django management commands, depending on your data freshness requirements.","message":"Materialized views are static snapshots of data at the time of their last refresh. They do not automatically update when underlying tables change. You must explicitly call the `refresh()` method or use the `pgviews.functions.refresh_materialized_view` utility.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-17T00:00:00.000Z","next_check":"2026-07-16T00:00:00.000Z","problems":[{"fix":"Ensure `pgviews` is in `INSTALLED_APPS`, and then run `python manage.py makemigrations myapp` followed by `python manage.py migrate`.","cause":"The PostgreSQL view has not been created in the database, or its definition is incorrect, or migrations have not been applied.","error":"django.db.utils.ProgrammingError: relation \"myapp_myview\" does not exist"},{"fix":"Add `'pgviews'` to the `INSTALLED_APPS` list in your Django `settings.py` file.","cause":"The `pgviews` app has not been added to your Django project's `INSTALLED_APPS` setting.","error":"django.core.exceptions.ImproperlyConfigured: 'pgviews' must be in your INSTALLED_APPS"},{"fix":"Use the `refresh(concurrently=True)` method for materialized views, which allows refreshing without blocking reads (though it may take longer). Alternatively, schedule refreshes during low-traffic periods or ensure no active queries are running against the view.","cause":"An attempt was made to refresh a materialized view (`REFRESH MATERIALIZED VIEW`) while there were active queries or locks on it, preventing a non-concurrent refresh.","error":"django.db.utils.ProgrammingError: materialized view \"my_app_mymaterializedview\" cannot be refreshed while it has open queries"},{"fix":"Ensure the model you intend to refresh inherits from `MaterializedView` (e.g., `class MyMV(MaterializedView, models.Model):`). Standard `PostgresView` models represent live views and do not need to be refreshed.","cause":"You are trying to call the `.refresh()` method on a standard `PostgresView` model. This method is only available for models that inherit from `MaterializedView`.","error":"AttributeError: 'MyPostgresView' object has no attribute 'refresh'"}]}