#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 , 17 years ago
comment:2 by , 17 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 , 17 years ago
Triage Stage: | Design decision needed → Ready for checkin |
---|
comment:4 by , 17 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
comment:5 by , 6 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 , 6 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: