Source code for boardinghouse.migrations.0005_group_views
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations
from boardinghouse.schema import _table_exists
from boardinghouse.signals import schema_aware_operation
[docs]def private_auth_models(apps):
"""
Which of the django.contrib.auth models should be private, and more specifically, should
also have an (empty) VIEW inside settings.PUBLIC_SCHEMA, so we don't get exceptions when
no schema is activated.
Note this takes into account the settings.PRIVATE_MODELS, to allow us to use the one
migration for when `boardinghouse.contrib.groups` is installed (or auth.group is added
by other means).
"""
User = apps.get_model(*settings.AUTH_USER_MODEL.split('.'))
models = [User.groups.through, User.user_permissions.through]
if 'auth.groups' in settings.PRIVATE_MODELS:
models.append(apps.get_model('auth', 'group'))
return models
[docs]def view_from_model(model):
"""
Return a DDL statement that creates a VIEW based on the model.
The view MUST always return an empty query, and writes to the view should be
silently discarded without error.
"""
return '''CREATE VIEW "{public}"."{table_name}" AS (SELECT * FROM "{template}"."{table_name}" WHERE false);
CREATE TRIGGER "{table_name}_noop"
INSTEAD OF INSERT ON "{public}"."{table_name}"
FOR EACH ROW EXECUTE PROCEDURE noop()'''.format(public=settings.PUBLIC_SCHEMA,
template=settings.TEMPLATE_SCHEMA,
table_name=model._meta.db_table)
def create_views(apps, schema_editor):
# We need a noop() function in the database, to use as our INSTEAD OF INSERT trigger.
schema_editor.execute(
"CREATE OR REPLACE FUNCTION noop() RETURNS TRIGGER AS 'BEGIN RETURN NEW; END;' LANGUAGE plpgsql;"
)
for model in private_auth_models(apps):
schema_editor.execute(view_from_model(model))
def drop_views(apps, schema_editor):
for model in private_auth_models(apps):
schema_editor.execute('DROP VIEW "{public}"."{table_name}" CASCADE'.format(
public=settings.PUBLIC_SCHEMA, table_name=model._meta.db_table
))
[docs]def move_existing_to_schemata(apps, schema_editor):
"""
This is not really working that well at the moment. Perhaps we should look at
using the migration operations that actually add/remove these tables?
Or maybe just give instructions about what to do if this case is detected?
"""
def move_table(table_name):
schema_editor.execute('CREATE TABLE "{table}" (LIKE "{public}"."{table}" INCLUDING ALL)'.format(
public=settings.PUBLIC_SCHEMA,
table=table_name
))
for model in private_auth_models(apps):
# Look for model in public schema.
table_name = model._meta.db_table
if (
_table_exists(schema=settings.PUBLIC_SCHEMA, table_name=table_name) and
not _table_exists(schema=settings.TEMPLATE_SCHEMA, table_name=table_name)
):
schema_aware_operation.send(schema_editor,
db_table=table_name,
function=move_table,
args=(table_name,))
# Now we need to delete the table from the public schema.
schema_editor.execute(
'ALTER TABLE "{public}"."{table}" RENAME TO ____{table}'.format(public=settings.PUBLIC_SCHEMA,
table=table_name))
def noop(apps, schema_editor):
pass
class Migration(migrations.Migration):
initial = True
dependencies = [
('boardinghouse', '0004_change_sequence_owners'),
]
operations = [
# We need to look and see if we have the tables that we are expecting to have as private relations,
# but in the public schema. If we do, then we need to drop them, and create empty versions in all
# schemata (template and otherwise). The reverse of this is a noop, because we don't support the erroneous
# state that existed before.
migrations.RunPython(move_existing_to_schemata, noop),
# Then we need to create the views, to prevent exceptions when querying for permissions without an active
# schema.
migrations.RunPython(create_views, drop_views),
]