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?