Code

Opened 5 years ago

Closed 5 years ago

Last modified 5 years ago

#9553 closed (invalid)

Save of ForeignKeys's model doesn't cause update of _id field

Reported by: oldium Owned by: nobody
Component: Database layer (models, ORM) Version: 1.0
Severity: Keywords:
Cc: Triage Stage: Unreviewed
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: UI/UX:

Description

For two models

class Inner(models.Model):
    field = models.CharField(max_length=10)

class Outer(models.Model):
    inner = models.ForeignKey(Inner)
    other_field = models.CharField(max_length=15)

It is not possible to write

inner = Inner(field="test")
outer = Outer(inner=inner, other_field="other test")
inner.save()
outer.save()

It ends-up with

IntegrityError: null value in column "inner_id" violates not-null constraint

Attachments (1)

django-related_model_fix.patch (1.9 KB) - added by oldium 5 years ago.
Proof of concept plus test

Download all attachments as: .zip

Change History (8)

comment:1 Changed 5 years ago by kmtracey

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Resolution set to invalid
  • Status changed from new to closed

Right, inner does not have a primary key value until you call save() on it. So you cannot correctly specify it as the target of outer's key before then. Your code should be:

inner = Inner(field="test")
inner.save()
outer = Outer(inner=inner, other_field="other test")
outer.save()

I'm pretty sure this is spelled out somewhere in the docs though I can't find it at the moment. The fact that save() is what creates the object's pk is noted here:

http://docs.djangoproject.com/en/dev/ref/models/instances/?from=olddocs#auto-incrementing-primary-keys

comment:2 Changed 5 years ago by oldium

  • Resolution invalid deleted
  • Status changed from closed to reopened

Actually the problem is not that the inner.save() itself doesn't update the outer's inner_id field, the problem is that the inner_id field is not "intelligently" taken from inner's pk during outer.save(). Outer has all the information to be able to save itself.

To illustrate the problem in detail here is a working code snippet:

inner.save()
outer.inner = outer.inner
outer.save()

My opinion is that this is not the "right thing". If you think this is really wanted behaviour, feel free to close the ticket.

This is from my real-life project (not with Inner and Outer classes, of course). I can live with the workaround, but this doesn't follow the DRY principle.

comment:3 Changed 5 years ago by oldium

The root cause of the problem is that there are two fields for the same thing:

  • inner_id (Inner's primary key as part of model)
  • inner (Inner instance)

comment:4 Changed 5 years ago by oldium

  • Has patch set

With the attached patch the saving works like a charm. I got inspired by the parent's pk update machinery in Model.save_base and used it to update all RelatedField's pk (that is maybe wrong).

comment:5 Changed 5 years ago by Alex

I'm not entirely sure I follow the issue, but either way, can you please include a test in your patch that fails before your patch is applied, and passes afterwords.

Changed 5 years ago by oldium

Proof of concept plus test

comment:6 Changed 5 years ago by ubernostrum

  • Resolution set to invalid
  • Status changed from reopened to closed

This is, I believe, intended behavior: assigning an object to a relation before it has a value for whatever column is used in the relation isn't supposed to work. If you wish to argue for that as a new feature in Django, please take it to the django-developers mailing list rather than endlessly reopening this ticket.

comment:7 Changed 5 years ago by oldium

All my arguments are here (the model has all the information for save), so let's leave it closed. I do not want to argue for such a stupid thing :-)

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.