How to save an array of models in Rails - ruby-on-rails

I've got models called answers, surveys, questions. Now in a survey, there can be up to 200 questions and so one survey can generate up to 200 answer-models in a page.
The question is: How can I save the array of answers I have in a single db-action and not iterate over the array and save each element individually, which takes a lot of time relatively?

You can pass the 'belongs_to' relationship an :autosave symbol. This will cause the answers to be automatically saved when you save the parent. Something like this then would probably be what you want:
class Survey < ActiveRecord::Base
has_many :questions
end
class Question < ActiveRecord::Base
belongs_to :survey, :autosave
has_one :answer
end
class Answer < ActiveRecord::Base
belongs_to :question, :autosave
end
I don't know how exactly this will perform behind the scenes, but it will allow ActiveRecord to optimise the SQL and removes the need for you to iterate explicitly over the relationships.

No matter what you do, don't forget to wrap your multiple inserts in a transaction. Will really speed things up.

Related

Array not saving from one model to another in rails

I am trying to save an array of multiple ids (item_variation_ids) to a model called items_stock from item variations model. In a column called item_variation_ids in item_stock, it is saving the ids like [1,2,3] for twice. I want the item_variation_ids to be saved once only with 1,2,3 in a single column.
My item_variation model
#app/models/item_variation
class ItemVariation < ApplicationRecord
belongs_to :item
validates_associated :item
after_save :add_to_item_stock
def add_to_item_stock
ItemStock.create(item_variation_ids: ItemVariation.ids, items_id: items_id)
end
end
My item model
#app/models/item
class Item < ApplicationRecord
has_many :item_variations, foreign_key: :items_id
has_many :item_stocks, foreign_key: :items_id
accepts_nested_attributes_for :item_stocks
end
My item_stock model
#app/models/item_stock
class ItemStock < ApplicationRecord
belongs_to :item
end
But how do you know which ItemVariation ids should go on that ItemStock? and you are creating one ItemStock each time any variation gets saved. I don't even think you need to set that ids array since the ItemStock already belongs to an Item which has many variations (#item_stock.item.variations and you are done).
Also now you are talking about a stock_qty attribute you never mentioned before, you are never setting it on the callback and you didn't show your database schema. where does that amout come from? is an attribute on the variation that you want to sum to the current item_stock?
I also don't understand why an item has many item stocks for the code you are showing.
I'll do a wild guess and suggest you do something like:
ItemStock
belongs_to :item
belongs_to :item_variation
end
ItemVariation
after_save :add_to_item_stock
def add_to_item_stock
item_stock = self.item.item_stock.where(item_variation_id: self.id).first_or_initialize
item_stock.stock_qty = self.stock_qty
item_stock.save
end
end
but as I said, it's a wiiiiild guess. I'd recommend you to first try to understand what you are doing, because it seems like you just copied to code from that question you linked and you are no actually understanding it.

Scaling large data in Rails4

I'm making a Q&A site. I have three models for now:
Question
Answer
User
Both Question and Answer can be voted for, so I need a way to store who voted on the model, to prevent multiple votes on one model by same user.
Would be a good idea to create a vote model? Where either the question_id or the answer_id will be empty in each. I'm afraid that this will create a lot of junk and slow the application down.
My other idea is to store the user_id in a hash in the Answer and in the Question.
If the user is already present in the hash it prevents voting. Or store the answer and question ids in the user.
What would be the Rails4 way to be fast but store most user interactions in models. What can be KISS, DRY and fast?
You should use a polymorphic model if you have multiple models the Vote can refer to.
class Vote < ActiveRecord::Base
belongs_to :voteable, polymorphic: true
end
class Question < ActiveRecord::Base
has_many :votes, as: :voteable
end
class Answer < ActiveRecord::Base
has_many :votes, as: :voteable
end
I don't think you have to worry about the speed for quite a while if you use indexes on the associations. Just put this snippet in a migration and you should be good to go!
add_index :votes, [:voteable_name, :voteable_id]

Rails: How to Convert has_one to has_many association

Assuming
class Kid < ActiveRecord::Base
has_one :friend
end
class Friend< ActiveRecord::Base
belongs_to :kid
end
How can I change this to
class Kid < ActiveRecord::Base
has_many :friends
end
class Friend< ActiveRecord::Base
belongs_to :kid
end
Will appreciate your insight...
Collection
The bottom line is that if you change your association to a has_many :x relationship, it creates a collection of the associative data; rather than a single object as with the single association
The difference here has no bearing on its implementation, but a lot of implications for how you use the association throughout your application. I'll explain both
Fix
Firstly, you are correct in that you can just change your has_one :friend to has_many :friends. You need to be careful to understand why this works:
ActiveRecord associations work by associating something called foreign_keys within your datatables. These are column references to the "primary key" (ID) of your parent class, allowing Rails / ActiveRecord to associate them
As long as you maintain the foreign_keys for all your Friend objects, you'll get the system working no problem.
--
Data
To expand on this idea, you must remember that as you create a has_many association, Rails / ActiveRecord is going to be pulling many records each time you reference the association.
This means that if you call #kind.friends, you will no longer receive a single object back. You'll receive all the objects from the datatable - which means you'll have to call a .each loop to manipulate / display them:
#kid = Kid.find 1
#kid.friends.each do |friend|
friend.name
end
If after doing this changes you have problem calling the save method on the order.save telling you that it already exists, and it not allowing you to actually have many order records for one customer you might need to call orders.save(:validate=> false)
You have answered the question. Just change it in model as you've shown.

Persist array from before_save callback for ActiveModel::Dirty like change tracking on has_many through relationship

Firstly, apologies for the snappy question title! It does however sum up what I am trying to do.
I've been using ActiveModel::Dirty successfully to create a kind of audit trail on various model attributes within my app (like on Product below).
I've now have a fairly pressing request to be able to track the changes (additions & deletions in this case) on an associated has_many through relationship.
The models in question are:
class Product < ActiveRecord::Base
has_many :products_territories, :dependent => :destroy
has_many :territories, :through => :products_territories
end
class Territory < ActiveRecord::Base
has_many :products_territories
has_many :products, :through => :products_territories
end
class ProductsTerritory < ActiveRecord::Base
belongs_to :territory
belongs_to :product
end
I've failed with using ActiveModel::Dirty, it doesn't seem possible, so am trying my own thing which on the surface is quite simple; grab an array of a product's products_territories before_save and then again after_save and then perform comparisons on the two arrays to identify the additions and deletions. What I can't get my head around is the best way to persist the array of products_territories from the before save so it's then available to my after_save callback. I'm pretty certain ## class variables aren't the way to go and i'm also not so sure about session variables. I'm wondering whether something like Redis or Memchached is what I should be looking at?
Can anyone that's had to do something similar to this give me any pointers or direct me to some further reading please?
Thanks in advance.

Rails checkboxes for a has_many :through association

A person can compete in various events, but they must enter a partner's name for that event. This association is stored in an entry, which contains a field for the partner's name.
class Person < ActiveRecord::Base
has_many :entries
has_many :events, :through => :entries
validates_presence_of :name
end
class Event < ActiveRecord::Base
end
class Entry < ActiveRecord::Base
belongs_to :person
belongs_to :event
validates_presence_of :partner_name
end
The question is: How do you create a single page form that allows a person to enter themselves in multiple events and input their partners' names? I've tried to implement an all_entries method in the person model that will return an array of entry objects for all the available events, and an all_entries_attributes method that will update, create, and delete entry objects, but I can't seem to find a good, clean way to do this. I know this is a rather open ended question, but this must be a pattern that someone else in the rails community has encountered before, so I'm hoping there is a good solution to it.
If you are still looking for the answer you might want to check out my question and answer that I found.
So you want a page in which you can create events for a user, and add people to those events
I won't give you a plain solution because there's some work to an implementation for this, but I will recommend checking out these railscasts about nested model forms
part1 and part2

Resources