Can't update rails has_many :through relationship - ruby-on-rails

I try:
#item.associations.update_attributes(:tag_id=>params[:tag])
and
#item.associations.tag_id=params[:tag]
Both give me undefined method errors for update_attributes and tag_id=, respectively. Here's my setup:
class Item < ActiveRecord::Base
has_many :associations,:foreign_key=>"item_id",:dependent=>:destroy
has_many :reverse_associations,:foreign_key=>"tag_id",:class_name=>"Association"
has_many :tags,:through=>:associations
end
class Tag < ActiveRecord::Base
has_many :associations,:foreign_key=>"tag_id",:dependent=>:destroy
has_many :reverse_associations,:foreign_key=>"item_id",:class_name=>"Association"
has_many :items,:through=>:associations
attr_accessible :name
end
class Association < ActiveRecord::Base
belongs_to :item
belongs_to :tag
end
What am I doing wrong?

You're trying to update tag_id on the entire #item.associations collection instead of updating a single Assocation instance.
The proper way to solve this depends on what you're trying to accomplish. To update the tag_id for all associations in #item.association, try:
#item.associations.each do |association|
association.update_attributes(:tag_id => params[:tag])
end
If you want to update the tag id for a specific Association, then you somehow need to get that association first:
# Just picking the first association for the item as an example.
# You should make sure to retrieve the association that you actually
# want to update.
retagged_association = #item.associations.first
# Now, retag the association
retagged_association.update_attributes(:tag_id => params[:tag])

Related

ActiveRecord how to add existing record to association in has_many :through relationship in rails?

In my rails project I have three models:
class Recipe < ActiveRecord::Base
has_many :recipe_categorizations
has_many :category, :through => :recipe_categorizations
accepts_nested_attributes_for :recipe_categories, allow_destroy: :true
end
class Category < ActiveRecord::Base
has_many :recipe_categorizations
has_many :recipes, :through => :recipe_categorizations
end
class RecipeCategorization < ActiveRecord::Base
belongs_to :recipe
belongs_to :category
end
With this simple has_many :through setup, how can I take a given recipe like so:
#recipe = Recipe.first
and add a category to this recipe based off an existing category, and have it be updated on the respective category as well.
So:
#category = #Existing category here
#recipe.categories.build(#category)
and then
#category.recipes
will contain #recipe?
The reason why I ask this is because I'm trying to achieve this behavior through the gem rails_admin, and every time I create a new recipe object, the form to specify it's categories is the form to create a new category, rather than attach an existing one to this recipe.
So it would be helpful to understand how ActiveRecord associates existing records to newly created records in a many_to_many relationship.
Thanks.
The build method is close enough to the new method, used to create new records.
if you need to add a current category to #recipe.categories, you just need to:
#recipe.categories << #category
This will add a record in the RecipeCategorization table (automatically saving it).
now #category.recipes will include #recipe

Using rails counter_cache on a non-standard has_many :through

so the standard way to use a has_many :through association would be to use the Physician-Appointment-Patient model from the Active Record Association Guide, right? It's basically a more verbose HABTM, with two models having a has_many :through and the connector model having two belongs_to. Now, in my current project I have a model structure like this:
class Cart < ActiveRecord::Base
has_many :line_items, through: line_item_groups
has_many :line_item_groups
end
class LineItemGroup < ActiveRecord::Base
belongs_to :cart
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :line_item_group
has_one :cart, through: line_item_group
end
Works fine. But now I want a line_item_count on Cart and can't figure out where I should add the counter_cache attribute.
Any help is appreciated :-)
First add line_item_count field in carts table, then in LineItem model add
class LineItem < ActiveRecord::Base
before_create :increment_counter
before_destroy :decrement_counter
def increment_counter
Cart.increment_counter(:line_item_count, cart.id)
end
def decrement_counter
Cart.decrement_counter(:line_item_count, cart.id)
end
end
I didn't tried it, but I think it will solve your problem.

Rails 4 create new Tag

