I have a parent model that has many child models
I want to destroy duplicate child records based on one parameter of the child.
I tried this:
parent.child.uniq! {|child| child.parameter }
It apparently works but the database does not reflects the change.
Your operation is performed on an object retrieved from database and not the actual database. Check out this post for the right answer.
Related
I have a procedure which receives two models, one which already exists, and another one which holds new attributes which I want to merge in the first one.
Since other parts of the program are holding the same reference to the new model, I can't just operate on the existing one. Therefor I do the following:
def merge(new_model, existing_model)
new_model.attributes = existing_model.attributes.merge(new_model.attributes)
new_model.id = existing_model.id
end
Now the new_model is being saved which gives me the uniqueness erorr (even though it's technically the same model). I also tried using the reload method, but that yields the same result.
Background:
The method above is run in a before_add callback on an association. I want to be able to call update on a model (with nested associations) without having to specify IDs of the nested models. This update is supposed to merge some associations, which is why I try to do the whole merge thing above.
You can't set the id of a model and then save the record expecting the id to be set since the id is the primary key of the database. So you are actually creating a whole new record and, thus, the uniqueness validation error. So you'll need to think of some other design to accomplish what you are wanting. It may help to know that what you are trying to do sounds similar to a deep_dup, except that ActiveRecord doesn't define this method (but Hash does).
I'm trying to work through a more complex issue, but I can't seem to find a definitive answer regarding the way has_many handles situations where an object already present in the association is added again.
What is the expected behaviour in the following situation (where alpha has many betas:
alpha.betas << beta_1
alpha.betas << beta_1
Should the second insertion be silently ignored (ensuring only unique betas in alpha's association?)
The docs state the following:
collection<<(object, …)
Adds one or more objects to the collection by setting their foreign keys to the collection's primary key. Note that this operation instantly fires update sql without waiting for the save or update call on the parent object, unless the parent object is a new record.
So my understanding is, that the object is referenced only once. It is therefor only once in the active_record relation alpha.betas. If you call it again, the foreign_key is set again, to the same value it already has. So this changes nothing.
According to the present Rails documentation, regarding the << operator on a has_many relationship on an existing object:
collection<<(object, …)
Adds one or more objects to the collection by setting their foreign keys
to the collection’s primary key.
(This is the interesting bit)
Note that this operation instantly fires update sql without waiting for
the save or update call on the parent object.
I didn't realize this would happen, I was quite surprised; I could have sworn this was not the case in the past, though I admit I could be wrong.
In either case, I haven't been able to find any additional documentation regarding this, however I wonder if there is a way to prevent this update?
My situation is simple, I merely have an object which exists in the database, which is being prepared for an "edit" page. I append one or multiple related objects before the page is render. C'est tout.
Update:
Apparently the same update-scenario also occurs if you set the has_many relationship directly from an array using the = operator.
Use the collection's build method. That won't immediately fire a SQL statement like the others do.
foo.bars.build(attributes)
foo.save
Lots of good information can be found here: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
Note: This method assumes you have the flexibility to create your objects through the build method rather than using Bar.new.
Is it possible to save a file in a dependent model entity when the parent (base) entity has not yet been saved?
I would like the user to be able to add images to a story prior to the story actually being saved. So my model is like this:
Story (Base Entity) has_many :pictures
+ Picture (Depedent Entity) belongs_to :story
In the Story controller, I have a separate action called add_image. However, because the instance variable #story does not seem to be persisted across requests, I cannot access it in this action.
What I have thought of doing so far is to store the #story variable in the session on create, so I can retrieve it from there. Will this work? May it have any unintended side-effects for subsequent requests? (E.g. what if I have the previous story stored in the session and add a picture to the wrong story?).
Just think about the implementation. There are two tables in the database and there should be some row story_id in the "pictures" table. What should be there, if story is not saved and it has no id yet?
Do it another way. Add some boolean flag to Stories, for example finished. Save all "unfinished" stories and change flag to "finished" on save in your application. Don't forget to regularly delete old "unfinished" (abandoned) stories and their pictures.
Apparently it is possible to save dependent records together with the main on first create:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
See the section "Unsaved objects and associations". You must use collection.build to add the dependent records.
Now my problem is still how to reliably persist the parent across requests, though I am thinking this may be possible by storing it in the session with the ID generated by mongoid (the only problem being that it is not portable across ORMs, I think).
This answers part of my question. So I will not accept any answer just yet, and once I have found a proper solution, I will give an update.
I have some models all linked together in memory (parent:child:child:child) and saved at the same time by saving the top-most parent. This works fine.
I'd like to tap into the after_create callback of one of the children to populate a changelog table. One of the attributes I need to copy/push into the changelog table is the child's foreign_key to it's direct parent, but it doesn't exist at the time after_create fires!?!
Without the after_create callback, I can look in the log and see that the child is being saved before it's parent (foreign key blank) then the parent is inserted... then the child is updated with the id from the parent. The child's after_create is firing at the right time, but it happens before Rails has had a chance to update the child with the foreign_key.
Is there any way to force Rails to save such a linkage of models in a certain order? ie.parent, then child (parent foreign_key exists), then that child's child (again, foreign_key is accessible) etc. ?? If not, how would I have my routine fire after a record is created AND get the foreign_key?
Seems a callback like this would be helpful: after_create_with_foreign_keys
Since I was building all my associated models in memory and relying on Rails to save everything when I saved the top-most parent, I couldn't tap into the after_create callbacks and utilize a foreign key (for a change_log table entry) because of the order in which Rails would save the models. Everything always ended up connected in the proper way, but sometimes a child record would be saved first, then the parent, then an update to the child record to insert the parent_id would happen.
My solution was to not build my models in memory, abandoning the idea of saving everything in one fell swoop. Instead, I would save the top-most model and then create its child via the after_create of that parent. That child's after_create would then create its child and so on. I like this arrangement much better as I have more control over my callbacks in relation to foreign keys. Lastly, the whole thing was wrapped in a db transaction so as to undo any inserts if something went horribly wrong along the way. This was my original reason for building everything in memory, so I'd have all my ducks in a row before saving. Model/db transactions alleviates the worry.
Could you use after_update to catch the child after the parent_id is available? When after_update fires, the parent_id will be available, so if the child is not in the table, insert it.