#6445 closed Uncategorized (fixed)
models.ForeignKey should accept instances as default value
| Reported by: | Owned by: | Philippe Raoult | |
|---|---|---|---|
| Component: | Core (Other) | Version: | dev |
| Severity: | Normal | Keywords: | foreignkey default |
| Cc: | Triage Stage: | Ready for checkin | |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
It looks like the 'default' parameter of a models.ForeignKey field requires one to pass an id, passing an instance doesn't work. This is rather contra-intuitive as setting a ForeignKey field of an instance of a model to an instance of the foreign model is supported.
Here's a sample:
#Models
class Foo(models.Model):
a = models.CharField(max_length=10)
def get_foo():
return Foo.objects.get(id=1)
class Bar(models.Model):
b = models.CharField(max_length=10)
a = models.ForeignKey(Foo, default=get_foo)
#Testing
In [8]: f = Foo(a='abc')
In [9]: f.save()
# Forgot to do a print here, but f.id == 1
In [10]: b = Bar(b='def')
In [11]: b.a = f
# Works fine, as expected
In [12]: b.save()
In [13]: b.a
Out[13]: <Foo: Foo object>
In [14]: c = Bar(b='ghi')
# Using 'default', callable returns f, doesn't work, unlike expected
In [15]: c.save()
---------------------------------------------------------------------------
pysqlite2.dbapi2.InterfaceError Traceback (most recent call last)
/home/nicolas/Projects/django/app/<ipython console>
/home/nicolas/Projects/django/django-trunk/django/db/models/base.py in save(self, raw)
259 cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % \
260 (qn(self._meta.db_table), ','.join(field_names),
--> 261 ','.join(placeholders)), db_values)
262 else:
263 # Create a new record with defaults for everything.
/home/nicolas/Projects/django/django-trunk/django/db/backends/util.py in execute(self, sql, params)
16 start = time()
17 try:
---> 18 return self.cursor.execute(sql, params)
19 finally:
20 stop = time()
/home/nicolas/Projects/django/django-trunk/django/db/backends/sqlite3/base.py in execute(self, query, params)
131 def execute(self, query, params=()):
132 query = self.convert_query(query, len(params))
--> 133 return Database.Cursor.execute(self, query, params)
134
135 def executemany(self, query, param_list):
InterfaceError: Error binding parameter 1 - probably unsupported type.
Running this same query in a web view shows 'params' is [id, <Foo instance>], where the second expected value should be the id of the used Foo instance.
Attachments (1)
Change History (7)
comment:1 by , 18 years ago
comment:2 by , 18 years ago
| Has patch: | set |
|---|---|
| Owner: | changed from to |
| Status: | new → assigned |
| Triage Stage: | Unreviewed → Design decision needed |
My patch fixes the issue by overriding get_default in ForeignKey to handle that case. I threw some tests in for good measure.
comment:3 by , 18 years ago
| Triage Stage: | Design decision needed → Ready for checkin |
|---|
comment:4 by , 18 years ago
| Resolution: | → fixed |
|---|---|
| Status: | assigned → closed |
comment:5 by , 7 years ago
| Easy pickings: | unset |
|---|---|
| UI/UX: | unset |
Was this behavior intentionally reverted? In Django 2.1.7, I'm getting the error:
TypeError: int() argument must be a string, a bytes-like object or a number, not 'Group'
It's coming from this code (because I can fix it by adding .pk to the get call):
# Can't serialize lambdas in migrations, but functions are fine
def const_my_group():
return Group.objects.get(name=settings.MY_GROUP)
class Task(models.Model):
group = models.ForeignKey(Group, default=const_my_group, on_delete=models.PROTECT)
EDIT: I don't want to spam a bunch of tickets with one issue, but it may actually be a reversion of this behavior:
comment:6 by , 7 years ago
| Severity: | → Normal |
|---|---|
| Type: | → Uncategorized |
Yes, you should use .pk -- see #25129.
Forgot something: this does work, but it's a rather ugly workaround: