[Django] #22741: Makemigrations fails when using lambda as default for ForeignKey field

205 views
Skip to first unread message

Django

unread,
May 31, 2014, 7:08:27 AM5/31/14
to django-...@googlegroups.com
#22741: Makemigrations fails when using lambda as default for ForeignKey field
----------------------------------------------+----------------------------
Reporter: rednaw | Owner: nobody
Type: Bug | Status: new
Component: Database layer (models, ORM) | Version: master
Severity: Normal | Keywords: migrations
Triage Stage: Unreviewed | foreignkey
Easy pickings: 0 | Has patch: 0
| UI/UX: 0
----------------------------------------------+----------------------------
When using a lambda function as a default value for a ForeignKey field,
`makemigrations` will fail with the exception:

ValueError: Cannot serialize function: lambda

This is the code:

from django.db import models


class House(models.Model):
address = models.CharField(max_length=100)


class Person(models.Model):
house = models.ForeignKey(House, default=lambda:
House.objects.all()[0])
name = models.CharField(max_length=100)

I tried to change the lambda to a defined function like this:

from django.db import models


class House(models.Model):
address = models.CharField(max_length=100)


def first_house():
House.objects.all()[0]


class Person(models.Model):
house = models.ForeignKey(House, default=first_house)
name = models.CharField(max_length=100)

In this case, `makemigrations` works, but `migrate` will now fail, with
the exception:

TypeError: int() argument must be a string or a number, not 'House'

I'm testing this in Python 3 btw.

--
Ticket URL: <https://code.djangoproject.com/ticket/22741>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
May 31, 2014, 1:19:55 PM5/31/14
to django-...@googlegroups.com
#22741: Migrate fails with `TypeError` when using a callable default for a
`ForeignKey`
-------------------------------------+-------------------------------------
Reporter: rednaw | Owner: nobody
Type: Bug | Status: closed
Component: Database layer | Version: master
(models, ORM) | Resolution: needsinfo
Severity: Normal | Triage Stage:
Keywords: migrations | Unreviewed
foreignkey | Needs documentation: 0
Has patch: 0 | Patch needs improvement: 0
Needs tests: 0 | UI/UX: 0
Easy pickings: 0 |
-------------------------------------+-------------------------------------
Changes (by charettes):

* status: new => closed
* cc: charettes (added)
* needs_better_patch: => 0
* needs_tests: => 0
* needs_docs: => 0
* resolution: => needsinfo


Comment:

Please use the preview options before submitting a ticket -- code blocks
should be wrapped in `{{{ }}}` in order to be displayed properly.

Unfortunately the migration framework doesn't support `lambda`s
deconstruction, see #21037 and #22351 for details.

Since you seems to have another issue setting `default=first_house` we'd
need the full `TypeError` traceback to determine if it's actually a Django
bug.

The fact that your `first_house` function doesn't return anything (copy-
pasta from the `lambda`?) is a bit suspicious here.

--
Ticket URL: <https://code.djangoproject.com/ticket/22741#comment:1>

Django

unread,
Mar 25, 2015, 9:00:48 AM3/25/15
to django-...@googlegroups.com
#22741: Migrate fails with `TypeError` when using a callable default for a
`ForeignKey`
-------------------------------------+-------------------------------------
Reporter: rednaw | Owner: nobody

Type: Bug | Status: closed
Component: Database layer | Version: master
(models, ORM) |
Severity: Normal | Resolution: needsinfo
Keywords: migrations | Triage Stage:
foreignkey | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Description changed by timgraham:

Old description:

> When using a lambda function as a default value for a ForeignKey field,
> `makemigrations` will fail with the exception:
>
> ValueError: Cannot serialize function: lambda
>
> This is the code:
>
> from django.db import models
>

> class House(models.Model):
> address = models.CharField(max_length=100)
>

> class Person(models.Model):
> house = models.ForeignKey(House, default=lambda:
> House.objects.all()[0])
> name = models.CharField(max_length=100)
>
> I tried to change the lambda to a defined function like this:
>
> from django.db import models
>

> class House(models.Model):
> address = models.CharField(max_length=100)
>

> def first_house():
> House.objects.all()[0]
>

> class Person(models.Model):
> house = models.ForeignKey(House, default=first_house)
> name = models.CharField(max_length=100)
>
> In this case, `makemigrations` works, but `migrate` will now fail, with
> the exception:
>
> TypeError: int() argument must be a string or a number, not 'House'
>
> I'm testing this in Python 3 btw.

New description:


def first_house():
House.objects.all()[0]

--

--
Ticket URL: <https://code.djangoproject.com/ticket/22741#comment:2>

Django

unread,
Sep 1, 2016, 5:40:15 AM9/1/16
to django-...@googlegroups.com
#22741: Migrate fails with `TypeError` when using a callable default for a
`ForeignKey`
-------------------------------------+-------------------------------------
Reporter: rednaw | Owner: nobody

Type: Bug | Status: closed
Component: Database layer | Version: master
(models, ORM) |
Severity: Normal | Resolution: needsinfo
Keywords: migrations | Triage Stage:
foreignkey | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by reinout):

I'm seeing something that looks like the same issue. I *do* have a
traceback :-) It is django 1.8.14 (I'm updating an ancient project):

{{{
Traceback (most recent call last):
File "bin/test", line 42, in <module>
sys.exit(djangorecipe.binscripts.test('trs.testsettings', '', 'trs'))
File
"/code/eggs/djangorecipe-2.2.1-py3.5.egg/djangorecipe/binscripts.py", line
22, in test
management.execute_from_command_line(sys.argv)
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/core/management/__init__.py",
line 354, in execute_from_command_line
utility.execute()
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/core/management/__init__.py",
line 346, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/core/management/commands/test.py",
line 30, in run_from_argv
super(Command, self).run_from_argv(argv)
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/core/management/base.py", line
394, in run_from_argv
self.execute(*args, **cmd_options)
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/core/management/commands/test.py",
line 74, in execute
super(Command, self).execute(*args, **options)
File
"/code/eggs/raven-5.7.2-py3.5.egg/raven/contrib/django/management/__init__.py",
line 41, in new_execute
return original_func(self, *args, **kwargs)
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/core/management/base.py", line
445, in execute
output = self.handle(*args, **options)
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/core/management/commands/test.py",
line 90, in handle
failures = test_runner.run_tests(test_labels)
File "/code/eggs/django_nose-1.4.4-py3.5.egg/django_nose/runner.py",
line 303, in run_tests
result = self.run_suite(nose_argv)
File "/code/eggs/django_nose-1.4.4-py3.5.egg/django_nose/runner.py",
line 241, in run_suite
addplugins=plugins_to_add)
File "/code/eggs/nose-1.3.7-py3.5.egg/nose/core.py", line 121, in
__init__
**extra_args)
File "/usr/lib/python3.5/unittest/main.py", line 94, in __init__
self.runTests()
File "/code/eggs/nose-1.3.7-py3.5.egg/nose/core.py", line 207, in
runTests
result = self.testRunner.run(self.test)
File "/code/eggs/nose-1.3.7-py3.5.egg/nose/core.py", line 50, in run
wrapper = self.config.plugins.prepareTest(test)
File "/code/eggs/nose-1.3.7-py3.5.egg/nose/plugins/manager.py", line 99,
in __call__
return self.call(*arg, **kw)
File "/code/eggs/nose-1.3.7-py3.5.egg/nose/plugins/manager.py", line
167, in simple
result = meth(*arg, **kw)
File "/code/eggs/django_nose-1.4.4-py3.5.egg/django_nose/plugin.py",
line 82, in prepareTest
self.old_names = self.runner.setup_databases()
File "/code/eggs/django_nose-1.4.4-py3.5.egg/django_nose/runner.py",
line 490, in setup_databases
return super(NoseTestSuiteRunner, self).setup_databases()
File "/code/eggs/Django-1.8.14-py3.5.egg/django/test/runner.py", line
166, in setup_databases
**kwargs
File "/code/eggs/Django-1.8.14-py3.5.egg/django/test/runner.py", line
370, in setup_databases
serialize=connection.settings_dict.get("TEST", {}).get("SERIALIZE",
True),
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/db/backends/base/creation.py",
line 368, in create_test_db
test_flush=not keepdb,
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/core/management/__init__.py",
line 120, in call_command
return command.execute(*args, **defaults)
File
"/code/eggs/raven-5.7.2-py3.5.egg/raven/contrib/django/management/__init__.py",
line 41, in new_execute
return original_func(self, *args, **kwargs)
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/core/management/base.py", line
445, in execute
output = self.handle(*args, **options)
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/core/management/commands/migrate.py",
line 222, in handle
executor.migrate(targets, plan, fake=fake, fake_initial=fake_initial)
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/db/migrations/executor.py",
line 110, in migrate
self.apply_migration(states[migration], migration, fake=fake,
fake_initial=fake_initial)
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/db/migrations/executor.py",
line 148, in apply_migration
state = migration.apply(state, schema_editor)
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/db/migrations/migration.py",
line 115, in apply
operation.database_forwards(self.app_label, schema_editor, old_state,
project_state)
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/db/migrations/operations/fields.py",
line 62, in database_forwards
field,
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/db/backends/sqlite3/schema.py",
line 179, in add_field
self._remake_table(model, create_fields=[field])
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/db/backends/sqlite3/schema.py",
line 77, in _remake_table
self.effective_default(field)
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/db/backends/base/schema.py",
line 211, in effective_default
default = field.get_db_prep_save(default, self.connection)
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/db/models/fields/related.py",
line 1958, in get_db_prep_save
return self.related_field.get_db_prep_save(value,
connection=connection)
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/db/models/fields/__init__.py",
line 710, in get_db_prep_save
prepared=False)
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/db/models/fields/__init__.py",
line 977, in get_db_prep_value
value = self.get_prep_value(value)
File
"/code/eggs/Django-1.8.14-py3.5.egg/django/db/models/fields/__init__.py",
line 985, in get_prep_value
return int(value)
TypeError: int() argument must be a string, a bytes-like object or a
number, not 'YearWeek'
}}}

