belongs_to and has_many to the same model - ruby-on-rails

I am wondering whether there is a way to do this with rails or not. Basically I have a user model and an event model. Event is created by a user and I want to have a foreign key (user_id) in the event model that indicates who created the event. Additionally, event can have many users who attend it so the event model becomes something like
belongs_to :user
has_many :users, :through => :guests #suppose i have the guest model
and the user model looks something like
has_many :events, :through => :guests
I have not tried this association yet but I want to be able to say
e = Event.find(1)
e.creator #returns the user who created this event
instead of
e.user
is there a way for me to do this?

Simply pass some options to belongs_to:
belongs_to :creator, :class_name => "User", :foreign_key => "user_id"
This specifies that the creator method will be a User object, referencing the user_id field.

Related

Database Association, Can a class has_many and belong_to the same object?

So I'm trying to create a simple events web application where class User can both create class Event and participate in them. In the case class User creates a class Event, I want that event to belong_to the user. But in case that same User signs up to participate in another event, I want the user to belong_to the event, and that event to has_many Users. I don't know how this would actually be written in the models though.
In one case class User is a participant belonging to the event, and in the other they are the host and the event belongs_to them. I wan't to keep this dry and not create separate classes of users(host and participant in this case) if I don't have too. A mere joint table doesn't seem to account for this conditional association. Any advice on the best way to do this?
class User
???
class Event
????
Thanks!
You'll need to make 2 associations - one to link to created Events, and one to link to subscribed Events:
create_table :event_subscriptions do |t|
t.references :subscribed_user
t.references :subscribed_event
end
create_table :events do |t|
t.references :user
# other fields
end
class User < ActiveRecord::Base
has_many :events # created Events
has_many :event_subscriptions, :foreign_key => :subscribed_user_id
has_many :subscribed_events, :through => :event_subscriptions # subscribed Events
end
class EventSubscription < ActiveRecord::Base
belongs_to :subscribed_user, :class_name => 'User'
belongs_to :subscribed_event, :class_name => 'Event'
end
class Event < ActiveRecord::Base
belongs_to :user # the creating User
has_many :event_subscriptions, :foreign_key => :subscribed_event_id
has_many :subscribed_users, :through => :event_subscriptions # subscribed Users
end
I would recommend you use the has_and_belongs_to_many association (documentation here).
Based on what you're trying to accomplish I don't think a joint table is avoidable. Granted, that shouldn't discourage you!
I would add a "role" column of type string to the Users table (i.e. Host or Participant) and use the has_and_belongs_to_many association. Note, you will need to create a joint table users_events that contains :user_id and :event_id columns.
Then, you can have a User that has the role of, let's say "host", create an Event and invite other users of roles "participant".
Of course, this is just my suggestion. There are several ways you can go about this.
Be aware that if an event has_many :users then the user table must have a event_id column, and a user can only belong to one event at a time. I suspect you want a many to many relationship between users and events. If that is the case you would use has_and_belongs_to_many :users association documented here
That said, yes, you can have multiple relations on the same classes.
class Event < ActiveRecord::Base
has_many :users
belongs_to :host, :class_name => 'User'
end
The events table would have a column host_id, but event.host would still return a User.
I would handle that with a has_many and belong_to between User and Event. User has_many Events and Event belongs_to User.
The rest would be your logic. Do you want your has_many relationship to be your event owners, or your participants. I'll assume the has_many relates to Event owners.
From there create a column called participants in your Event table. In the Event or User model you can then create a before_save method that inserts the user_id of your participant.
You could then create a scope in either model that returns a list of all participants of the Event.

Rails 3 Multiple Foreign Keys

Im writing an app that has a user model, and the users in this model can be two different user types.
user.rb
class User < ActiveRecord::Base
has_many :transactions
has_many :transaction_details, :through => :transactions
has_many :renters, :class_name => "Transactions"
end
transaction.rb
class Transaction < ActiveRecord::Base
has_many :transaction_details
belongs_to :user
belongs_to :renter, :class_name => "User"
end
transaction_detail.rb
class TransactionDetail < ActiveRecord::Base
belongs_to :transaction
belongs_to :inventory
scope :all_open, where("transaction_details.checked_in_at is null")
scope :all_closed, where("transaction_details.checked_in_at is not null")
end
Basically a user could be a renter, or the person checking the item out. Once I have a transaction, I can call:
#transaction.renter.first_name # receive the first name of the renter from the renter table
#transaction.user.first_name # receive the first name of user who performed the transaction
This is perfect and works as I explected. For the life of me, I can not figure out how to get the scope to work when called through a user:
# trying to perform the scrope "all_open", limted to the current user record, but I cant get it to use the
# renter_id column instead of the user_id column
u.transaction_details.all_open
is this possible to have a scrope look up by the second forien_key instead of user_id?
Short answer - Yes. This is very possible.
You need to mention the foriegn key being used in the reverse association definition.
In users.rb:
has_many :rents, :class_name => "Transactions", :foreign_key => "renter_id"
This will allow you to write:
User.find(5).rents # array of transactions where user was renter
If you want to call the transaction_details directly, then once again you would need to specify another association in user.rb:
has_many :rent_transaction_details, :through => :rents, :source => "TranactionDetail"
Which would allow you to call:
User.find(5).rent_transaction_details.all_open

How to access referenced table in a n:m relation in Ruby on Rails?

