Question about nested objects and .save_related() #314
-
I'm converting over from using SQLAlchemy ORM and I'm having some trouble with getting nested objects to save correctly. I have models and relationships set up like so: In order to seed the database for testings purposes, I'm adding some new employees, a vendor, a restaurant, and some orders. I create the vendor object w/ nested objects in it, and the order object. Then I run the following code:
After executing this code, the employees, vendor, and order get added correctly (including relationships) but the restaurants, menus, etc... don't get added. If I manually go in and add a loop to call .save() on each of the restaurant objects first, then they will also get added correctly (including relationships) but then menus, menuSections, etc... don't get added. If I do the same manually for menus, then it works but menuSections, menuItems, etc... don't get added. I feel like I'm missing something important about how .save_related() is supposed to work. Any ideas? I'm getting pretty frustrated trying to figure this out. Help would be much appreciated. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 13 replies
-
You don't provide a full code sample but I would assume you provide a primary keys for your deeply nested relations. Save related is a shortcut function that executes upsert on each model. The insert/update operation is based on the presence of the primary keys. If the primary key is present ormar assumes it's an update (otherwise you would get an integrity error with duplicated primary key if in fact the given models already exists and you try to insert them again). So if you want to create the full tree at once (so all models are not existing) the child models cannot have primary keys set (so should be autoincrement or columns with default). I can add something like force_insert flag but then it's on you to handle the integrity errors. I want to avoid running a separate query to check if the model already exists for each instance as that would slow down the whole process substantiality. |
Beta Was this translation helpful? Give feedback.
-
Thanks for the nice words😁 I assume you mean multiple related models of the same type/class are inserted separately, why not issue one query per model? The answer is mainly the complexity and primary keys. Since depending on the side of the relation and which one is which (fk or m2m) you need to save sometimes the left side first, other times the right side first and obtain the primary keys to populate the relations. So it's never a linear save So in order to save all models, keep the corresponding primary keys after save (bulk save return none in SQL so you need additional query there to fetch inserted primary keys and server defaults), save proper models in order including through models for m2m relations it becomes much more complicated than you would think in a fists place. All in all you need to create something similar to prefetch query. (Which on master is a little nightmare, it's completely rewritten in composite primary keys branch 😅). There might be also problems with primary key fetching after bulk save, since you can have simultaneous connections, so you cannot just return last inserted n rows as someone might have added something in the meantime, so you can break relations if you are not carefull. |
Beta Was this translation helpful? Give feedback.
You don't provide a full code sample but I would assume you provide a primary keys for your deeply nested relations.
Save related is a shortcut function that executes upsert on each model. The insert/update operation is based on the presence of the primary keys. If the primary key is present ormar assumes it's an update (otherwise you would get an integrity error with duplicated primary key if in fact the given models already exists and you try to insert them again).
So if you want to create the full tree at once (so all models are not existing) the child models cannot have primary keys set (so should be autoincrement or columns with default). I can add something like force_insert flag bu…