1

I need to change this

data = models.CharField(max_length=500, null=True)

to this

data = JSONField(null=True, blank=True, default={})

From what I understand, I have to write custom migration. Closest info I managed to find is here, but I am completely clueless what to do with RunPython if that even correct thing to do here.

vvAve
  • 177
  • 1
  • 3
  • 13

2 Answers2

2

You can do it in next steps: 1. Add new field with type JSONField and run 'makemigrations':

data = models.CharField(max_length=500, null=True)
data_json = JSONField(null=True, blank=True, default={})
  1. Create data migration(using RunPython)

import json

from django.db import migrations

def forwards_func(apps, schema_editor):
    MyModel = apps.get_model("myapp", "MyModel")

    for obj in MyModel.objects.all():
        try:
            obj.data_json = json.loads(obj.data)
            obj.save()
        except json.decoder.JSONDecodeError as e:
            print('Cannot convert {} object'.format(obj.pk))


class Migration(migrations.Migration):

    dependencies = []  # WRITE YOUR LAST MIGRATION HERE

    operations = [
        migrations.RunPython(forwards_func, lambda apps, schema_editor: pass),
    ]
  1. Remove old data field and run 'makemigrations':

    data_json = JSONField(null=True, blank=True, default={})

  2. Rename json field and run 'makemigrations':

    data = JSONField(null=True, blank=True, default={})

P.S. You can use one migration for all this steps. I described all the steps for better understanding.

Detonavomek
  • 432
  • 5
  • 14
0

Suppose you have a model like this

class SampleModel(models.Model):
    name = models.CharField(max_length=120)
    age = models.IntegerField()
    address = models.CharField(max_length=100)


and you need to change the address to a JSONField
So you have to define a temporary field as below

from django.db import models
from jsonfield import JSONField


class SampleModel(models.Model):
    name = models.CharField(max_length=120)
    age = models.IntegerField()
    address = models.CharField(max_length=100)
    temp_address = JSONField(default={})


Then run the commands,
python manage.py makemigrations app_name and python manage.py migrate app_name

Then comes the role of DATA MIGRATION. What you have to do is that, run the command, python manage.py makemigrations app_name --empty
This willl create a empty migration file in your directory.Change that file into something like this
0003_auto_20180320_1525.py (migration file)

# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-03-20 15:25
from __future__ import unicode_literals
import json
from django.db import migrations
from sample.models import SampleModel


class Migration(migrations.Migration):
    def forward_data_migration(apps, schema):
        for sample in SampleModel.objects.all():
            try:
                sample.temp_address = json.loads(sample.address)
                sample.save()
            except json.decoder.JSONDecodeError as e:
                print('Cannot convert {} object'.format(sample.pk))

    def revert_migration(apps, schema):
        pass

    dependencies = [
        ('sample', '0002_samplemodel_temp_address'),
    ]

    operations = [
        migrations.RunPython(
            code=forward_data_migration,
            reverse_code=revert_migration
        )
    ]


Migrate this change,then create another empty migration file as before, and write RenameField script as below
0004_auto_20180320_1530.py

# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-03-20 15:30
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):
    dependencies = [
        ('sample', '0003_auto_20180320_1525'),
    ]

    operations = [
        migrations.RenameField(
            model_name='samplemodel',
            old_name='address',
            new_name='temp_address'
        ),
        migrations.RenameField(
            model_name='samplemodel',
            old_name='temp_address',
            new_name='address'
        )

    ]


Migrate this too. That's it.

Detonavomek
  • 432
  • 5
  • 14
JPG
  • 82,442
  • 19
  • 127
  • 206
  • I think I got it. What is exactly `json.dumps(sample.address)`? I used this, seems to be working. `for obj in UserLog.objects.all(): obj.data_json = obj.data obj.save()` – vvAve Mar 20 '18 at 15:50
  • 1
    @vvAve here by json.loads we check "can TextField data can convert to json" – Detonavomek May 17 '18 at 10:27