openwisp / django-ipam

The development of this project has moved to openwisp-ipam
https://github.com/openwisp/openwisp-ipam
BSD 3-Clause "New" or "Revised" License
78 stars 28 forks source link

Getting errors when running a migration for a NetworkField on PostgreSQL #94

Closed okraits closed 4 years ago

okraits commented 4 years ago

I'm getting errors when running a migration for a NetworkField on PostgreSQL:

  File "/home/openwisp/openwisp/.venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
psycopg2.errors.InvalidTextRepresentation: invalid input syntax for type cidr: "None"

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "manage.py", line 27, in <module>
    main()
  File "manage.py", line 23, in main
    execute_from_command_line(sys.argv)
  File "/home/openwisp/openwisp/.venv/lib/python3.5/site-packages/django/core/management/__init__.py", line 371, in execute_from_command_line
    utility.execute()
  File "/home/openwisp/openwisp/.venv/lib/python3.5/site-packages/django/core/management/__init__.py", line 365, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/openwisp/openwisp/.venv/lib/python3.5/site-packages/django/core/management/base.py", line 288, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/openwisp/openwisp/.venv/lib/python3.5/site-packages/django/core/management/base.py", line 335, in execute
    output = self.handle(*args, **options)
  File "/home/openwisp/openwisp/.venv/lib/python3.5/site-packages/django/core/management/commands/migrate.py", line 200, in handle
    fake_initial=fake_initial,
  File "/home/openwisp/openwisp/.venv/lib/python3.5/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/openwisp/openwisp/.venv/lib/python3.5/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/openwisp/openwisp/.venv/lib/python3.5/site-packages/django/db/migrations/executor.py", line 244, in apply_migration
    state = migration.apply(state, schema_editor)
  File "/home/openwisp/openwisp/.venv/lib/python3.5/site-packages/django/db/migrations/migration.py", line 122, in apply
    operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
  File "/home/openwisp/openwisp/.venv/lib/python3.5/site-packages/django/db/migrations/operations/fields.py", line 84, in database_forwards
    field,
  File "/home/openwisp/openwisp/.venv/lib/python3.5/site-packages/django/db/backends/base/schema.py", line 437, in add_field
    self.execute(sql, params)
  File "/home/openwisp/openwisp/.venv/lib/python3.5/site-packages/django/db/backends/base/schema.py", line 133, in execute
    cursor.execute(sql, params)
  File "/home/openwisp/openwisp/.venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 100, in execute
    return super().execute(sql, params)
  File "/home/openwisp/openwisp/.venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 68, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/home/openwisp/openwisp/.venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/home/openwisp/openwisp/.venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
  File "/home/openwisp/openwisp/.venv/lib/python3.5/site-packages/django/db/utils.py", line 89, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/home/openwisp/openwisp/.venv/lib/python3.5/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
django.db.utils.DataError: invalid input syntax for type cidr: "None"

I tried setting blank=True, null=True and tried setting default="192.168.10/24"" but that didn't help.

I would be grateful for any hints.

okraits commented 4 years ago

I did further investigations. A migration like

from django.db import migrations
import django_ipam.base.fields

class Migration(migrations.Migration):

    dependencies = [
        ('hal', '0005_add_setup_target_date'),
    ]

    operations = [
        migrations.AddField(
            model_name='setup',
            name='lan_net',
            field=django_ipam.base.fields.NetworkField(help_text='Subnet in CIDR notation, eg: "10.0.0.0/24" for IPv4 and "fdb6:21b:a477::9f7/64" for IPv6', max_length=43),
        ),
    ]

leads to the following SQL statements (from python manage.py sqlmigrate hal 0006):

BEGIN;
--
-- Add field lan_net to setup
--
ALTER TABLE "hal_setup" ADD COLUMN "lan_net" cidr DEFAULT 'None' NOT NULL;
ALTER TABLE "hal_setup" ALTER COLUMN "lan_net" DROP DEFAULT;
COMMIT;

I think that PostgreSQL doesn't like cidr DEFAULT 'None' here. If I add default='192.168.1.0/24' to the definition of the NetworkField in the migration then the migration is executed without errors.

That DEFAULT 'None' is added implicitly and not in the field definition in Python.

Interesting is also that the default value is added first and then removed again with another ALTER TABLE statement.

nemesifier commented 4 years ago

I think the empty representation is being passed as "None" (string) instead of None (which should be converted to NULL). The column should accept NULL values, but it won't accept empty strings nor "None" strings.

May be this line causing it, but I'm just speculating: https://github.com/openwisp/django-ipam/blob/master/django_ipam/base/fields.py#L56-L57

okraits commented 4 years ago

I think the empty representation is being passed as "None" (string) instead of None (which should be converted to NULL). The column should accept NULL values, but it won't accept empty strings nor "None" strings.

May be this line causing it, but I'm just speculating: https://github.com/openwisp/django-ipam/blob/master/django_ipam/base/fields.py#L56-L57

Good catch! This also removes the DEFAULT part in the ALTER TABLE statement completely.

I already created a PR.

Thank you very much! :+1: