Ordering by complex structure in Rails - ruby

I have a model called Event that has many EventRegistrations, which belongs to a person.
Also the EventRegistration has many EventAtendees, which belongs to a person too.
I want to order in Rails all the people related to a event, which means:
- Person associated to an event registration
- Person associated to an event atendee which is associated to a registration..
Any help?

As I understand it, you have the following models and associations:
Event
has_many :event_registrations
EventAttendee
belongs_to :event_registration
belongs_to :person
Person
has_many :event_registrations
has_many :event_attendees
EventRegistration
belongs_to :person
belongs_to :event
Now, as to your actual question. You say you want to 'order all the people related to an event'
I don't actually see how 'ordering' (a.k.a sorting) enters into this.
To get all users associated with an event, I recommend adding some :through associations:
Event
has_many :event_attendees, :through => :event_registrations
has_many :people, :through => :event_attendees
Then, when you have an event object, you can simply call .people on it, and it'll just work.

Here is how you would order by the person's last name, assuming you have all your ActiveRecord associations set up correctly.
event_registrations = EventRegistrations.find(:all)
ordered_event_registrations = event_registrations.sort{|registration_a,registration_b|registration_a.people.lastname<=>registration_b.people.lastname}

Related

Papertrail tracks create but not delete through association

I have a table of Industries and am keeping tracking of it's competitors which are also industries. This is through a mapping table industry_competitors which has industry_id and competitor_id. I want papertrail to track associations and dissociations of industry competitors.
class Industry < ApplicationRecord
has_many :industry_competitors, dependent: :destroy
has_many :competitors, through: :industry_competitors
end
class IndustryCompetitor < ApplicationRecord
has_paper_trail
belongs_to :industry
belongs_to :competitor, class_name: "Industry"
end
My controller code is as such.
competitors = ::Industry.where(id: params[:competitor_ids])
#industry.competitors = competitors
#industry.save
Every time the entire competitor list is passed. If I try to disassociate a few competitor (by not passing the ids to the controller) from the industry a 'Delete' query is fired.
DELETE FROM `industry_competitors` WHERE `industry_competitors`.`industry_id` = 4559 AND `industry_competitors`.`competitor_id` = 4564
I suspect because activerecord calls 'delete' and not 'destroy' this papertrail callbacks are not triggered hence the changes are not tracked.
If there a way to call delete explicitly (with minimal code changes). Or is there a way for papertrail to track delete?
Adding this patch can get it to work.
module HasManyThroughAssociationPatch
def delete_records(records, method)
method ||= :destroy
super
end
end
ActiveRecord::Associations::HasManyThroughAssociation.prepend(HasManyThroughAssociationPatch)
Credits: https://github.com/westonganger/paper_trail-association_tracking

Rails association, I want something but the other model shouldn't care

So I think I need help with what I am doing with my asscoiations. I have two models (well many but for the purposes here). One is a Provider. Each provider can have a designation. Though the Designations is just a list it can appear in many different providers so it is not one to one. My models are as follows:
Provider.rb
class Provider < ActiveRecord::Base
has_and_belongs_to_many :groups
has_one :designation
has_one :specialty
end
Designation.rb
class Designation < ActiveRecord::Base
end
The error I get is this:
SQLite3::SQLException: no such column: designations.provider_id: SELECT "designations".* FROM "designations" WHERE "designations"."provider_id" = ? LIMIT 1
Which tells me my associations are off cause the designations should not have the provider_id, if anything the provider should have the designation_id.
Do I ditch the has_one and just put has_many in the Designation?
If I do that, I am new to rails, do I have to create a migration so the database is correctly updated?
Try using
models/provider.rb
belongs_to :designation
Then in
models/designation.rb
has_many :providers
It may feel a little strange but the belongs_to just lets you know which model the id column needs to go in. In this case the provider model, so you'll need:
rails g migration add_designation_id_to_provider designation_id:integer

How to properly handle multiple model associations to different keys in Rails 4.0.8

