Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added DropView migration helper #16

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
139 changes: 94 additions & 45 deletions dbview/helpers.py
Original file line number Diff line number Diff line change
@@ -1,69 +1,118 @@
"""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) manage.py makemigrations
2) change:
migrations.CreateModel on helpers.CreateView
migrations.DeleteModel on helpers.DropView
3) manage.py migrate"""
import logging
import types

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_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
:return model: loaded from App"""
models = apps.get_app_config(app_label).models_module

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')
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 or get_view_str")

def drop_view(self, app_label, schema_editor, state):
"""Drop 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)


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)

def database_backwards(self, app_label, schema_editor, from_state, to_state):
"""Backwards CREATE VIEW from DB"""
self.create_view(app_label, schema_editor, to_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, 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, to_state)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down