Opened 11 years ago

Closed 11 years ago

Last modified 11 years ago

#20978 closed Bug (fixed)

Cannot migrate if on_delete=models.SET_NULL

Reported by: Markus Holtermann Owned by: loic84
Component: Migrations Version: dev
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Given the following models, the migrate step fails with 'module' object has no attribute 'set_on_delete'.

# -*- coding: utf-8 -*-
from django.db import models
from django.utils.encoding import python_2_unicode_compatible


@python_2_unicode_compatible
class MyModel(models.Model):
    name = models.CharField('Name', max_length=50, unique=True)
    text = models.TextField('Text', blank=True, null=True)

    class Meta:
        verbose_name = 'My “fancy” Model'
        verbose_name_plural = 'My “fancy” Models'

    def __str__(self):
        return self.name


@python_2_unicode_compatible
class OtherModel(models.Model):
    mymodel = models.ForeignKey(MyModel, null=True, on_delete=models.SET_NULL)
    value = models.IntegerField('Value', blank=True, null=False, default=50)

    class Meta:
        verbose_name = 'Other Model'
        verbose_name_plural = 'Other Models'

    def __str__(self):
        return '%(mymodel)s %(value)d' % {
            'mymodel': self.mymodel.name,
            'value': self.value,
        }

The migration file looks like

# encoding: utf8
from django.db import models, migrations
import django.db.models.deletion


class Migration(migrations.Migration):

    dependencies = [(u'something', '0001_initial')]

    operations = [
        migrations.CreateModel(
            fields = [(u'id', models.AutoField(verbose_name=u'ID', serialize=False, auto_created=True, primary_key=True),), ('name', models.CharField(unique=True, max_length=50, verbose_name='Name'),), ('text', models.TextField(null=True, verbose_name='Text', blank=True),)],
            bases = (models.Model,),
            options = {u'verbose_name': 'My \xe2\x80\x9cfancy\xe2\x80\x9d Model', u'verbose_name_plural': 'My \xe2\x80\x9cfancy\xe2\x80\x9d Models'},
            name = 'MyModel',
        ),
        migrations.CreateModel(
            fields = [(u'id', models.AutoField(verbose_name=u'ID', serialize=False, auto_created=True, primary_key=True),), ('mymodel', models.ForeignKey(to=u'something.MyModel', to_field=u'id', null=True, on_delete=django.db.models.deletion.set_on_delete),), ('value', models.IntegerField(default=50, verbose_name='Value', blank=True),)],
            bases = (models.Model,),
            options = {u'verbose_name': 'Other Model', u'verbose_name_plural': 'Other Models'},
            name = 'OtherModel',
        ),
    ]

Migrating with Python 3.3:

Traceback (most recent call last):
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/markus/.venvs/django-master-py3/src/django/django/core/management/__init__.py", line 397, in execute_from_command_line
    utility.execute()
  File "/home/markus/.venvs/django-master-py3/src/django/django/core/management/__init__.py", line 390, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/markus/.venvs/django-master-py3/src/django/django/core/management/base.py", line 242, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/home/markus/.venvs/django-master-py3/src/django/django/core/management/base.py", line 289, in execute
    output = self.handle(*args, **options)
  File "/home/markus/.venvs/django-master-py3/src/django/django/core/management/commands/migrate.py", line 52, in handle
    executor = MigrationExecutor(connection, self.migration_progress_callback)
  File "/home/markus/.venvs/django-master-py3/src/django/django/db/migrations/executor.py", line 14, in __init__
    self.loader.load_disk()
  File "/home/markus/.venvs/django-master-py3/src/django/django/db/migrations/loader.py", line 77, in load_disk
    migration_module = import_module("%s.%s" % (module_name, migration_name))
  File "/home/markus/.venvs/django-master-py3/lib/python3.3/importlib/__init__.py", line 90, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1586, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1567, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1534, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 586, in _check_name_wrapper
  File "<frozen importlib._bootstrap>", line 1024, in load_module
  File "<frozen importlib._bootstrap>", line 1005, in load_module
  File "<frozen importlib._bootstrap>", line 562, in module_for_loader_wrapper
  File "<frozen importlib._bootstrap>", line 870, in _load_module
  File "<frozen importlib._bootstrap>", line 313, in _call_with_frames_removed
  File "/home/markus/Coding/django-testing/example/something/migrations/0001_initial.py", line 6, in <module>
    class Migration(migrations.Migration):
  File "/home/markus/Coding/django-testing/example/something/migrations/0001_initial.py", line 20, in Migration
    fields = [('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID'),), ('mymodel', models.ForeignKey(t_field='id', on_delete=django.db.models.deletion.set_on_delete, null=True, to='something.MyModel'),), ('value', models.IntegerField(default=50, bank=True, verbose_name='Value'),)],
