Giter VIP home page Giter VIP logo

django-materialized-view's Introduction

GitHub Actions codecov GitHub Actions GitHub GitHub last commit

Supported Django versions PyPI PyPI - Python Version PyPI - Downloads

Materialized View support for the Django framework. Django (in general) does not support materialized views by the default therefor migrations are not created automatically with ./manage.py makemigrations. This library provides new manage.py command: ./manage.py migrate_with_views. This command is to be used instead of the default one migrate.

This command automatically finds your materialized view models and keeps them up to date. In case when materialized view is a parent view for another materialized view, use migrate_with_views command in order to change query of parent materialized view. migrate_with_views command finds all related materialized views and recreates them sequentially.

Contents

Requirements

django-materialized-view has been tested with:

  • Django: 4.0, 4.1
  • Python: 3.9, 3.10, 3.11
  • Postgres >= 13

Installation

Via pip into a virtualenv:

pip install django-materialized-view

In settings.py add the following:

INSTALLED_APPS = (
    ...
    'django_materialized_view'
)

Before running migrate:

python manage.py migrate

Then you can use new migrate command instead of the default one:

python manage.py migrate_with_views

This command will automatically begin interception of materialized view models, and proceed to create/delete/update your view on your DB if required.

Usage

  1. Create class and inherit from MaterializedViewModel

    EXAMPLE:

    from django.db import models
    from django_materialized_view.base_model import MaterializedViewModel
    
    class MyViewModel(MaterializedViewModel):
        create_pkey_index = True  # if you need add unique field as a primary key and create indexes
    
        class Meta:
            managed = False
    
        # if create_pkey_index=True you must add argument primary_key=True
        item = models.OneToOneField("app.ItemModel", on_delete=models.DO_NOTHING, primary_key=True, db_column="id")
        from_seconds = models.IntegerField()
        to_seconds = models.IntegerField()
        type = models.CharField(max_length=255)
    
        # ATTENTION: this method must be a staticmethod or classmethod
        @staticmethod
        def get_query_from_queryset():
            # define this method only in case use queryset as a query for materialized view.
            # Method must return Queryset
            pass
  2. Add materialized view query (You can create a materialized view either from Raw SQL or from a queryset)

    • Create materialized view from Raw SQL

      1. run django default makemigrations command for creating model migrations if necessary:
        ./manage.py makemigrations
        
      2. run migrate_with_views command for getting your new sql file name and path:
        ./manage.py migrate_with_views
        
      3. you will get file path in your console
        [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
        
      4. create file on suggested path with suggested name
      5. run again django command migrate_with_views:
        ./manage.py migrate_with_views
        
        this command will run the default migrate command and apply materialized views
    • Create materialized view query from Queryset

      1. run django default makemigrations command for creating model migrations if necessary:
        ./manage.py makemigrations
        
      2. add to your materialized view model the method get_query_from_queryset:
        # ATTENTION: this method must be a staticmethod or classmethod
        @staticmethod
         def get_query_from_queryset():
             return SomeModel.objects.all()
      3. run django command migrate_with_views:
        ./manage.py migrate_with_views
        
        This command will run default migrate command and apply materialized views
  3. Use refresh method to update materialized view data.

    1. For updating concurrently:
      MyViewModel.refresh()
      
    2. For updating non-concurrently:
      MyViewModel.refresh(concurrently=Fasle)
      

    Note: All refreshes will be logged in to the model MaterializedViewRefreshLog:

    class MaterializedViewRefreshLog(models.Model):
        updated_at = models.DateTimeField(auto_now_add=True, db_index=True)
        duration = models.DurationField(null=True)
        failed = models.BooleanField(default=False)
        view_name = models.CharField(max_length=255)

Development

  • Release CI triggered on tags. To release new version, create the release with new tag on GitHub

  • For integration with pytest add following fixture:

    @pytest.fixture(scope="session")
    def django_db_setup(django_db_setup, django_db_blocker):
        with django_db_blocker.unblock():
            view_processor = MaterializedViewsProcessor()
            view_processor.process_materialized_views()

django-materialized-view's People

Contributors

sheripov avatar

Stargazers

 avatar  avatar Rob Lusk avatar  avatar Takeshi Masukawa avatar Pierre Couy avatar  avatar Rasul avatar Justice avatar Nikolaus Schlemm avatar George Kettleborough avatar Matías Agustín Méndez avatar Christopher Mayfield avatar Gnosnay avatar raúl avatar  avatar Oleg Stepura avatar Anton Fomin avatar Vadim Rudenkov avatar Nina Viter avatar  avatar Silvan Mühlemann avatar Eldiiar Egemberdiev avatar  avatar

Watchers

Silvan Mühlemann avatar  avatar

django-materialized-view's Issues

get_query_from_queryset not works if value passes

If you pass any value to the queryset, then the view is not created.

Example:
MyModel.objects.filter(name="test")
Cause:
queryset.query returns RAW sql query with incorrect type of values. name=test instead name='test'

TypeError: not enough arguments for format string

Traceback (most recent call last):
  File "/home/seva/PycharmProjects/devflow-django/src/manage.py", line 22, in <module>
    main()
  File "/home/seva/PycharmProjects/devflow-django/src/manage.py", line 18, in main
    execute_from_command_line(sys.argv)
  File "/home/seva/PycharmProjects/devflow-django/venv/lib/python3.10/site-packages/django/core/management/__init__.py", line 446, in execute_from_command_line
    utility.execute()
  File "/home/seva/PycharmProjects/devflow-django/venv/lib/python3.10/site-packages/django/core/management/__init__.py", line 440, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/seva/PycharmProjects/devflow-django/venv/lib/python3.10/site-packages/django/core/management/base.py", line 402, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/seva/PycharmProjects/devflow-django/venv/lib/python3.10/site-packages/django/core/management/base.py", line 448, in execute
    output = self.handle(*args, **options)
  File "/home/seva/PycharmProjects/devflow-django/venv/lib/python3.10/site-packages/django_materialized_view/management/commands/migrate_with_views.py", line 46, in handle
    self.view_processor.process_materialized_views()
  File "/home/seva/PycharmProjects/devflow-django/venv/lib/python3.10/site-packages/django_materialized_view/processor.py", line 35, in process_materialized_views
    self.mark_to_be_applied_new_views()
  File "/home/seva/PycharmProjects/devflow-django/venv/lib/python3.10/site-packages/django_materialized_view/processor.py", line 55, in mark_to_be_applied_new_views
    actual_view_definition_hash = self.__get_hash_from_string(actual_view_definition % args)
TypeError: not enough arguments for format string

Percentage sign considered as a placeholder by mistake:

LIKE UPPER('%bug%')

Suggest to change:

actual_view_definition_hash = self.__get_hash_from_string(actual_view_definition % args)

to

actual_view_definition_hash = self.__get_hash_from_string(actual_view_definition % args if args else actual_view_definition)

RecursionError arises if view query is incorrect

test setup failed
request = <SubRequest '_django_db_marker' for <Function test_public_user_can_not_be_set_as_company_admin>>

    @pytest.fixture(autouse=True)
    def _django_db_marker(request) -> None:
        """Implement the django_db marker, internal to pytest-django."""
        marker = request.node.get_closest_marker("django_db")
        if marker:
>           request.getfixturevalue("_django_db_helper")

.venv/lib/python3.10/site-packages/pytest_django/plugin.py:465: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/conftest.py:293: in django_db_setup
    view_processor.process_materialized_views()
.venv/lib/python3.10/site-packages/django_materialized_view/processor.py:37: in process_materialized_views
    self.create_views()
.venv/lib/python3.10/site-packages/django_materialized_view/processor.py:92: in create_views
    self.create_views()
.venv/lib/python3.10/site-packages/django_materialized_view/processor.py:92: in create_views
    self.create_views()
E   RecursionError: maximum recursion depth exceeded while calling a Python object
!!! Recursion detected (same locals & position)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.