Validating and saving related models together - ruby-on-rails

I have 2 related models and I need to validate and create them together.
Application
class Application < ActiveRecord::Base
has_many :application_sessions, inverse_of: :application
ApplicationsSession
class ApplicationSession < ActiveRecord::Base
belongs_to :application, inverse_of: :application_sessions
If it was possible I'd like to create an Application through an ApplicationSession butapplication_session.build_application won't work because it will never be a valid record.
application_session.create_application wont work either because even if ApplicationSession is not a valid record, it will create an Application.
For the first one; it validates Application and ApplicationSession. This logic might work fine if I only skip application_id validation for ApplicationSession if when Application is a valid record. Still I prefer to use a more elegant solution if there is any.
For the second one; I can delete the Application afterwards if ApplicationSession is not a valid record but I didn't quite like this solution.
What is best approach to create/not to create those dependent records together with Rails?
Clarification:
Simply, I want the parent and child to be created together while there is no parent exists and a valid child about to save (valid expect it does not have any parent). If child is not a valid record, nothing should be created.

I think using new and build would work.
application = Application.new({attr1: val1, attr2: val2 ..})
application.application_sessions.build({attr1: val1, attr2: val2 ..})
application.save
In this way, if application is invalid, application and its new application_session would not be saved. The same goes if the application_session is invalid.
In terminal where you fired-up your rails console command, you would see something like:
(0.1ms) begin transaction
(0.1ms) rollback transaction
If application and its new application_session are both valid, both would be saved :)

From a data modelling point of view I'm not sure what you're trying to do.
I think you have a parent/child relationship where the parent is optional?
If you create a parent and then delete it you will have dangling keys in the child that point to nothing, be far better to just have nulls in there.
Is there any reason you can't just call new or create with a null parent id? Is the parent key mandatory? If so turn off the mandatory requirement and it should work. Build only works from parent to child, not the other way round when all the keys are null. I think you have to call save on the parent and the children are saved once the parent ID is known.
If the parent must exist for your app to work create a dummy one and have all the children you don't want to have a specific parent belonging to it, then you can easily find them again. Without knowing the flow of your app I'm not sure what else I could advise.

Related

Rails - create child and child's child at the same time