I have a few questions about how to add a Tag to a users account:
Here is the User Model in relation to Tags
has_many :tags, through: :taggings
Here is the Tag Model:
class Tag < ActiveRecord::Base
attr_accessor :unread_count, :user_feeds
has_many :taggings
has_many :feeds, through: :taggings
end
And the Tagging Model:
class Tagging < ActiveRecord::Base
belongs_to :tag
belongs_to :feed
belongs_to :user
end
I'm at a point in the script where I have the current user object #user and I need to simply create a tag named "Mailbox" if it does not exist. I have tried a few create methods, and got expected object errors.
If anyone can help explain how to work with these models I would appreciate it.
A legit way to do this is to
#user.tags.create(name: "Mailbox")
If you want to check if it exists first, the rails 4 way is to:
#user.tags.find_or_create_by(name: "Mailbox")
Comment if you have any more question.
You can simply use the << method which the has_many association automatically adds:
#user.tags << Tag.find_or_create_by(name:'Mailbox')
Or, the rails 3 way:
#user.tags << Tag.where(name:'Mailbox').first_or_create!(name:'Mailbox')
# Or, as the << method automatically saves new objects
#user.tags << Tag.where(name:'Mailbox').first_or_initialize(name:'Mailbox')

How do I handle using 1 model to belong_to 2 different models at different times?

I am creating a Shopping Cart.
So I will have a model called LineItem.
Each instance of a cart will have 1+ line items. As will a completed transaction - which becomes an 'order'.
So, in theory, a LineItem belongs_to Cart and also belongs_to Order.
But, what would the DB table look like? a cart_id and order_id in my LineItems table?
That would mean that for every valid cart record, there will be an order_id that is nil (or empty). Likewise, for every valid order, there will be an empty cart_id.
This seems like a non-Railsy way to do this.
What is the best way to do this? A polymorphic association? What would that look like if I should do that?
The line item model can have two belongs_to associations
class LineItem < ActiveRecord::Base
belongs_to :cart
belongs_to :order
end
But this can get messy if it needs to belong to several different classes. The solution is polymorphic association which allows a model to belong to more than one model on a SINGLE association, which in the example below is :line_itemable. It will add two attributes, line_itemable_id and line_itemable_type to the LineItem model.
class LineItem < ActiveRecord::Base
belongs_to :line_itemable, :polymorphic => true
end
class Order < ActiveRecord::Base
has_many :line_items, :as => :line_itemable
end
class Cart < ActiveRecord::Base
has_many :line_items, :as => :line_itemable
end
If cart and order share similar attributes such as cost, then another option is to eliminate the order class and simply add a :paid flag on the cart.
I would suggest polymorphic association.
class LineItem < ActiveRecord::Base
belongs_to :owner, :polymorphic => true
end
class Order < ActiveRecord::Base
has_many :line_items, :as => :owner
end
class Cart < ActiveRecord::Base
has_many :line_items, :as => :owner
end
you will need to add owner_type and owner_id to line_items table.
Refer this

rails model assignment with has_many :through

I just can't figure out how to create a relation with a join table. I've read all the posts about them, but the main error seems to be that in the join table to models should be singular, which I have. I just can seem to create the models correctly and assign them. I have projects with datasets, and projects can have multiple datasets, while a dataset can belong to multiple projects. A dataset can be active or not, which is why I need the has_many through instead of the has_many_and_belongs_to setup.
My model definitions are:
class Project < ActiveRecord::Base
attr_accessible :name, :user_id
belongs_to :user
has_many :activedatasets
has_many :datasets, :through => :activedatasets
end
class DataSet < ActiveRecord::Base
attr_accessible :name, :project_id, :filename, :tempfilename
has_many :activedatasets
has_many :projects, :through => :activedatasets
end
class ActiveDataSet < ActiveRecord::Base
attr_accessible :active, :data_set_id, :project_id
belongs_to :project
belongs_to :dataset
end
When I create a new dataset I've got the project_id in the params, so I'm trying to setup the relationship like below:
class DataSetsController < ApplicationController
def new
#dataset = DataSet.new
#dataset.activedatasets.project_id = params[:project_id]
end
end
The error I'm getting seems famous:
NameError in DataSetsController#new
uninitialized constant DataSet::Activedataset
Can anybody point me in the right direction please?
Thanks for you attention.
You need to use:
has_many :active_data_sets
has_many :data_sets, :through => :active_data_sets
And in the DataSet model:
has_many :active_data_sets
has_many :projects, :through => :active_data_sets
Basically, rails expects you to use underscores to separate words in association names, and converts them to CamelCase. So active_data_sets becomes ActiveDataSet. Rails then uses this to work out which model class the association is with.
You also need to change your controller to this:
class DataSetsController < ApplicationController
def new
#dataset = DataSet.new
#dataset.active_data_sets.build(:project_id => params[:project_id])
end
end
Otherwise you'll get an error because you tried to set the project_id of the active_data_sets collection rather than creating a new ActiveDataSet.

Resources