Rails nested form with uniquness condition - ruby-on-rails

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.

Related

Can I make a modification to a child force a validation on a parent when using Active Record Autosave Association?

I am looking for how to trigger a validation on a parent when a child is modified or
deleted.
Some background in case I am asking the wrong question:
I am trying to implement some basic double entry accounting in a Rails app. I have found a couple accounting Gems but they seem poorly maintained and overly complex. I also want to have complete control so I am building it myself.
My abstraction currently is a parent class, Txn (avoiding Transaction becuase although it is not a reserved word, it doesn't play well with Active Record). Txn has meta data about the transaction, as well as many LineItems.
LineItems are pretty simple; they have a debit_amount, credit_amount, account_id, and txn_id. I am trying to enforce a constraint on all LineItems belonging to a single Txn such that the sum of debits == the sum of credits. Autosave Association gets me most of the way there, allowing me to put a validator in Txn, but I want to ensure, at the ORM level, that if a LineItem is updated or deleted, that the parent Txn will re-validate and only allow the LineItem to be updated or deleted if all remaining LineItems balance.
I am hesistant to start messing with callbacks on the
LineItem triggering a change to the Txn for fear of creating cirular logic resulting in a race
condition.
I welcome any suggestions or resources as well as specific steps and or callbacks I should use.
class Txn
has_many :lineItems, autosave: true
end
class LineItem
belongs_to :txn
end

What's the best way to create groups/collections of objects (of the same model) in Rails?

I have an application which asks users questions, then (depending on the answer) passes through the relevant next_question to the controller - a kind of state machine/decision tree. For one use-case I need the ability to group together a set of answers, then loop back to the beginning question of the group and start building another group following the same chain.
So far I can direct the controller to start a new chain, but the existing chain just sort of floats there, and I want to segregate it into groups. As such I've defined certain questions as "collectable" by adding a column to the DB add_column :collectable, :boolean, default: false and flagging those I want to group together by setting that column to true. This works fine.
In the model, these items are called SaleQualifiers each has_one :answer and belongs_to :question (the question model holds the collectable information). On my SaleQualifiers model I have added a column collection_id, :integer - so the plan is to use some logic that will check whether a SaleQualifier's question is collectable or not, then pass an id to the collection_id column which I'll use to group these objects in the display and in the routing logic (for e.g. editing a SaleQualifier).
How do you go about setting the id's of a column outside of the standard Rails controlled id allocation? What would be the safest way of doing this? For my purposes I probably want to give more than one SaleQualifier the id=1, because SaleQualifiers belong_to SalesOpportunities and I can scope the SaleQualifier by the parent SalesOpportunity_id.
Any recommendations?

Cost of constantly querying an associated nested model (does activerecord cache results?)

Let's say I have User model with a boolean flag called email_notifications. This lets us know whether this user cares to receive email notifications from the app.
Recently I've been moving all these flags and settings into a separate sub-model call Preference to keep it away from the core user model. A User has_one Preference.
class User < ActiveRecord::Base
has_one :preference, dependent: :destroy
accepts_nested_attributes_for :preference
scope :email_recipients, -> { where(preference: { email_notification: true} ) }
def can_receive_email?
preference.email_notification
end
end
As you can see, I have a helper scope and a helper method on the model.
However, now that it lives in another table, I have always query the association :preference first and then get the value I need. It's not directly available from the User model itself any more.
Is this considered bad practice? Or does Rails help out and cache associated models so it doesn't have to re-query every time? I like organizing my various configs in a separate model, which I DO think is good practice, but this potential cost of constantly re-querying is stopping me.
Thanks!
EDIT:
I'm aware that I can do User.includes(:preference).blah_blah, however I want to avoid updating this in EVERY place where I call the user, and it may not always be known ahead of time whether I'm going to query the sub-model and need to include an .includes()
Rails associations are stored in memory after they're accessed so calling user.preference wouldn't hit the database except for the first time it's referenced.
Also, includes wouldn't have much of an effect in this case since this is a has_one. includes is usually useful for eager loading many associations in a single larger query rather than hitting the database each time a child object is called.

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 save 2 id in joint table for many-to-many relationship in rails 3.1

There are two models. One is rfq and another one is standard. It is like:
class Rfq << ActiveRecord::Base
has_and_belongs_to_many :standards
end
class Standard << ActiveRecord::Base
has_and_belongs_to_many :rfqs
end
Table rfqs_standards has been created. My question is when creating rfq, how to save the paid of rfq_id and standard_id in table rfqs_standards automatically.
Thought adding accepts_nested_attributes_for :standard in rfq model. However since there is no real attributes (but just pair of id) saved for this many-to-many relationship, this seems not the right way.
Both rfq and standard was declared in routes.rb as resources :rfqs and resources :standards.
The procedure is when creating rfq, standard will be picked up via a drop down list. Then rfq is saved and at the same time, a new entry in joint table is created. When creating new standard, there is no need to create entry in joint table.
Any suggestion to save the id in joint table? Thanks.
this is easier than you might think because it's handled automatically by ActiveRecord.
When you say "has_and_belongs_to_many", you're telling AR to associate those two models with a many-to-many relationship using the table, and for the most part you no longer need to worry about the join table. When you add an instance of Standard to an Rfq's list of standards, this will be done for you.
Here's an example:
rfq = Rfq.create
standard = Standard.create
rfq.standards << standard
We've created each of the objects, and the third line creates the connection, saving a new record in the rfqs_standards table with the proper ids. rqf.standards looks and acts like a normal array, but when you assign objects to it, ActiveRecord does the database work for you.
After creating the records, you could have also done:
standard.rfqs << rfq
You could also do both at the same time:
rfq = Rfq.create
standard rfq.standards.create
This created an rfq, then created a standard that is automatically connected to the rfq. You can do the same thing in reverse:
standard = Standard.create
rfq = standard.rfqs.create
I hope this helps!
UPDATE: Since you mentioned forms and automatic saving, read my article on nested attributes that shows how to implement that, including full code samples.

Resources