The relevant field on my model looks like this:

{{{
start = models.ForeignKey(
'YearWeek',
blank=True,
null=True,
default=this_year_week,
related_name="starting_projects",
verbose_name="startweek")
}}}

So... a callable as a default that returns an object instance (of the
correct type), but it looks like the migration mechanism wants a foreign
key ID instead of an actual instance.

Two possible things that might make it a corner case:

- I see the error when I run my tests. It happens in the test setup. The
complication/cornercase is that the callable tries to grab the instance
from memcache. So the test might get an instance somehow without it
actually having an ID in the database. Doesn't sound very logical, I'm
just mentioning it :-)

- It is an initial migration.

I haven't investigated further. I also don't know yet if the issue should
be re-openend. I'm leaving it closed for now and I'll do some further
digging.

--
Ticket URL: <https://code.djangoproject.com/ticket/22741#comment:3>

Django

unread,
Sep 1, 2016, 5:53:28 AM9/1/16
to django-...@googlegroups.com
#22741: Migrate fails with `TypeError` when using a callable default for a
`ForeignKey`
-------------------------------------+-------------------------------------
Reporter: rednaw | Owner: nobody

Type: Bug | Status: closed
Component: Database layer | Version: master
(models, ORM) |
Severity: Normal | Resolution: needsinfo
Keywords: migrations | Triage Stage:
foreignkey | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by reinout):

Yes, it can stay closed.

See #23454, especially comment 5 by Tim:
https://code.djangoproject.com/ticket/23454#comment:5

Basically: yes, it should be an ID. (I assume an instance was OK for
south, which I used previously).

--
Ticket URL: <https://code.djangoproject.com/ticket/22741#comment:4>

Reply all
Reply to author
Forward
0 new messages