Code

Opened 11 months ago

Closed 10 months ago

Last modified 10 months ago

#20978 closed Bug (fixed)

Cannot migrate if on_delete=models.SET_NULL

Reported by: MarkusH Owned by: loic84
Component: Migrations Version: master
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'

Attachments (0)

Change History (6)

comment:1 Changed 11 months ago by timo

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Triage Stage changed from Unreviewed to Accepted

comment:2 Changed 10 months ago by loic84

  • 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 Changed 10 months ago by MarkusH

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 Changed 10 months ago by loic84

  • Owner set to loic84
  • Status changed from new to assigned

Updated the PR with a generic solution for closures.

comment:5 Changed 10 months ago by Tim Graham <timograham@…>

  • Resolution set to fixed
  • Status changed from assigned to closed

In 5df8f749e6338c85a091f41803e6ecb280fc9f70:

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

comment:6 Changed 10 months ago by Tim Graham <timograham@…>

In d59f1993f150f83524051d96b52df08da4dcf011:

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

Refs #20978.

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.