I'm having the following problem while trying to model my application, it's basically a functionality to handle bills with multiple people, like sharing the rent or any other similar stuff, the deal is:
I have a user model and a billing model but can't find out how to build the associations.
A user has_many billings and billings belongs_to user but, also the billing has_many users, e.g. Tom registers a bill that is meant to be shared by Tom himself, Betty and Bob.
So that makes Tom the creditor and the other two become debtors in the billing.
I'm kinda lost at this point, how to consolidate these associations, dunno if any more information is needed or if it's clear enough, but will appreciate any help and update with any other information needed.
Thanks in advance,
---EDIT---
I've come to the following approach:
Class User < ActiveRecord::Base
has_many :billings, foreign_key: "creditor_id", dependent: :destroy
end
Class Billing < ActiveRecord::Base
belongs_to :creditor, class_name: "User"
has_many :debts, dependent: :destroy
end
Class Debt < ActiveRecord::Base
belongs_to :billing
has_one :user
end
I also tried to graphically model it for better understanding here: imgur
Would that all be correct?
Don't be afraid to use the options available to you in ActiveRecord. If your model naming is getting cluttered (Billing belongs to a User and has_many Users) then be more specific with the association labels and sort the links with options. The Rails Associations Guide is easy to Google and covers pretty much everything.
class User < ActiveRecord::Base
has_many :billings, :foreign_key => 'creditor_id'
has_many :debts, :through => :debtor_users, :source => :billing
end
class Billing < ActiveRecord::Base
belongs_to :creditor, :class_name => 'User'
has_many :debtors, :through => :debtor_users, :source => :user
end
class DebtorUser < ActiveRecord::Base
belongs_to :billing
belongs_to :user
end
You can extend the Billing model to have multiple creditors also simply by converting the belongs_to association into a has_many :through association following the same pattern as debtors just with a different join model (say CreditorUser).

ActiveRecord: Does destroy_all execute callbacks for associated records?

I have four models:
class Order < WebDatabase
has_many :shipments
class Shipment < WebDatabase
belongs_to :order
has_many :line_items, :order => "id", :dependent => :destroy
class LineItem < WebDatabase
belongs_to :shipment
has_many :line_item_messages, :dependent => :destroy
class LineItemMessage < WebDatabase
belongs_to :line_item
So if I did #order.shipments.destroy_all, would there be a chain of destroys where #order.shipments.line_items are destroyed and #order.shipments.line_items.line_item_messages are destroyed too because of the :dependent => :destroy on each of the models?
Yes.
from the docs (emphasis is my own)
destroy_all(conditions = nil)
public
Destroys the records matching conditions by instantiating each record and calling its destroy method. Each object’s callbacks are executed (including :dependent association options and before_destroy/after_destroy Observer methods). Returns the collection of objects that were destroyed; each will be frozen, to reflect that no changes should be made (since they can’t be persisted).
Note: Instantiation, callback execution, and deletion of each record can be time consuming when you’re removing many records at once. It generates at least one SQL DELETE query per record (or possibly more, to enforce your callbacks). If you want to delete many rows quickly, without concern for their associations or callbacks, use delete_all instead.

Ruby on Rails: Associations when a user likes a song

I'm trying to figure out the best way to setup my database and models for the following scenario.
A user can like an infinite number of songs.
A song can be liked once by an infinite number of users.
I have these tables:
songs, users, likes etc... Following RoR conventions.
The table named likes has these foreign keys: user_id, song_id. And also a field named 'time' to save a timestamp when the song was liked.
I'm not sure of how to do this, I would like to be able to use code like this in my controllers:
User.find(1).likes.all
This should not return from the likes table, but join the songs table and output all the songs that the user likes.
What are the best practises to achieve this in Ruby on Rails following their conventions?
Unless you need to act specifically on the likes table data, the model itself is probably not necessary. The relationship is easy:
class User < ActiveRecord::Base
has_and_belongs_to_many :songs
end
class Song < ActiveRecord::Base
has_and_belongs_to_many :users
end
This will join through the currently non-existent song_users table. But since you want it to join through likes you can change each one to this:
has_and_belongs_to_many :songs, :join_table => 'likes'
If you want to be able to call User.find(1).likes and get songs, then change the user's version to this:
class User < ActiveRecord::Base
has_and_belongs_to_many :likes, :join_table => 'likes', :class_name => 'Song'
end
And you could change the songs version to something like this:
class Song < ActiveRecord::Base
has_and_belongs_to_many :liked_by, :join_table => 'likes', :class_name => 'User'
end
This will give you Song.find(1).liked_by.all and give you the users (You could keep it to users if you wanted using the first version)
More details on habtm relationships can be found here: http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_and_belongs_to_many
Edit to add
If you want to act on the join table for whatever reason (you find yourself needing methods specifically on the join), you can include the model by doing it this way:
class User < ActiveRecord::Base
has_many :songs, :through => :likes
has_many :likes
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :song
end
class Song < ActiveRecord::Base
has_many :users, :through => :likes
has_many :likes
end
This will let you do User.find(1).songs.all, but User.find(1).likes.all will give you the join data

Resources