2

I use Django 2.0 and mysql database and my project model is like:

class Parent(models.Model):
    name = models.CharField()

class Child(models.Model):
    number = models.IntegerField()
    parent = models.ForeignKey(Parent)

what I want is to save both parent and child object together at the same time so that if in any case the child object has error(like number field is "some text") and can not be saved the parent object doesn't save.

in Flask (with postgresql) there is add(object) and add_all([parent, child]) methods and I used add_all so if child has error parent doesn't save neither.

but in Django I couldn't find this method.

the default method is:

parent = Parent(name = "my name")
parent.save()

child = Child(number=3, parent=parent)
child.save()

what I want is something like this:

parent = Parent(name = "my name")
child = Child(number=3, parent=parent)

and then:

child.save(with related parent)

or:

save(parent, child together)

PS: I read this link and I think "SET" method is what I need but I'm not sure how to use it and if it's the solution: django relations

1 Answers1

8

What I want is to save both parent and child object together at the same time so that if in any case the child object has error [...] and can not be saved the parent object doesn't save.

Sounds to me like you can use a database transaction for this. Within a transaction, either all the operations go through, or all of them get rolled back, no half-ways (no child is saved without the parent being saved in your case)

Below is an example using transaction.atomic:

from django.db import DatabaseError, transaction
try:
    with transaction.atomic():
        parent = Parent(name = "my name")
        parent.save()

        child = Child(number=3, parent=parent)
        child.save()

except DatabaseError:
    # Handle the case where an error prevented the operation
bakkal
  • 54,350
  • 12
  • 131
  • 107
  • I can't use this method because I have up to 4 layers of related objects and in your code the parent object will be created in database and I have to delete it in except. what I want is to forbid saving parent in database. – Syed Mohammad Hosseini Dec 25 '17 at 03:56
  • By the way, you mentioned Flask with PostgreSQL. If you used SQLAlchemy with Flask, then know that it defaults to running inside a transaction and does not autocommit, and you have to run e.g. `db.session.commit()` which commits both the child and the parent or neither. – bakkal Dec 25 '17 at 04:00
  • yes. exactly. I want the same thing in django to commit both parent and child together at the same time. both or neither. Thanks anyway. – Syed Mohammad Hosseini Dec 25 '17 at 04:05
  • "the parent object will be created in the database and I have to delete it in except", no that's the point of running inside a transaction, it is atomic, everything goes or nothing goes. If you have more than 2 relations (doesn't mater really), put all the operations in a transaction, that will guarantee it runs atomically. Again, this is why databases have transactions. – bakkal Dec 25 '17 at 04:05
  • Put simply: all the operations in `transaction.atomic` either go or none of the operations there go. The `except` block is where you can know that the operation didn't go, it is not a place for you to clean up the lingering objects, you can't do that because the transaction would have already been rolled back automatically. – bakkal Dec 25 '17 at 04:08
  • Thank you. atomic works. Exception handling should be modified but it works – Syed Mohammad Hosseini Dec 25 '17 at 04:19