AttributeError: 'module' object has no attribute 'set_on_delete'

Migrating with Python 2.7.5:

Traceback (most recent call last):
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/markus/.venvs/django-master-py27/src/django/django/core/management/__init__.py", line 397, in execute_from_command_line
    utility.execute()
  File "/home/markus/.venvs/django-master-py27/src/django/django/core/management/__init__.py", line 390, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/markus/.venvs/django-master-py27/src/django/django/core/management/base.py", line 242, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/home/markus/.venvs/django-master-py27/src/django/django/core/management/base.py", line 289, in execute
    output = self.handle(*args, **options)
  File "/home/markus/.venvs/django-master-py27/src/django/django/core/management/commands/migrate.py", line 52, in handle
    executor = MigrationExecutor(connection, self.migration_progress_callback)
  File "/home/markus/.venvs/django-master-py27/src/django/django/db/migrations/executor.py", line 14, in __init__
    self.loader.load_disk()
  File "/home/markus/.venvs/django-master-py27/src/django/django/db/migrations/loader.py", line 77, in load_disk
    migration_module = import_module("%s.%s" % (module_name, migration_name))
  File "/usr/lib64/python2.7/importlib/__init__.py", line 37, in import_module
    __import__(name)
  File "/home/markus/Coding/django-testing/example/something/migrations/0001_initial.py", line 6, in <module>
    class Migration(migrations.Migration):
  File "/home/markus/Coding/django-testing/example/something/migrations/0001_initial.py", line 18, in Migration
    fields = [(u'id', models.AutoField(verbose_name=u'ID', serialize=False, auto_created=True, primary_key=True),), ('mymodel', models.ForeignKey(to=u'something.MyModel', to_field=u'id', null=True, on_delete=django.db.models.deletion.set_on_delete),), ('value', models.IntegerField(default=50, verbose_name='Value', blank=True),)],
AttributeError: 'module' object has no attribute 'set_on_delete'

Change History (6)

comment:1 by Tim Graham, 11 years ago

Triage Stage: UnreviewedAccepted

comment:2 by loic84, 11 years ago

Has patch: set

PR https://github.com/django/django/pull/1542.

This fixes the particular case of SET_NULL which should be a pretty common case.

That said the underlying problem remains for SET which relies on a closure, the serialization system is not going to like closure in general. Maybe the value passed to SET should be stored on the field itself so it can be recovered, just like SET_DEFAULT relies on field.get_default().

comment:3 by Markus Holtermann, 11 years ago

Yeah, your solution obviously works, but is more a workaround than a fix. That said, I'm actually not happy with this patch and would like to either change the ticket to cover closures in general and provide a proper patch or close this issue and open a new one covering the general problem.

comment:4 by loic84, 11 years ago

Owner: set to loic84
Status: newassigned

Updated the PR with a generic solution for closures.

comment:5 by Tim Graham <timograham@…>, 11 years ago

Resolution: fixed
Status: assignedclosed

In 5df8f749e6338c85a091f41803e6ecb280fc9f70:

Fixed #20978 -- Made deletion.SET_NULL more friendly for MigrationWriter.serialize.

comment:6 by Tim Graham <timograham@…>, 11 years ago

In d59f1993f150f83524051d96b52df08da4dcf011:

Made MigrationWriter look for a "deconstruct" attribute on functions.

Refs #20978.

Note: See TracTickets for help on using tickets.
Back to Top