I have got a Database like this:
users
-id
user_cars
-user_id
-car_id
cars
-id
-model
-color
So a user can have multiple cars, and detailed information about the cars are in a big cars table. I also created the models with the relationships.
class User
has_many :user_cars
class User_car
belongs_to :user
belongs_to :cars
class Car
has_many :user_cars
Now I want to access the information for all the cars for one user. My first approach would be to get at least one information (i.e. color) from the cars table.
I tried this one, just as an example for accessing the middle table:
#user_id = current_user.user_cars.find(1).user_id
works! But when I try to access the cars table I always get an error.
#color = current_user.user_cars.cars.find(1).color
undefined method `cars' for #<ActiveRecord::Relation:0xaf92e8c>
So I think Im doin something easy very wrong...
When I know how to get access to the third table, I have to do it in that fashion, that I only get results for the user and not just only the first entry, maybe you guys can help me with that aswell. Thanks!
The issue in your example by the way is that belongs_to should be singular. Also, your order was wrong.
#color = current_user.user_cars.find_by_car_id(1).car.color
You should rewrite this to use a has_many through association:
class User
has_many :user_cars
has_many :cars, :through => :user_cars
class UserCar
belongs_to :user
belongs_to :car
You can then access the cars by doing:
current_user.cars
And the color by doing:
#color = current_user.cars.find_by_car_id(1).color
EDIT
After some debugging, it turns out that the Car model has a class property. Class is a reserved word in ruby. Be careful with naming your attributes!
Without has_many :through associations:
#color = current_user.user_cars.where(:car_id => 1).first.color
With them:
class User < ActiveRecord::Base
has_many :user_cars, :foreign_key => :user_id, :class_name => "UserCar", :inverse_of => :user
has_many :cars, :through => :user_cars
end
class UserCar < ActiveRecord::Base
belongs_to :user
belongs_to :car
end
class Car < ActiveRecord::Base
has_many :user_cars, :foreign_key => :car_id, :class_name => "UserCar", :inverse_of => :car
has_many :cars, :through => :user_cars
end
#color = current_user.cars.find(1).color
http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association
:through defines a shortcut
:inverse_of defines a method (association) which represents current model in :class_name model
:class_name defines which model should be represented by :user_cars
:foreign_key tells which column in the target model's table represents current model (in table user_cars (or users_cars, depends on how you define the association, i think it should be user_cars in this example.. and users_cars for has_and_belongs_to_many)
Details on those are in the link above.

Write a migration with reference to a model twice

I have a message model (Message) and this models as a userTo and userFrom, so two references to User. How can i write the migration? My user model is User.
Thank you
Here's a complete answer to this issue, in case people visiting this question are having a hard time putting everything together (as I was when I first looked into this).
Some parts of the answer take place in your Migrations and some in your Models:
Migrations
class CreateMessages < ActiveRecord::Migration
create_table :messages do |t|
def up
t.references :sender
t.references :recipient
end
end
end
Here you are specifying that there are two columns in this table that will be referred to as :sender and :recipient and which hold references to another table. Rails will actually create columns called 'sender_id' and 'recipient_id' for you. In our case they will each reference rows in the Users table, but we specify that in the models, not in the migrations.
Models
class Message < ActiveRecord::Base
belongs_to :sender, :class_name => 'User'
belongs_to :recipient, :class_name => 'User'
end
Here you are creating a property on the Message model named :sender, then specifying that this property will be referencing an instance of the User class. Rails, seeing the "belongs_to", will look for a column in your database called "sender_id", which we defined above, and use that to store the foreign key. Then you're doing the exact same thing for the recipient.
This will allow you to access your Sender and Recipient, both instances of the User model, through an instance of the Message model, like this:
#message.sender.name
#message.recipient.email
Here is your User Model:
class User < ActiveRecord::Base
has_many :sent_messages, :class_name => 'Message', :foreign_key => 'sender_id'
has_many :received_messages, :class_name => 'Message', :foreign_key => 'recipient_id'
end
Here you are creating a property on the User Model named :sent_messages, specifying that this property is related to the Message Model, and that the foreign key on the Message model which relates to this property is called 'sender_id'. Then you are doing the same thing for received messages.
This allows you to get all of a users sent or received messages by doing something like this:
#user.sent_messages
#user.received_messages
Doing either of these will return an array of instances of the Message model.
In the migration, create two different columns for each kind of user. For example:
add_column :messages, :sender_id, :integer
add_column :messages, :receiver_id, :integer
Then in the model, that's where the logic to map each column to the User class happens:
belongs_to :sender, :class_name => 'User'
belongs_to :receiver, :class_name => 'User'
Of course, use your own words for sender and receiver, but Rails will automatically associate sender to the sender_id column (and the same logic for receiver)
You will then be able to interact with both user user.sender and user.receiver.

How can I relate to an object twice in rails?

I've got a Users model and a Tasks model.
A task has a creator, of type user, and an assignee of type user.
I've already done a migration of AddUserIdtoTasks to get the creator relation working, but now I need to do the same thing again to add the assignee, but I'm already using the keyword 'user'. How should I go about building a proper relation.
A task only has one assignee, always.
I'm using devise for the user model.
has_one :creator, :class_name => "User"
has_one :asignee, :class_name => "User"
Or belongs_to, depending on how your fields are set up. has_one and belongs_to both take an optional :class_name argument for cases just such as yours.
Create the field assignee_id in your Task model and then use it for the relation as in
class Task < AR::Base
belongs_to :assignee, :class_name => 'User'
end
On the other side of the relation
class User < AR::Base
has_many: :assigned_tasks, :class_name => 'Task', :foreign_key => :assignee_id
end
Sorry, should be :class_name. Updated User class also with a :foreign_key parameter, without it user.assigned_tasks would have joined records using the :user_id parameter (the default value for has_many, i.e. "#{class_name}_id"`).
I invite you to read the link I've posted, it explains better than me all these things.
Source: http://guides.rubyonrails.org/association_basics.html#detailed-association-reference

Resources