Django

Code

Ticket #5514 (closed: wontfix)

Opened 10 months ago

Last modified 7 months ago

Consistency issue when using ForeignKeys

Reported by: PhiR Assigned to: PhiR
Milestone: Component: Core framework
Version: SVN Keywords: select_related
Cc: Triage Stage: Design decision needed
Has patch: 0 Needs documentation: 0
Needs tests: 0 Patch needs improvement: 0

Description

Consider the following models:

class Operand(models.Model):
    value_1 = models.CharField(maxlength=200)
    value_2 = models.CharField(maxlength=200)

class Operation(models.Model):
    arg = models.ForeignKey(Operand)

Now let's suppose we want to do some work on Operands:

>>> common_arg = Operand(value_1="val1", value_2="val2")
>>> common_arg.save()
>>> op1 = Operation(arg = common_arg)
>>> op2 = Operation(arg = common_arg)
>>> op1.save()
>>> op2.save()
>>> operations = Operation.objects.all().select_related()
>>> operations
[<Operation: Operation object>, <Operation: Operation object>]
>>> operations[0].arg.value_1 = "new_val1"
>>> operations[0].arg.save()
>>> operations[1].arg.value_2 = "new_val2"
>>> operations[1].arg.save()
>>> modified_arg = Operand.objects.get(id=1)
# OOPS we lost one change here !!
>>> modified_arg.value_1
u'val1'
>>> modified_arg.value_2
u'new_val2'

# restore the initial state 
>>> modified_arg.value_2 = "val2"
>>> modified_arg.save()

# this is not limited to select_related but pervasive with FKs
>>> work_to_do = [(op, op.arg) for op in Operation.objects.all()]
>>> work_to_do[0][1].value_1 = "new_val1"
>>> work_to_do[0][1].value_1
'new_val1'

# oops, operation 2 will use the outdated argument
>>> work_to_do[1][1].value_1
u'val1'

#17 could provide a solution for this issue: caching instances in memory so get() and friends can return the instance we already have instead of creating a new, distinct one.

The issue is especially easy to encounter with select_related because all related fields will be preloaded in distinct instances in memory. Walking Operation.objects.all() and modifying the Operands from there would load an new instance from the DB for each operation and work as expected in most cases.

Attachments

Change History

09/16/07 13:10:51 changed by PhiR

  • status changed from new to assigned.
  • needs_better_patch changed.
  • stage changed from Unreviewed to Design decision needed.
  • needs_tests changed.
  • needs_docs changed.

12/01/07 14:28:56 changed by jacob

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

Though I agree this is counter-intuitive, this isn't going to be fixed. Doing so would require a complex identity-mapper style solution, and that doesn't fit with Django's "80% ORM" philosophy.


Add/Change #5514 (Consistency issue when using ForeignKeys)




Change Properties
Action