Saving related models in one single call fails

If you have this 2 related models


class Sale(models.Model):
    transaction_id = models.CharField(max_length=50)
    has_discount = models.BooleanField(default=False)


class SaleItem(models.Model):
    sale = models.ForeignKey(
        Sale,
        related_name='sale',
        on_delete=models.SET_NULL,
        blank=True,
        null=True
    )

    product_name = models.CharField(max_length=50)
    quantity = models.IntegerField()

Why can’t I just do this to create a sale?

sale_obj = Sale(transaction_id="ts89990", has_discount=True)
item = SaleItem(sale=sale_obj, product_name="Sample product", quantity=9)
item.save()

This throws an error ValueError: save() prohibited to prevent data loss due to unsaved related object

Instead you have to explicitly create the Sale first in the db

sale_obj = Sale.objects.create(transaction_id="ts89990", has_discount=True)
item = SaleItem(sale=sale_obj, product_name="Sample product", quantity=9)
item.save()

Correct, because a foreign key is related to an object by its primary key - which doesn’t exist until the related object has been saved. This is by design and is exactly as it should be.

You can save them both at effectively the same time by ensuring the two statements are within the same transaction.

Other ORMs implicitly creates and do the association for you when you append a related object without a primary key. Can’t same be done for Django?

Honestly, I don’t understand what you’re trying to say here in terms of database (not ORM) operations.

The save method of a model results in an SQL statement being created and sent to the database.

You can’t insert a row into a database with a non-null field being null.

Therefore, it wouldn’t make any sense for Django to try and do that.

The row being inserted (or updated) must comply with all database integrity constraints, otherwise the database is going to reject it.

Also note that neither of these statements result in a database operation taking place, and therefore do not create an error:

I have to say I’m with @kaypee90 on this. If you have created the related object, and set the relevant element in the parent object to that value, it is logical to think/hope that the ORM recognises that the child object does not have an id, or has been changed, and therefore needs to be saved when the parent is saved.

That’s independent of the SQL needed to do all of that.

This really isn’t an opinion-type issue here, I’m trying to explain how the Django ORM works. It is what it is.

When you try to save an object to the database (which is what the save method does), it must create a valid row - the database isn’t going to accept anything that isn’t valid.

And the design of the ORM is such that it doesn’t try to resolve related objects automatically.

<opinion>
Yes, an argument could be made that it would be “better” if it did. Fortunately, the design of Django itself allows you to implement that by overriding the save method to examine related objects and perform those operations should you choose to handle it that way.

Personally, I think trying to implement that globally would be an incredibly bad idea because of all the edge-cases and unusual situations that I could see occurring. I think the work necessary to solve the general case far exceeds any benefit that may be obtained.

But that’s just my opinion.
<opinion>

I just saw this issue had been raised some years back and it got shot down

https://code.djangoproject.com/ticket/26366