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.
Related
Assuming i have 3 models associated to each other:
class Farm < ApplicationRecord
has_many :horses
has_many :events
end
class Horse < ApplicationRecord
belongs_to :farm
has_many :events_horses, class_name: 'Event::EventsHorse'
has_many :events, through: :events_horses, source: :event, dependent: :destroy
end
class Event
belongs_to :farm
has_many :events_horses, class_name: 'Event::EventsHorse'
has_many :horses, through: :events_horses, source: :horse, dependent: :destroy
end
class Event::EventsHorse < ApplicationRecord
self.table_name = "events_horses"
belongs_to :horse
belongs_to :event
audited associated_with: :event, except: [:id, :event_id]
end
How to guarantee that each of the Horse belongs to same Farm as event? Possible solution is using custom validation, but i was wondering if there is some other way. I have few other models like Horse, so it force me to do custom validation method to each of them.
class Event
...
validate :horses_belongs_to_farm
private
def horses_belongs_to_farm
horses.all? {|h| h.farm_id == farm_id}
end
end
I think the model you are using is setting up too many id's between the tables that require consistency checking.
If you set the model up this way, then you don't need to validate that a horse's farm and event are consistent since the data ensures it:
class Farm < ApplicationRecord
has_many :horses
has_many :events
end
class Horse < ApplicationRecord
belongs_to :farm
has_many :events, through: :farm
end
class Event < ApplicationRecord
belongs_to :farm
has_many :horses, through: :farm
end
If you need efficient access to horses from events or events from horses, you can use joins. This gives some simplicity, clarity, and consistency.
You should also have a look at Choosing Between has_many_through and has_and_belongs_to_many.
[Edit based upon updated question and comments] Now that your model and question are a little more clear, my hunch is that putting the validation in the Event model causes redundant validations. Since your intent is to make sure that, in a given event, the horse and farm are consistent, I would put the validation in EventsHorses:
class Event::EventsHorse < ApplicationRecord
...
validate :horse_belongs_to_farm
private
def horse_belongs_to_farm
horse.farm_id == event.farm_id
end
end
As an aside, thy do you have Event::EventsHorse rather than simply have a separate model for EventsHorse?
I'm stuck in a circle of indecision and need a shove to break free. I'm a noob with Rails and I'm attempting to translate a pre-existing database using Rails conventions. Basically, I currently have 5 models/tables: Workorders, Mileage_logs, Time_logs, Parts, & Equipment. A Workorder can have many Mileage_logs, Time_logs, and Parts, because each of them is shown on the Workorder's index page. But, that's the seemingly easy part...
I'm getting confused when the Equipment model is introduced because it seems like it's basically the same thing as the Workorder.
What is the best way to handle this relationship setup? Is this an appropriate use for the has_many :through convention? Or, is this best done with simply having the workorder_id and equipment_id in the Mileage_log, Time_log, and Part models and then:
class Part < ActiveRecord::Base
belongs_to :workorder
belongs_to :equipment
end
class Mileage_log < ActiveRecord::Base
belongs_to :workorder
belongs_to :equipment
end
class Time_log < ActiveRecord::Base
belongs_to :workorder
belongs_to :equipment
end
class Workorder < ActiveRecord::Base
has_many :Time_logs
has_many :Parts
has_many :Mileage_logs
end
class Equipment < ActiveRecord::Base
has_many :Time_logs
has_many :Parts
has_many :Mileage_logs
end
Or, is the has_many through: relationship what I should look into for the Workorder & Equipment models?
class Workorder < ActiveRecord::Base
has_many :parts
has_many :mileage_logs
has_many :time_logs
end
class Equipment < ActiveRecord::Base
has_many :parts, through: :workorder
has_many :mileage_logs, through: :workorder
has_many :time_logs, through: :workorder
has_many :workorders
end
Any help would be greatly appreciated!
Also, any general advice on the route setup would be welcomed as well.
I have three models:
data_set.rb
class DataSet < ActiveRecord::Base
has_many :browse_options
accepts_nested_attributes_for :browse_options, allow_destroy: true
end
browse_option.rb
class BrowseOption < ActiveRecord::Base
belongs_to :data_set
has_many :browse_option_datas
accepts_nested_attributes_for :browse_option_datas, allow_destroy: true
end
browse_option_data.rb
class BrowseOptionData < ActiveRecord::Base
belongs_to :browse_options
has_one :tradesman
end
I want to be able to display all the tradesman associated with a data set in the data set view with no duplicates. Is there a way I can use the joins method to do this in the controller? Thanks!
You can actually achieve this by setting up has_many through relationships between your models. There are great docs on this topic.
class DataSet
has_many :browse_options
has_many :browse_option_datas, :through => :browse_options
has_many :tradesmen, :through => :browse_option_datas
end
class BrowseOption
belongs_to :data_set
has_many :browse_option_datas
end
class BrowseOptionData
belongs_to :browse_options
belongs_to :tradesman
end
class Tradesman
has_many :browse_options_data
end
Edit: After some discussion in chat we also realised the relationships between Tradesman & BrowseOptionData needed some fixing.
Now, in your controller, you can call:
#data_set = DataSet.first
#tradesmen = #data_set.tradesmen # .uniq if you don't want any duplicates
I'm a bit new to Rails Active Record associations. I've tried to set up a relationship, but I get ActiveRecord error when I try to retrieve data. Did I associate my models incorrectly?
User has many Uploads, which has many UserGraphs:
class User < ActiveRecord::Base
has_many :uploads, through: :user_graphs
end
class Upload < ActiveRecord::Base
has_many :users, through: :user_graphs
end
class UserGraph < ActiveRecord::Base
belongs_to :user
belongs_to :upload
end
I want to get all of a user's uploads, and all of a user's graphs. The 2nd line doesn't work in rails console and gives an error
#user = User.find(1)
#uploads = #user.uploads
The error:
ActiveRecord::HasManyThroughAssociationNotFoundError: Could not find the association :user_graphs in model User
Extra Credit:
If Users have Uploads that have UserGraphs... shouldn't it be has_many :uploads and has_many :user_graphs, through :uploads?
Add
has_many :user_graphs
to the User and Upload classes.
The :through option defines a second association on top of this one.
You didn't tell Rails that you have a user_graphs association on User, only an uploads association. So when Rails goes to follow the user_graphs association on uploads, it can't find it.
So, you need add the user_graphs association. Your models should look like this:
class User < ActiveRecord::Base
has_many :user_graphs # <<< Add this!
has_many :uploads, through: :user_graphs
end
class Upload < ActiveRecord::Base
has_many :user_graphs # <<< Add this!
has_many :users, through: :user_graphs
end
class UserGraph < ActiveRecord::Base
belongs_to :user
belongs_to :upload
end
I have three models which look something like this:
Class User < ActiveRecord::Base
has_many :comments
end
Class Comment < ActiveRecord::Base
belongs_to :user
has_many :votes
end
Class Vote < ActiveRecord::Base
belongs_to :comment
end
Now I want to get all the votes associated with a user's comments like so:
#user.comments.votes
But this throws the error:
undefined method `votes' for #<ActiveRecord::Relation:0x3f6f8a0>
This seems like it should work, but I suspect ActiveRecord is coughing on the deeper has_many relationship. I've hacked together an SQL query that gets the desired results, but I suspect there's a cleaner way using purely ActiveRecord. Any tips?
You should use a has_many :through association
In your case it would be
Class User < ActiveRecord::Base
has_many :comments
has_many :votes, :through => :comments
end
Class Comment < ActiveRecord::Base
belongs_to :user
has_many :votes
end
Class Vote < ActiveRecord::Base
belongs_to :comment
end
And then simply get the votes with
#user.votes
Try this:
Vote.joins(comment: :user).where(users: {id: #user.id})