Opened 7 years ago

Closed 7 years ago

#25129 closed Bug (fixed)

Migration crash on ForeignKey with a callable default that returns a model instance

Reported by: Tim Graham Owned by: nobody
Component: Migrations Version: 1.8
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

For example, this model in Django's test suite (from tests/delete/models.py):

class R(models.Model):
    is_default = models.BooleanField(default=False)

def get_default_r():
    return R.objects.get_or_create(is_default=True)[0]

class A(models.Model):
    setdefault = models.ForeignKey(R, default=get_default_r)

will fail like this:

  File "/home/tim/code/django/django/db/migrations/migration.py", line 123, in apply
    operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
  File "/home/tim/code/django/django/db/migrations/operations/fields.py", line 62, in database_forwards
    field,
  File "/home/tim/code/django/django/db/backends/sqlite3/schema.py", line 224, in add_field
    self._remake_table(model, create_fields=[field])
  File "/home/tim/code/django/django/db/backends/sqlite3/schema.py", line 104, in _remake_table
    self.effective_default(field)
  File "/home/tim/code/django/django/db/backends/base/schema.py", line 214, in effective_default
    default = field.get_db_prep_save(default, self.connection)
  File "/home/tim/code/django/django/db/models/fields/related.py", line 2017, in get_db_prep_save
    return self.target_field.get_db_prep_save(value, connection=connection)
  File "/home/tim/code/django/django/db/models/fields/__init__.py", line 721, in get_db_prep_save
    prepared=False)
  File "/home/tim/code/django/django/db/models/fields/__init__.py", line 969, in get_db_prep_value
    value = self.get_prep_value(value)
  File "/home/tim/code/django/django/db/models/fields/__init__.py", line 977, in get_prep_value
    return int(value)
TypeError: int() argument must be a string or a number, not 'R'

The issue is that ForeignKey.get_default() doesn't return the model instance's ID because self.remote_field.model is <class '__fake__.R'> when migrating while the default function returns a "real" model instance.

Change History (3)

comment:1 Changed 7 years ago by Simon Charette

Since no arguments are passed to the default value callable we've got two options:

  1. Document that foreign key callable defaults must return the value of the field they reference (pk unless to_field is set).
  2. Change the check in ForeignKey.get_default() to instance(field_default, models.Model) and field_default._meta.label == self.remote_field.model._meta.label.

I'd favor the first option.

comment:2 Changed 7 years ago by Tim Graham

Has patch: set

Option 1 seems okay to me. It's the second commit in this pull request.

comment:3 Changed 7 years ago by Tim Graham <timograham@…>

Resolution: fixed
Status: newclosed

In b60375d4:

Fixed #25129 -- Made model instance defaults work with migrations (refs #24919).

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