|
| 1 | +[](https://github.com/muehlemann-popp/django-materialized-view/actions) |
| 2 | +[](https://codecov.io/gh/muehlemann-popp/django-materialized-view) |
| 3 | +[](https://github.com/muehlemann-popp/django-materialized-view/actions) |
| 4 | + |
| 5 | + |
| 6 | + |
| 7 | +[](https://pypi.python.org/pypi/django-materialized-view) |
| 8 | + |
| 9 | + |
| 10 | + |
| 11 | + |
| 12 | + |
| 13 | +Materialized View support for the Django framework. Django (in general) does not support materialized views by the default |
| 14 | +therefor migrations are not created automatically with `./manage.py makemigrations`. |
| 15 | +This library provides new `manage.py` command: `./manage.py migrate_with_views`. |
| 16 | +This command is to be used instead of the default one `migrate`. |
| 17 | + |
| 18 | +This command automatically finds your materialized view models and keeps them up to date. |
| 19 | +In case when materialized view is a parent view for another materialized view, use `migrate_with_views` command |
| 20 | +in order to change query of parent materialized view. |
| 21 | +`migrate_with_views` command finds all related materialized views and recreates them sequentially. |
| 22 | + |
| 23 | +## Contents |
| 24 | + |
| 25 | +* [Requirements](#requirements) |
| 26 | +* [Installation](#installation) |
| 27 | +* [Usage](#Usage) |
| 28 | + * [Create class and inherit from MaterializedViewModel](#create-class-and-inherit-from-materializedviewmodel) |
| 29 | + * [Add materialized view query](#add-materialized-view-query) |
| 30 | + * [Create materialized view query with .sql file](#create-materialized-view-query-with-sql-file) |
| 31 | + * [Create materialized view query from Queryset](#create-materialized-view-query-from-queryset) |
| 32 | + * [Use refresh method to update materialized view data](#use-refresh-method-to-update-materialized-view-data) |
| 33 | + |
| 34 | + |
| 35 | +## Requirements |
| 36 | + |
| 37 | +django-materialized-view has been tested with: |
| 38 | + |
| 39 | +* Django: 4.0, 4.1 |
| 40 | +* Python: 3.9, 3.10, 3.11 |
| 41 | + |
| 42 | +## Installation |
| 43 | + |
| 44 | +Via pip into a `virtualenv`: |
| 45 | + |
| 46 | +```bash |
| 47 | +pip install django-materialized-view |
| 48 | +``` |
| 49 | + |
| 50 | +In `settings.py` add the following: |
| 51 | + |
| 52 | +```python |
| 53 | +INSTALLED_APPS = ( |
| 54 | + ... |
| 55 | + 'django_materialized_view' |
| 56 | +) |
| 57 | +``` |
| 58 | +Before running migrate: |
| 59 | + |
| 60 | +```bash |
| 61 | +python manage.py migrate |
| 62 | +``` |
| 63 | + |
| 64 | +Then you can use new migrate command instead of the default one: |
| 65 | +```bash |
| 66 | +python manage.py migrate_with_views |
| 67 | +``` |
| 68 | + |
| 69 | +This command will automatically begin interception of materialized view models, |
| 70 | +and proceed to create/delete/update your view on your DB if required. |
| 71 | + |
| 72 | +## Usage |
| 73 | + |
| 74 | +1. ### Create class and inherit from `MaterializedViewModel` |
| 75 | + |
| 76 | + EXAMPLE: |
| 77 | + ```python |
| 78 | + from django.db import models |
| 79 | + from django_materialized_view.base_model import MaterializedViewModel |
| 80 | + |
| 81 | + class MyViewModel(MaterializedViewModel): |
| 82 | + create_pkey_index = True # if you need add unique field as a primary key and create indexes |
| 83 | + |
| 84 | + class Meta: |
| 85 | + managed = False |
| 86 | + |
| 87 | + # if create_pkey_index=True you must add argument primary_key=True |
| 88 | + item = models.OneToOneField("app.ItemModel", on_delete=models.DO_NOTHING, primary_key=True, db_column="id") |
| 89 | + from_seconds = models.IntegerField() |
| 90 | + to_seconds = models.IntegerField() |
| 91 | + type = models.CharField(max_length=255) |
| 92 | + |
| 93 | + def get_query_from_queryset(self): |
| 94 | + # define this method only in case use queryset as a query for materialized view. |
| 95 | + # Method must return Queryset |
| 96 | + pass |
| 97 | + ``` |
| 98 | +2. ### Add materialized view query |
| 99 | + - #### Create materialized view query with .sql file |
| 100 | + 1. run django default `makemigrations` command for creating model migrations if necessary: |
| 101 | + ``` |
| 102 | + ./manage.py makemigrations |
| 103 | + ``` |
| 104 | + 2. run `migrate_with_views` command for getting your new sql file name and path: |
| 105 | + ``` |
| 106 | + ./manage.py migrate_with_views |
| 107 | + ``` |
| 108 | + 3. you will get file path in your console |
| 109 | + ``` |
| 110 | + [Errno 2] No such file or directory: '.../app/models/materialized_views/sql_files/myviewmodel.sql' - please create SQL file and put it to this directory |
| 111 | + ``` |
| 112 | + 4. create file on suggested path with suggested name |
| 113 | + 5. run again django command `migrate_with_views`: |
| 114 | + ``` |
| 115 | + ./manage.py migrate_with_views |
| 116 | + ``` |
| 117 | + this command will run the default `migrate` command and apply materialized views |
| 118 | + |
| 119 | + - #### Create materialized view query from Queryset |
| 120 | + 1. run django default `makemigrations` command for creating model migrations if necessary: |
| 121 | + ``` |
| 122 | + ./manage.py makemigrations |
| 123 | + ``` |
| 124 | + 2. add to your materialized view model the method `get_query_from_queryset`: |
| 125 | + ```python |
| 126 | + def get_query_from_queryset(self): |
| 127 | + return SomeModel.objects.all() |
| 128 | + ``` |
| 129 | + 3. run django command `migrate_with_views`: |
| 130 | + ``` |
| 131 | + ./manage.py migrate_with_views |
| 132 | + ``` |
| 133 | + This command will run default `migrate` command and apply materialized views |
| 134 | +3. ### Use `refresh` method to update materialized view data. |
| 135 | + 1. For updating concurrently: |
| 136 | + ``` |
| 137 | + MyViewModel.refresh() |
| 138 | + ``` |
| 139 | + 2. For updating non-concurrently: |
| 140 | + ``` |
| 141 | + MyViewModel.refresh(concurrently=Fasle) |
| 142 | + ``` |
| 143 | + Note: All refreshes will be logged in to the model MaterializedViewRefreshLog: |
| 144 | + ```python |
| 145 | + class MaterializedViewRefreshLog(models.Model): |
| 146 | + updated_at = models.DateTimeField(auto_now_add=True, db_index=True) |
| 147 | + duration = models.DurationField(null=True) |
| 148 | + failed = models.BooleanField(default=False) |
| 149 | + view_name = models.CharField(max_length=255) |
| 150 | + ``` |
0 commit comments