I have an association where user has_many user_items and user_items has_many user_item_images. With an already exiting user. I can create a new user_item
user_item = user.user_items.create(name: 'foo')
and I can create a new user_item_image
user_item.user_item_images.create(picture: file)
But I have a validation on user_item where a user_item can't exist without a user_item_image.
How can I create these two at the same time?
Firstly build both items and then save the parent. This will work because:
Validations are only called when saving the object in the database
Saving unsaved parent automatically saves all associated objects (via has_one and has_many, belongs_to object won't be saved without autosave option)
Validation is (most likely) based on the association and association includes non-saved but assigned objects in its target. Note however that you cannot use count in your validation, as it performs COUNT query and non-saved objects won't be included. Use size instead, or to be super sure (as size calls count for non-loaded associations) .to_a.size
Your code should like like:
user_item = user.user_items.build(name: 'foo')
user_item.user_item_images.build(picture: file)
user_item.save! # Bang for safety. If in controller, you can fork with if instead
BroiSatse has a correct answer. If you really want to do it in one single line you can:
user_item = user.user_items.create!(name: 'foo', user_item_images_attributes: { picture: file })
In my own code I usually make it look like BroiSatse's code simply for the sake of readability and maintainability - build the initial object, add related items, then save. It might be a little faster to do it with the single line, but unless you're doing it millions of times it's unlikely to make a difference.

How to handle a single form to update multiple unrelated models?

I am working on a legacy Rails 3.2 application that has a lot of settings a user can manage. Settings are associated with 3 types of model in the system: User, Company and CompanyUser. In order to avoid having to write database migrations each time a new type of setting is added
I've essentially created a key/value store (1 row for each setting) that has a polymorphic association with each of the above mentioned models. A base Setting class handles all of the common functionality like setting the key, relationships etc. each type of setting extends the base class and can contain it's own validation and/or logic. For example:
class Settings::EmailSignature < Setting
validates :whatever
end
For any model that requires a setting I've implemented a has_setting helper method that sets up the association and provides some delegates to directly get and set the setting without needing to go via the associated model object, the User model might look like:
class User < ActiveRecord::Base
has_setting :email_signature
end
This side of the code is working well, however the problem I have is when I create the form for the settings. For the user it might make sense to have User, Company and CompanyUser settings mixed together in the same form. Using nested attributes doesn't feel like a good solution in this situation as the settings are not related and there is no common parent object. I've considered using a form object to handle mapping each setting to the correct object but that doesn't feel like a great option either as each setting would require knowing it's id, the associated records id and it's type. This would not be particularly easy to manage when building the form.
I'm about to go down the route of having each setting in it's own form and having the record save automatically as the user edits each item. This would mean only a single record is ever saved at a time and will make things much simpler at the controller layer and also provide a lot of flexibility in how settings a grouped. Before I go down this route I wanted to see if there are any other options for submitting a single form in a single transaction that I may have overlooked?
Please note, this application is written in Rails 3.2 and is not in a state in which it can be easily upgraded to Rails 4 right now so any solutions need to work with Rails 3.2.

Eager load grandchild attribute without child in rails

In Rails, I'm trying to eager load something to speed it up. I've got a Parent-Child-Grandchild situation, but only need the Parent and an attribute of the Grandchild. If I...
Parent.all.includes(:child => :grandchild)
then Bullet gripes about an unused eager load of the child (which is true, I don't need it). Is there an easier way to have access to the Parent record and Grandchild.some_attribute?
A couple options...
1) Cache the value of the grand-child (denormalize the db, add a column and store the attribute) on the parent. Do this if there is only one grandchild and you want to frequently show this value in a situation where you have many parents.
2) can you do a has_many :through on the grandchildren? Then you can just do an include(:grandchildren). That may save generating the child objects.
3) If it really is child and grandchild (has_ones, or belongs_to), you can probably find a simple way to do a query (or scope) on grandchild based on the parent id. You can then run two queries, one to get parent.all, one to get grandchild.grandchild_of(parents.map(&:id)), and then loop in ruby and add the attribute to the parent as a attribute, ready for use when you loop through the parents later.
4) Lastly, do a specific, hand worked sql query to select the parent attributes and the grandchild.attribute. The attribute will be attached to the parent and accessible by parent.grandchild_attribute_name. Parent.select('parents.*, grandchild.attribute').joins(:grandchild). This you'll need to experiment with, depending on your model.

Add file to unsaved model entity in Rails?

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.

Rails nested form with uniquness condition

Rails 2.3.5, Ruby 1.8.7.
I have three models, Person, AcademicTerm, and PersonTermStatus.
class PersonTermStatus {
belongs_to :academic_term
belongs_to :person
validates_uniquness_of :academic_term_id, :scope => :person_id
# ...
}
class Person {
has_many :person_term_statuses
}
In a dynamic nested form for a Person record, I allow the editing of the person_term_statuses. But I get validation errors if the user does either of the following:
Deletes a status and creates a new one with the same academic term in the same change.
Swaps the academic terms between two existing statuses.
I understand why this is happening. In (1), the status marked for deletion is not actually deleted before validation of the new status's uniquness condition. In (2), the uniquness condition again is applied before any changes, and it finds another record with the same academic_term.
The problem is, I can't figure a way around this. Is there a known solution?
(My nested form implmenetation is currrently using pretty much exactly the technique from RailsCast [ Part I and Part II )
There is no workaround for this that I know of. However, you can add foreign keys to your database to enforce the uniqueness on the database side and then use the following approach.
Add a before_validation to the parent model that deletes and recreates as new records all the children. Then add a custom validation function that manually checks the children records for uniqueness based on what's in memory (rather than what's in the database).
The downsides to this approach include:
The children don't retain the same IDs.
The created timestamp changes.

Resources