Search code examples
djangopython-3.xpostgresqldjango-migrationsjson-serialization

TypeError: Object of type Decimal is not JSON serializable during Django data migration


I added a postgres JsonField in my model, and created data migration for it.

new_column = pg_fields.JSONField(default=dict, encoder=DjangoJSONEncoder)

Same logic is used for future data(in save() of model) and for historic data(data migration).

This is the format of data I am trying to save

data = {
        'premium': obj.amount * obj.persons,
    }
proposal.new_column = data
proposal.save()

Here, obj is the instance of some other model, and amount is the DecimalField of that model.

The logic runs fine in save() method of model class. But on running data migration, I am getting this error TypeError: Object of type Decimal is not JSON serializable

Stacktrace

Traceback (most recent call last):
  File "./manage.py", line 21, in <module>
    execute_from_command_line(sys.argv)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/core/management/__init__.py", line 375, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/core/management/base.py", line 323, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/core/management/base.py", line 364, in execute
    output = self.handle(*args, **options)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/core/management/base.py", line 83, in wrapped
    res = handle_func(*args, **kwargs)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/core/management/commands/migrate.py", line 234, in handle
    fake_initial=fake_initial,
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/db/migrations/executor.py", line 117, in migrate
    state = self._migrate_all_forwards(state, plan, full_plan, fake=fake, fake_initial=fake_initial)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/db/migrations/executor.py", line 147, in _migrate_all_forwards
    state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/db/migrations/executor.py", line 245, in apply_migration
    state = migration.apply(state, schema_editor)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/db/migrations/migration.py", line 124, in apply
    operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/db/migrations/operations/special.py", line 190, in database_forwards
    self.code(from_state.apps, schema_editor)
  File "/home/simarpreet/Aegon/jarvis/insurance/travel/proposal/migrations/0006_auto_20191111_1943.py", line 34, in repopulate_slab_in_proposals
    proposal.save()
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/db/models/base.py", line 741, in save
    force_update=force_update, update_fields=update_fields)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/db/models/base.py", line 779, in save_base
    force_update, using, update_fields,
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/db/models/base.py", line 851, in _save_table
    forced_update)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/db/models/base.py", line 900, in _do_update
    return filtered._update(values) > 0
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/db/models/query.py", line 760, in _update
    return query.get_compiler(self.db).execute_sql(CURSOR)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/db/models/sql/compiler.py", line 1429, in execute_sql
    cursor = super().execute_sql(result_type)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/db/models/sql/compiler.py", line 1100, in execute_sql
    cursor.execute(sql, params)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/db/backends/utils.py", line 99, in execute
    return super().execute(sql, params)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/db/backends/utils.py", line 67, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/db/backends/utils.py", line 76, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/psycopg2/_json.py", line 78, in getquoted
    s = self.dumps(self.adapted)
  File "/home/simarpreet/Envs/j/lib/python3.7/site-packages/django/contrib/postgres/fields/jsonb.py", line 27, in dumps
    return json.dumps(obj, **options)
  File "/usr/lib/python3.7/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/usr/lib/python3.7/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python3.7/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/usr/lib/python3.7/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type Decimal is not JSON serializable

But as per the documentation of Django data migration (https://docs.djangoproject.com/en/2.2/topics/migrations/) , Django can serialze the decimal.Decimal value. Following is the screenshot for reference. screenshot from django documentation

Can someone suggest why am I getting this error?


Solution

  • I was getting this issue because I forgot to run migration after adding the encoder

    new_column = pg_fields.JSONField(default=dict, encoder=DjangoJSONEncoder)

    Running migration again python manage.py migrate solved the problem.

    Posting the solution in case someone else does such things like me.