Skip to content

Commit

Permalink
Merge pull request #154 from bjester/postgres-drop-null
Browse files Browse the repository at this point in the history
Fix Postgres issue not dropping null constraint
  • Loading branch information
rtibbles authored Mar 8, 2022
2 parents f42dac9 + 0be72df commit efc29fd
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

List of the most important changes for each release.

## 0.6.10
- Fixes Django migration issue introduced in 0.6.7 allowing nullable fields with PostgreSQL backends

## 0.6.9
- Fixes un-ordered selection of buffers during sync which can allow duplicates to be synced with PostgreSQL backends
- Moves updating of database counters to occur in the same DB transaction as updates to the Store
Expand Down
2 changes: 1 addition & 1 deletion morango/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
from __future__ import unicode_literals

default_app_config = "morango.apps.MorangoConfig"
__version__ = "0.6.9"
__version__ = "0.6.10"
33 changes: 33 additions & 0 deletions morango/migrations/0020_postgres_fix_nullable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2022-01-13 18:07
from __future__ import unicode_literals

from django.db import migrations


def apply(apps, schema_editor):
# sqlite does not allow ALTER COLUMN, but also isn't affected by this issue
if "postgresql" in schema_editor.connection.vendor:
schema_editor.execute("ALTER TABLE morango_transfersession ALTER COLUMN transfer_stage DROP NOT NULL")
schema_editor.execute("ALTER TABLE morango_transfersession ALTER COLUMN transfer_stage_status DROP NOT NULL")


def revert(apps, schema_editor):
# sqlite does not allow ALTER COLUMN, but also isn't affected by this issue
if "postgresql" in schema_editor.connection.vendor:
schema_editor.execute("ALTER TABLE morango_transfersession ALTER COLUMN transfer_stage SET NOT NULL")
schema_editor.execute("ALTER TABLE morango_transfersession ALTER COLUMN transfer_stage_status SET NOT NULL")


class Migration(migrations.Migration):
"""
Applies nullable change made to 0018_auto_20210714_2216.py after it was released
"""

dependencies = [
("morango", "0019_auto_20220113_1807"),
]

operations = [
migrations.RunPython(apply, reverse_code=revert),
]
38 changes: 38 additions & 0 deletions tests/testapp/tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

import factory
import mock
from django.db import connection
from django.db.migrations.executor import MigrationExecutor
from django.test import TestCase
from django.test.testcases import LiveServerTestCase
from django.core.serializers.json import DjangoJSONEncoder
from django.utils import timezone
Expand Down Expand Up @@ -442,3 +445,38 @@ def stage_status(self):
def update_state(self, stage=None, stage_status=None):
self._stage = stage or self._stage
self._stage_status = stage_status or self._stage_status


class TestMigrations(TestCase):
# Modified from https://www.caktusgroup.com/blog/2016/02/02/writing-unit-tests-django-migrations/

migrate_from = None
migrate_to = None
app = None

def setUp(self):
assert (
self.migrate_from and self.migrate_to
), "TestCase '{}' must define migrate_from and migrate_to properties".format(
type(self).__name__
)

migrate_from = [(self.app, self.migrate_from)]
migrate_to = [(self.app, self.migrate_to)]
executor = MigrationExecutor(connection)
old_apps = executor.loader.project_state(migrate_from).apps

# Reverse to the original migration
executor.migrate(migrate_from)

self.setUpBeforeMigration(old_apps)

# Run the migration to test
executor = MigrationExecutor(connection)
executor.loader.build_graph() # reload.
executor.migrate(migrate_to)

self.apps = executor.loader.project_state(migrate_to).apps

def setUpBeforeMigration(self, apps):
pass
54 changes: 54 additions & 0 deletions tests/testapp/tests/test_migrations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import uuid

import pytest

from django.conf import settings
from django.db import connection
from django.db.utils import IntegrityError
from django.utils import timezone

from .helpers import TestMigrations


@pytest.mark.skipif(not settings.MORANGO_TEST_POSTGRESQL, reason="Only postgres")
class MorangoNullableMigrationTest(TestMigrations):
"""
Test migration that applies nullable status to `transfer_stage` and `transfer_stage_status`
"""

app = "morango"
migrate_from = "0018_auto_20210714_2216"
migrate_to = "0020_postgres_fix_nullable"

def setUpBeforeMigration(self, apps):
# simulate as if 0018_auto_20210714_2216 hadn't applied Nullablity to the columns,
# a change which we added after the migration might have run on other
SyncSession = apps.get_model("morango", "SyncSession")

with connection.cursor() as cursor:
cursor.execute("ALTER TABLE morango_transfersession ALTER COLUMN transfer_stage SET NOT NULL")
cursor.execute("ALTER TABLE morango_transfersession ALTER COLUMN transfer_stage_status SET NOT NULL")

self.sync_session = SyncSession.objects.create(
id=uuid.uuid4().hex,
profile="facilitydata",
last_activity_timestamp=timezone.now(),
)

def test_nullable(self):
TransferSession = self.apps.get_model("morango", "TransferSession")

try:
transfer_session = TransferSession.objects.create(
id=uuid.uuid4().hex,
sync_session_id=self.sync_session.id,
push=True,
last_activity_timestamp=timezone.now(),
transfer_stage=None,
transfer_stage_status=None,
)
except IntegrityError:
self.fail("Couldn't create TransferSession with nullable fields")

self.assertIsNone(transfer_session.transfer_stage)
self.assertIsNone(transfer_session.transfer_stage_status)

0 comments on commit efc29fd

Please sign in to comment.