Django

Code

Ticket #6445 (closed: fixed)

Opened 6 months ago

Last modified 4 months ago

models.ForeignKey should accept instances as default value

Reported by: eikke@eikke.com Assigned to: PhiR
Milestone: Component: Core framework
Version: SVN Keywords: foreignkey default
Cc: Triage Stage: Ready for checkin
Has patch: 1 Needs documentation: 0
Needs tests: 0 Patch needs improvement: 0

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

6645.diff (2.0 kB) - added by PhiR on 03/19/08 13:41:31.
fix + tests + doc

Change History

01/22/08 20:05:52 changed by Nicolas Trangez <eikke@eikke.com>

  • needs_better_patch changed.
  • needs_tests changed.
  • needs_docs changed.

Forgot something: this does work, but it's a rather ugly workaround:

a = models.ForeignKey(Foo, default=lambda: get_foo().id)

03/19/08 13:32:23 changed by PhiR

  • owner changed from nobody to PhiR.
  • status changed from new to assigned.
  • has_patch set to 1.
  • stage changed from Unreviewed to 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.

03/19/08 13:41:31 changed by PhiR

  • attachment 6645.diff added.

fix + tests + doc

03/19/08 14:37:18 changed by jacob

  • stage changed from Design decision needed to Ready for checkin.

03/20/08 01:56:24 changed by mtredinnick

  • status changed from assigned to closed.
  • resolution set to fixed.

(In [7331]) Fixed #6445 -- Allow model instances to be used as a default for ForeignKeys? (via a callable). Also updates the documentation of the "default" attribute to indicate a callable can be used. Thanks, Philipe Raoult.


Add/Change #6445 (models.ForeignKey should accept instances as default value)




Change Properties
Action