Repository: openwisp/openwisp-radius (language: python)

Issue:
[bug] Fix django.db.migrations.exceptions.InconsistentMigrationHistory
Let's fix it with using https://github.com/openwisp/django-swappable-models/commit/7a636a8dc3567ebc029b76735eca74e9a232baa1 and https://github.com/openwisp/openwisp-users/commit/993f7ee64d389558cd3cb8dfcaf4a352de00afe9
[bug] Fix django.db.migrations.exceptions.InconsistentMigrationHistory
Let's fix it with using https://github.com/openwisp/django-swappable-models/commit/7a636a8dc3567ebc029b76735eca74e9a232baa1 and https://github.com/openwisp/openwisp-users/commit/993f7ee64d389558cd3cb8dfcaf4a352de00afe9


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1f65afbd..c39288cd 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -44,6 +44,7 @@ jobs:
         run: |
           pip install -U "pip==20.2.4" wheel setuptools
           pip install ${{ matrix.django-version }}
+          pip install swapper@git+git://github.com/openwisp/django-swappable-models.git@master
           pip install -U -r requirements-test.txt
 
       - name: Install npm dependencies
diff --git a/openwisp_radius/migrations/0002_initial_openwisp_radius.py b/openwisp_radius/migrations/0002_initial_openwisp_radius.py
index 77bd58bb..7b42a93d 100644
--- a/openwisp_radius/migrations/0002_initial_openwisp_radius.py
+++ b/openwisp_radius/migrations/0002_initial_openwisp_radius.py
@@ -29,7 +29,9 @@ class Migration(migrations.Migration):
     """
 
     dependencies = [
-        swapper.dependency(*swapper.split(settings.AUTH_USER_MODEL), latest=True),
+        swapper.dependency(
+            *swapper.split(settings.AUTH_USER_MODEL), version='0004_default_groups'
+        ),
         ('openwisp_radius', '0001_initial_freeradius'),
     ]
 
diff --git a/setup.py b/setup.py
index 25374b24..d93c4958 100644
--- a/setup.py
+++ b/setup.py
@@ -36,7 +36,7 @@
     zip_safe=False,
     install_requires=[
         'django>=3.0,<3.2',
-        'swapper~=1.2.0',
+        'swapper @ git+git://github.com/openwisp/django-swappable-models.git@master',
         # Needed for the new authentication backend in openwisp-users
         # TODO: remove when the new version of openwisp-users is released
         'openwisp-users @ https://github.com/openwisp/openwisp-users/tarball/master',


Test patch:
diff --git a/tests/openwisp2/sample_radius/migrations/0003_default_groups_and_permissions.py b/tests/openwisp2/sample_radius/migrations/0003_default_groups_and_permissions.py
index f5e1a7aa..afe87478 100644
--- a/tests/openwisp2/sample_radius/migrations/0003_default_groups_and_permissions.py
+++ b/tests/openwisp2/sample_radius/migrations/0003_default_groups_and_permissions.py
@@ -20,12 +20,12 @@ class Migration(migrations.Migration):
 
     rad_model = swapper.get_model_name('openwisp_radius', 'RadiusToken')
     users_model = swapper.get_model_name('openwisp_users', 'Organization')
-    model_app_label = swapper.split(rad_model)[0]
-    users_model_app_label = swapper.split(users_model)[0]
     dependencies = [
         swapper.dependency('openwisp_radius', 'RadiusToken'),
-        (model_app_label, '0002_initial_openwisp_app'),
-        (users_model_app_label, '0002_default_groups_and_permissions'),
+        swapper.dependency(
+            *swapper.split(rad_model), version='0002_initial_openwisp_app'
+        ),
+        swapper.dependency(*swapper.split(users_model), version='0004_default_groups'),
     ]
 
     operations = [
diff --git a/tests/openwisp2/sample_radius/migrations/0022_registered_user.py b/tests/openwisp2/sample_radius/migrations/0022_registered_user.py
index 0d79a417..9c319aa5 100644
--- a/tests/openwisp2/sample_radius/migrations/0022_registered_user.py
+++ b/tests/openwisp2/sample_radius/migrations/0022_registered_user.py
@@ -9,7 +9,6 @@
 class Migration(migrations.Migration):
 
     dependencies = [
-        ('sample_users', '0004_user_notes'),
         ('sample_radius', '0021_organizationradiussettings_registration_enabled'),
     ]
 
diff --git a/tests/openwisp2/sample_users/migrations/0001_initial.py b/tests/openwisp2/sample_users/migrations/0001_initial.py
index d6f1bf86..912df3a0 100644
--- a/tests/openwisp2/sample_users/migrations/0001_initial.py
+++ b/tests/openwisp2/sample_users/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.0.7 on 2020-06-11 10:56
+# Generated by Django 3.1.8 on 2021-11-27 15:27
 
 import uuid
 
@@ -132,13 +132,18 @@ class Migration(migrations.Migration):
                 (
                     'location',
                     models.CharField(
-                        blank=True, max_length=128, verbose_name='location'
+                        blank=True, max_length=256, verbose_name='location'
                     ),
                 ),
                 (
                     'phone_number',
                     phonenumber_field.modelfields.PhoneNumberField(
-                        blank=True, max_length=128, null=True, region=None, unique=True
+                        blank=True,
+                        max_length=128,
+                        null=True,
+                        region=None,
+                        unique=True,
+                        verbose_name='phone number',
                     ),
                 ),
                 (
@@ -179,6 +184,26 @@ class Migration(migrations.Migration):
                         verbose_name='user permissions',
                     ),
                 ),
+                (
+                    'birth_date',
+                    models.DateField(blank=True, null=True, verbose_name='birth date'),
+                ),
+                (
+                    'language',
+                    models.CharField(
+                        choices=settings.LANGUAGES,
+                        default=settings.LANGUAGE_CODE,
+                        max_length=8,
+                    ),
+                ),
+                (
+                    'notes',
+                    models.TextField(
+                        blank=True,
+                        help_text='notes for internal usage',
+                        verbose_name='notes',
+                    ),
+                ),
             ],
             options={
                 'verbose_name': 'user',
@@ -186,7 +211,9 @@ class Migration(migrations.Migration):
                 'abstract': False,
                 'index_together': {('id', 'email')},
             },
-            managers=[('objects', openwisp_users.base.models.UserManager())],
+            managers=[
+                ('objects', openwisp_users.base.models.UserManager()),
+            ],
         ),
         migrations.CreateModel(
             name='Organization',
@@ -243,7 +270,9 @@ class Migration(migrations.Migration):
                 ),
                 ('url', models.URLField(blank=True, verbose_name='URL')),
             ],
-            options={'abstract': False},
+            options={
+                'abstract': False,
+            },
             bases=(organizations.base.UnicodeMixin, models.Model),
         ),
         migrations.CreateModel(
@@ -257,7 +286,9 @@ class Migration(migrations.Migration):
                 'constraints': [],
             },
             bases=(openwisp_users.base.models.BaseGroup, 'auth.group'),
-            managers=[('objects', django.contrib.auth.models.GroupManager())],
+            managers=[
+                ('objects', django.contrib.auth.models.GroupManager()),
+            ],
         ),
         migrations.CreateModel(
             name='OrganizationUser',
@@ -289,7 +320,7 @@ class Migration(migrations.Migration):
                     models.ForeignKey(
                         on_delete=django.db.models.deletion.CASCADE,
                         related_name='organization_users',
-                        to='sample_users.Organization',
+                        to='sample_users.organization',
                     ),
                 ),
                 (
@@ -301,9 +332,32 @@ class Migration(migrations.Migration):
                     ),
                 ),
             ],
-            options={'abstract': False},
+            options={
+                'abstract': False,
+                'ordering': ['organization', 'user'],
+                'verbose_name': 'organization user',
+                'verbose_name_plural': 'organization users',
+                'unique_together': {('user', 'organization')},
+            },
             bases=(organizations.base.UnicodeMixin, models.Model),
         ),
+        migrations.AddField(
+            model_name='organization',
+            name='users',
+            field=models.ManyToManyField(
+                related_name='sample_users_organization',
+                through='sample_users.OrganizationUser',
+                to=settings.AUTH_USER_MODEL,
+            ),
+        ),
+        migrations.AlterModelOptions(
+            name='organization',
+            options={
+                'ordering': ['name'],
+                'verbose_name': 'organization',
+                'verbose_name_plural': 'organizations',
+            },
+        ),
         migrations.CreateModel(
             name='OrganizationOwner',
             fields=[
@@ -333,54 +387,90 @@ class Migration(migrations.Migration):
                     models.OneToOneField(
                         on_delete=django.db.models.deletion.CASCADE,
                         related_name='owner',
-                        to='sample_users.Organization',
+                        to='sample_users.organization',
                     ),
                 ),
                 (
                     'organization_user',
                     models.OneToOneField(
                         on_delete=django.db.models.deletion.CASCADE,
-                        to='sample_users.OrganizationUser',
+                        to='sample_users.organizationuser',
                     ),
                 ),
             ],
-            options={'abstract': False},
-            bases=(organizations.base.UnicodeMixin, models.Model),
-        ),
-        migrations.AddField(
-            model_name='organization',
-            name='users',
-            field=models.ManyToManyField(
-                related_name='sample_users_organization',
-                through='sample_users.OrganizationUser',
-                to=settings.AUTH_USER_MODEL,
-            ),
-        ),
-        migrations.AlterModelOptions(
-            name='organization',
-            options={
-                'ordering': ['name'],
-                'verbose_name': 'organization',
-                'verbose_name_plural': 'organizations',
-            },
-        ),
-        migrations.AlterModelOptions(
-            name='organizationowner',
             options={
+                'abstract': False,
                 'verbose_name': 'organization owner',
                 'verbose_name_plural': 'organization owners',
             },
+            bases=(organizations.base.UnicodeMixin, models.Model),
         ),
-        migrations.AlterModelOptions(
-            name='organizationuser',
+        migrations.CreateModel(
+            name='OrganizationInvitation',
+            fields=[
+                (
+                    'id',
+                    models.AutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name='ID',
+                    ),
+                ),
+                ('guid', models.UUIDField(editable=False)),
+                (
+                    'invitee_identifier',
+                    models.CharField(
+                        help_text=(
+                            'The contact identifier for the invitee, email,'
+                            ' phone number, social media handle, etc.'
+                        ),
+                        max_length=1000,
+                    ),
+                ),
+                (
+                    'created',
+                    organizations.fields.AutoCreatedField(
+                        default=django.utils.timezone.now, editable=False
+                    ),
+                ),
+                (
+                    'modified',
+                    organizations.fields.AutoLastModifiedField(
+                        default=django.utils.timezone.now, editable=False
+                    ),
+                ),
+                (
+                    'invited_by',
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name=(
+                            'sample_users_organizationinvitation_sent_invitations'
+                        ),
+                        to=settings.AUTH_USER_MODEL,
+                    ),
+                ),
+                (
+                    'invitee',
+                    models.ForeignKey(
+                        blank=True,
+                        null=True,
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name='sample_users_organizationinvitation_invitations',
+                        to=settings.AUTH_USER_MODEL,
+                    ),
+                ),
+                (
+                    'organization',
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name='organization_invites',
+                        to='sample_users.organization',
+                    ),
+                ),
+            ],
             options={
-                'ordering': ['organization', 'user'],
-                'verbose_name': 'organization user',
-                'verbose_name_plural': 'organization users',
+                'abstract': False,
             },
         ),
-        migrations.AlterUniqueTogether(
-            name='organizationuser',
-            unique_together={('user', 'organization')},
-        ),
     ]
diff --git a/tests/openwisp2/sample_users/migrations/0003_update_user_fields.py b/tests/openwisp2/sample_users/migrations/0003_update_user_fields.py
deleted file mode 100644
index 5ff08e26..00000000
--- a/tests/openwisp2/sample_users/migrations/0003_update_user_fields.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Generated by Django 3.1.5 on 2021-01-13 16:11
-
-import phonenumber_field.modelfields
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('sample_users', '0002_default_groups_and_permissions'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='user',
-            name='birth_date',
-            field=models.DateField(blank=True, null=True, verbose_name='birth date'),
-        ),
-        migrations.AlterField(
-            model_name='user',
-            name='location',
-            field=models.CharField(blank=True, max_length=256, verbose_name='location'),
-        ),
-        migrations.AlterField(
-            model_name='user',
-            name='phone_number',
-            field=phonenumber_field.modelfields.PhoneNumberField(
-                blank=True,
-                max_length=128,
-                null=True,
-                region=None,
-                unique=True,
-                verbose_name='phone number',
-            ),
-        ),
-    ]
diff --git a/tests/openwisp2/sample_users/migrations/0002_default_groups_and_permissions.py b/tests/openwisp2/sample_users/migrations/0004_default_groups.py
similarity index 90%
rename from tests/openwisp2/sample_users/migrations/0002_default_groups_and_permissions.py
rename to tests/openwisp2/sample_users/migrations/0004_default_groups.py
index 013207be..209c0ef0 100644
--- a/tests/openwisp2/sample_users/migrations/0002_default_groups_and_permissions.py
+++ b/tests/openwisp2/sample_users/migrations/0004_default_groups.py
@@ -14,9 +14,8 @@
 class Migration(migrations.Migration):
 
     org_model = swapper.get_model_name('openwisp_users', 'organization')
-    model_app_label = swapper.split(org_model)[0]
     dependencies = [
-        (model_app_label, '0001_initial'),
+        swapper.dependency(*swapper.split(org_model), version='0001_initial')
     ]
 
     operations = [
diff --git a/tests/openwisp2/sample_users/migrations/0004_user_notes.py b/tests/openwisp2/sample_users/migrations/0004_user_notes.py
deleted file mode 100644
index 031ac226..00000000
--- a/tests/openwisp2/sample_users/migrations/0004_user_notes.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Generated by Django 3.1.6 on 2021-02-17 01:46
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('sample_users', '0003_update_user_fields'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='user',
-            name='notes',
-            field=models.TextField(
-                blank=True, help_text='notes for internal usage', verbose_name='notes'
-            ),
-        ),
-    ]
diff --git a/tests/openwisp2/sample_users/models.py b/tests/openwisp2/sample_users/models.py
index bbc72ad3..e3631777 100644
--- a/tests/openwisp2/sample_users/models.py
+++ b/tests/openwisp2/sample_users/models.py
@@ -3,6 +3,7 @@
 from django.db import models
 from organizations.abstract import (
     AbstractOrganization,
+    AbstractOrganizationInvitation,
     AbstractOrganizationOwner,
     AbstractOrganizationUser,
 )
@@ -43,6 +44,10 @@ class Meta(AbstractOrganizationOwner.Meta):
         abstract = False
 
 
+class OrganizationInvitation(AbstractOrganizationInvitation):
+    pass
+
+
 class Group(BaseGroup, AbstractGroup):
     class Meta(BaseGroup.Meta):
         abstract = False
diff --git a/tests/openwisp2/settings.py b/tests/openwisp2/settings.py
index af417304..4fc67abd 100644
--- a/tests/openwisp2/settings.py
+++ b/tests/openwisp2/settings.py
@@ -299,6 +299,7 @@
     OPENWISP_USERS_ORGANIZATION_MODEL = 'sample_users.Organization'
     OPENWISP_USERS_ORGANIZATIONUSER_MODEL = 'sample_users.OrganizationUser'
     OPENWISP_USERS_ORGANIZATIONOWNER_MODEL = 'sample_users.OrganizationOwner'
+    OPENWISP_USERS_ORGANIZATIONINVITATION_MODEL = 'sample_users.OrganizationInvitation'
     OPENWISP_RADIUS_RADIUSREPLY_MODEL = 'sample_radius.RadiusReply'
     OPENWISP_RADIUS_RADIUSGROUPREPLY_MODEL = 'sample_radius.RadiusGroupReply'
     OPENWISP_RADIUS_RADIUSCHECK_MODEL = 'sample_radius.RadiusCheck'