From d40119e7b6af2c943d62833c19b537292bc670c4 Mon Sep 17 00:00:00 2001 From: Serious Date: Mon, 31 Oct 2022 19:07:03 +0300 Subject: [PATCH 1/5] feat(migrations): Added Drop view Some refactoring helpers Added Drop View helper --- dbview/helpers.py | 119 ++++++++++++++++++++++++++++++---------------- 1 file changed, 78 insertions(+), 41 deletions(-) diff --git a/dbview/helpers.py b/dbview/helpers.py index 3298932..5b27b01 100644 --- a/dbview/helpers.py +++ b/dbview/helpers.py @@ -1,36 +1,37 @@ +"""HELPERS to create SQL VIEW +https://github.com/manuelnaranjo/django-database-view updated helper +(issue created. fork with solve created: https://github.com/Seriouskosk/django-database-view) +For Create/Drop SQL VIEW you must do: +1) set True in db_route.py in allow_migrate +2) manage.py makemigrations +3) set False in db_route.py in allow_migrate +4) manage.py migrate""" import logging -from django.db import migrations from django.apps import apps +from django.db import migrations -class CreateView(migrations.CreateModel): - - def database_forwards(self, app_label, schema_editor, from_state, to_state): - fake_model = to_state.apps.get_model(app_label, self.name) - - if not self.allow_migrate_model( - schema_editor.connection.alias, fake_model): - return - - model = self._get_model(fake_model, app_label, to_state) - - self._drop_view(fake_model, model, schema_editor) +class SQLViewsMixin: + """Mixin for work with SQL VIEWS""" - if hasattr(model, 'view'): - self._create_standard_view(model, schema_editor) - elif hasattr(model, 'get_view_str'): - self._create_view_from_raw_sql(model.get_view_str(), schema_editor) + @staticmethod + def _drop_view(model, schema_editor): + """DROP VIEW from DB""" + if hasattr(model, "drop_view_sql"): + sql_template = model.drop_view_sql else: - raise Exception('{} has neither view nor get_view_str'.format( - model)) - - def database_backwards(self, app_label, schema_editor, from_state, to): - fake_model = from_state.apps.get_model(app_label, self.name) - model = self._get_model(fake_model, app_label, to) - self._drop_view(fake_model, model, schema_editor) + sql_template = "DROP VIEW IF EXISTS %(table)s" + args = { + "table": schema_editor.quote_name(model._meta.db_table), + } + sql = sql_template % args + schema_editor.execute(sql, None) - def _get_model(self, state, app_label, fake_model): + def _get_model(self, app_label, fake_model): + """ + :fake_model: loaded from code + :return model: loaded from App""" models = apps.get_app_config(app_label).models_module if hasattr(models, self.name): @@ -41,29 +42,65 @@ def _get_model(self, state, app_label, fake_model): if hasattr(submodule, self.name): return getattr(submodule, self.name) - logging.warning('Using fake model, this may fail with inherited views') + logging.warning("Using fake model, this may fail with inherited views") return fake_model - def _drop_view(self, fake_model, model, schema_editor): - if hasattr(model, 'drop_view_sql'): - sql_template = model.drop_view_sql - else: - sql_template = 'DROP VIEW IF EXISTS %(table)s' - args = { - 'table': schema_editor.quote_name(fake_model._meta.db_table), - } - sql = sql_template % args - schema_editor.execute(sql, None) - def _create_standard_view(self, model, schema_editor): - sql_template = 'CREATE VIEW %(table)s AS %(definition)s' + """CREATE VIEW in DB""" + sql_template = "CREATE VIEW %(table)s AS %(definition)s" qs = str(model.view()) args = { - 'table': schema_editor.quote_name(model._meta.db_table), - 'definition': qs, + "table": schema_editor.quote_name(model._meta.db_table), + "definition": qs, } sql = sql_template % args self._create_view_from_raw_sql(sql, schema_editor) - def _create_view_from_raw_sql(self, sql, schema_editor): + @staticmethod + def _create_view_from_raw_sql(sql, schema_editor): + """Execute sql""" schema_editor.execute(sql, None) + + def create_view(self, app_label, schema_editor, state): + """create view method""" + fake_model = state.apps.get_model(app_label, self.name) + model = self._get_model(app_label, fake_model) + + self._drop_view(model, schema_editor) + + if hasattr(model, "view"): + self._create_standard_view(model, schema_editor) + elif hasattr(model, "get_view_str"): + self._create_view_from_raw_sql(model.get_view_str(), schema_editor) + else: + raise Exception(f"{model} has neither view nor get_view_str") + + def drop_view(self, app_label, schema_editor, from_state, to_state): + """Drop view method""" + fake_model = from_state.apps.get_model(app_label, self.name) + model = self._get_model(app_label, fake_model) + self._drop_view(model, schema_editor) + + +class DropView(migrations.DeleteModel, SQLViewsMixin): + """Drop SQL View migrations""" + + def database_forwards(self, app_label, schema_editor, from_state, to_state): + """Forwards DROP VIEW from DB""" + self.drop_view(app_label, schema_editor, from_state, to_state) + + def database_backwards(self, app_label, schema_editor, from_state, to_state): + """Backwards CREATE VIEW from DB""" + self.create_view(app_label, schema_editor, from_state) + + +class CreateView(migrations.CreateModel, SQLViewsMixin): + """Create SQL View migrations""" + + def database_forwards(self, app_label, schema_editor, from_state, to_state): + """Forwards CREATE VIEW from DB""" + self.create_view(app_label, schema_editor, to_state) + + def database_backwards(self, app_label, schema_editor, from_state, to_state): + """Backwards DROP VIEW from DB""" + self.drop_view(app_label, schema_editor, from_state, to_state) From 208ffac4a298656979d3a3effce84ee63bcd2b1c Mon Sep 17 00:00:00 2001 From: Serious Date: Tue, 1 Nov 2022 19:10:18 +0300 Subject: [PATCH 2/5] todo: Resolve todo Refactor --- dbview/helpers.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/dbview/helpers.py b/dbview/helpers.py index 5b27b01..99841c9 100644 --- a/dbview/helpers.py +++ b/dbview/helpers.py @@ -7,6 +7,7 @@ 3) set False in db_route.py in allow_migrate 4) manage.py migrate""" import logging +import types from django.apps import apps from django.db import migrations @@ -28,6 +29,17 @@ def _drop_view(model, schema_editor): sql = sql_template % args schema_editor.execute(sql, None) + def get_model_instance(self, submodules): + """get model recursive""" + attrs = [attr for attr in dir(submodules) if '_' not in attr] + for attr in attrs: + value = getattr(submodules, attr) + if self.name in str(value): + return value + if isinstance(value, types.ModuleType): + return self.get_model_instance(value) + return None + def _get_model(self, app_label, fake_model): """ :fake_model: loaded from code @@ -37,10 +49,9 @@ def _get_model(self, app_label, fake_model): if hasattr(models, self.name): return getattr(models, self.name) - # TODO: identify model more reliably and support more than 1 level - for submodule in models.__dict__.values(): - if hasattr(submodule, self.name): - return getattr(submodule, self.name) + sub_model = self.get_model_instance(models) + if sub_model: + return sub_model logging.warning("Using fake model, this may fail with inherited views") return fake_model @@ -75,9 +86,9 @@ def create_view(self, app_label, schema_editor, state): else: raise Exception(f"{model} has neither view nor get_view_str") - def drop_view(self, app_label, schema_editor, from_state, to_state): + def drop_view(self, app_label, schema_editor, state): """Drop view method""" - fake_model = from_state.apps.get_model(app_label, self.name) + fake_model = state.apps.get_model(app_label, self.name) model = self._get_model(app_label, fake_model) self._drop_view(model, schema_editor) @@ -87,7 +98,7 @@ class DropView(migrations.DeleteModel, SQLViewsMixin): def database_forwards(self, app_label, schema_editor, from_state, to_state): """Forwards DROP VIEW from DB""" - self.drop_view(app_label, schema_editor, from_state, to_state) + self.drop_view(app_label, schema_editor, to_state) def database_backwards(self, app_label, schema_editor, from_state, to_state): """Backwards CREATE VIEW from DB""" @@ -103,4 +114,4 @@ def database_forwards(self, app_label, schema_editor, from_state, to_state): def database_backwards(self, app_label, schema_editor, from_state, to_state): """Backwards DROP VIEW from DB""" - self.drop_view(app_label, schema_editor, from_state, to_state) + self.drop_view(app_label, schema_editor, from_state) From c0266721524fb80aff82b4ea14e7560382d3e0c3 Mon Sep 17 00:00:00 2001 From: Serious Date: Sat, 5 Nov 2022 12:57:28 +0300 Subject: [PATCH 3/5] bug(state): wrong state Fix state to forwards and backwards migrations --- dbview/helpers.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dbview/helpers.py b/dbview/helpers.py index 99841c9..fdbe1d0 100644 --- a/dbview/helpers.py +++ b/dbview/helpers.py @@ -31,7 +31,7 @@ def _drop_view(model, schema_editor): def get_model_instance(self, submodules): """get model recursive""" - attrs = [attr for attr in dir(submodules) if '_' not in attr] + attrs = [attr for attr in dir(submodules) if "_" not in attr] for attr in attrs: value = getattr(submodules, attr) if self.name in str(value): @@ -84,7 +84,7 @@ def create_view(self, app_label, schema_editor, state): elif hasattr(model, "get_view_str"): self._create_view_from_raw_sql(model.get_view_str(), schema_editor) else: - raise Exception(f"{model} has neither view nor get_view_str") + raise Exception(f"{model} has neither view or get_view_str") def drop_view(self, app_label, schema_editor, state): """Drop view method""" @@ -98,11 +98,11 @@ class DropView(migrations.DeleteModel, SQLViewsMixin): def database_forwards(self, app_label, schema_editor, from_state, to_state): """Forwards DROP VIEW from DB""" - self.drop_view(app_label, schema_editor, to_state) + self.drop_view(app_label, schema_editor, from_state) def database_backwards(self, app_label, schema_editor, from_state, to_state): """Backwards CREATE VIEW from DB""" - self.create_view(app_label, schema_editor, from_state) + self.create_view(app_label, schema_editor, to_state) class CreateView(migrations.CreateModel, SQLViewsMixin): @@ -110,8 +110,8 @@ class CreateView(migrations.CreateModel, SQLViewsMixin): def database_forwards(self, app_label, schema_editor, from_state, to_state): """Forwards CREATE VIEW from DB""" - self.create_view(app_label, schema_editor, to_state) + self.create_view(app_label, schema_editor, from_state) def database_backwards(self, app_label, schema_editor, from_state, to_state): """Backwards DROP VIEW from DB""" - self.drop_view(app_label, schema_editor, from_state) + self.drop_view(app_label, schema_editor, to_state) From a2c3a88ad90773aa805a19962d9529e6311efdbd Mon Sep 17 00:00:00 2001 From: Serious Date: Sat, 5 Nov 2022 13:01:26 +0300 Subject: [PATCH 4/5] bug(state): wrong state Fix state to forwards and backwards migrations --- dbview/helpers.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dbview/helpers.py b/dbview/helpers.py index fdbe1d0..ed93aab 100644 --- a/dbview/helpers.py +++ b/dbview/helpers.py @@ -2,10 +2,11 @@ https://github.com/manuelnaranjo/django-database-view updated helper (issue created. fork with solve created: https://github.com/Seriouskosk/django-database-view) For Create/Drop SQL VIEW you must do: -1) set True in db_route.py in allow_migrate -2) manage.py makemigrations -3) set False in db_route.py in allow_migrate -4) manage.py migrate""" +1) manage.py makemigrations +2) change: +migrations.CreateModel on helpers.CreateView +migrations.DeleteModel on helpers.DropView +3) manage.py migrate""" import logging import types From 0329d4ea1cf9ae03987efe59d707eebf19d8f1b1 Mon Sep 17 00:00:00 2001 From: Serious Date: Sat, 5 Nov 2022 13:12:20 +0300 Subject: [PATCH 5/5] docs: Added Drop view Added Drop view version update --- README.rst | 4 ++++ setup.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 92f9dee..fff077a 100644 --- a/README.rst +++ b/README.rst @@ -63,7 +63,11 @@ Quick start 3. Then create a migration point for your view generation, edit that migration and modify it, add: + ``from dbview.helpers import CreateView`` and replace the line the call to ``migrations.CreateModel`` with ``CreateView``. + ``from dbview.helpers import DropView`` and replace the line the + call to ``migrations.DeleteModel`` with ``DropView``. + 4. Migrate your database and start using your database views. diff --git a/setup.py b/setup.py index 370b20b..4fa4386 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ def read(fname): setup( name='django-database-view', - version='0.3.0', + version='0.3.1', packages=['dbview'], long_description=read('README.rst'), include_package_data=True,