0
votes

I have two models with one-to-one relationship in Django 1.11 with PostgreSQL. These two models are defined in models.py as follows:

class Book(models.Model):
    info = JSONField(default={})


class Author(models.Model):
    book = models.OneToOneField(Book, on_delete=models.CASCADE)

The auto-created migration file regarding these models is like:

class Migration(migrations.Migration):

    dependencies = [
        ('manager', '0018_some_migration_dependency'),
    ]

    operations = [
        migrations.CreateModel(
            name='Book',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('info', JSONField(default={})),
            ],
        ),
        migrations.AddField(
            model_name='author',
            name='book',
            field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='manager.Book'),
        ),
    ]

These implementations have worked successfully. In addition to this migration, we also had some other additional migrations related to other tasks of our project.

Due to our design changes we made today, we decided to move all of the Book info data into our cloud storage. In order to do that, I have implemented a custom migration code as follows:

def push_info_to_cloud(apps, schema_editor):

    Author = apps.get_model('manager', 'Author')
    for author in Author.objects.all():
        if author.book.info is not None and author.book.info != "":

            # push author.book.info to cloud storage

            author.book.info = {}
            author.book.save()


def pull_info_from_cloud(apps, schema_editor):

    Author = apps.get_model('manager', 'Author')
    Book = apps.get_model('manager', 'Book')
    for author in Author.objects.all():

            # pull author.book.info back from cloud storage

            book = Book.objects.create(info=info)
            author.book = book
            author.save()


class Migration(migrations.Migration):

    dependencies = [
        ('manager', '0024_some_migration_dependency'),
    ]

    operations = [
        migrations.RunPython(push_info_to_cloud, pull_info_from_cloud)
    ]

As the code tells itself, this migrations push each non-null book info data to our cloud storage and replace that with an empty dict in the database. I have tested this migration back and forth and make sure that both the forward and backward migration work successfully.

Then, to get rid of the redundant Book table and book column in Author table, I deleted the Book model and the OneToOneField book field in the Author model and run manage.py makemigrations, which resulted in the following auto-generated migration code:

class Migration(migrations.Migration):

    dependencies = [
        ('manager', '0025_some_migration_dependency'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='user',
            name='book',
        ),
        migrations.DeleteModel(
            name='Book',
        ),
    ]

Running manage.py migrate did worked. In the end, the Book table and the book column of the Author table are deleted.

Now, the problem is; when I want to migrate back to 0024_some_migration_dependency, I get the following error during the execution of the latest migration file:

  Unapplying manager.0026_auto_20190503_1702...Traceback (most recent call last):
  File "/home/cagrias/Workspace/Project/backend/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
psycopg2.IntegrityError: column "book_id" contains null values

I have seen this answer. To try that, I have manually re-create Book model and the OneToOneField book field of the Author model, by using blank=True, null=True parameters this time. But after I apply the migrations above successfully, I got the same exceptions when migrating backwards.

What might be the problem?

1

1 Answers

0
votes

I have managed to solve problem by changing the order of the migrations.

As I mentioned in my question, I have applied this answer by adding blank=True, null=True parameters to both info and book fields. But it's related migration file was created after the migration file that moves our book info to the cloud storage. When I've changed the orders of these two migration files, the problem was solved.