Rails Migrations - Alternative name for a foreign key? - ruby-on-rails

In my application I have 3 tables, users, lists, tasks.
Users and lists is a many-to-many relationship, tasks belong to lists, and users can complete tasks.
For my migration, when a user completes a list I'd like to store who it was completed by in the database but I'm unsure how to do this.
I could simply add t.integer :user_id to my tasks migration, though I'd like to refer to it as .completed_by. Something that references the :user_id in my User table but is named :completed_by?

As MrDanA mentioned, you can specify a different foreign_key, but I would recommend against it. If you can, your db structure will be more understandable at the low level if you stay with entity name conventions.
In the future, it'll be immediately obvious that user_id points at the user table, while a new programmer won't be able to tell that completed_by points at the user table without exploring code. If you do want to have a specific name, or need more than one user association, something like "completed_user_id" remains clear at both the app and db levels.
You can then add methods like:
has_one :user
has_one :completed_by, :class_name => "User", :foreign_key => "completed_user_id"
or
has_one :user
def completed_by
self.user.id
end
etc.

In your migration, add t.integer :completed_by
Then, in your model, instead of:
has_one :user
do:
has_one :user, :class_name => "User", :foreign_key => "completed_by"
or even customize the association name, like:
has_one :completer, :class_name => "User", :foreign_key => "completed_by"

Related

How can I code to retrieve records when using this complicated doubled association?

I have 2 models such as User and Tracking
Tracking models has these 6 columns below
id
user_id
target_user_id
accessed_at
created_at
updated_at
The objective for this model is to know who accessed to my users/show.html.erb page.
Each user wants to know who looked at my page.
How can I make associations in both User and Tracking model?
Is it something like this?
models/user.rb
has_many: trackings
models/tracking.rb
belongs_to: user
belongs_to: user, :foreign_key => "target_user_id", :class_name => "TargetUser"
After all,
current_user.tracking.target_user shows the user whom the current_user accessed?
Then how can I retrieve the tracking records who accessed to current_user's?
How can I code in this case?
in your project there is no class with name "TargetUser",
:class_name option is to specify the name of the model you want to set association
in models/tracking.rb change your code like this,
belongs_to: user
belongs_to: target_user, :foreign_key => "target_user_id", :class_name => "User"
then you can access user from tracking by tracking.target_user
First of all your accessed_at column is redundant, you can simply delegate it like this:
delegate :accessed_at, to: :created_at
models/tracking.rb
belongs_to: tracked_user, foreign_key: 'target_user_id', class_name: 'User'
Then traverse it:
current_user.trackings.each do |t|
puts t.tracked_user_id
end

Rails one-to-one relationship

I have the following:
class User < ActiveRecord::Base
has_one :car, :class_name => 'Car', :foreign_key => 'user_id'
class Car < ActiveRecord::Base
belongs_to :worker, :class_name => 'User', :foreign_key => 'user_id'
It is basically a one-to-one relationship between a user and a car.
What I want is for the User to be able to have one and only one car. That implies the fact that if he creates a car assigned to him, he won't be able to create the second.
How could this be done?
There are certainly a couple different ways of accomplishing this. I would suggest creating a composite key index on that table to ensure that the user_id is unique in the table. This will ensure that it will only be present once. In a migration, you could write something like this.
add_index(:cars, :worker_id, :unique => true)
The first argument is the table name (don't forget this is generally the pluralized version of the class name). The field name comes second. The unique true is what will prevent you from inserting an extra row.
Note: This is a database level constraint. If you hit this because validations didn't catch it, it will throw an error.
In addition to this solution, you will want to add a validation to the Car model itself.
validates_uniqueness_of :worker_id, message: "can not have more than one car"
You'll see this error come through with something like "Worker ID can not have more than one car". You will most likely want to customize the "Worker ID" section of this. Refer to this post for instructions on how to do that.
You certainly don't have to do the db constraint, but in case anyone else inserts into the DB, it's a good idea. Otherwise, you'll have "invalid" data as far as Rails is concerned.
Change the definition of the relationship slightly:
class User < ActiveRecord::Base
has_one :car
class Car < ActiveRecord::Base
belongs_to :worker, :class_name => 'User', :foreign_key => 'user_id'
And you'll establish what you're looking for. See: http://guides.rubyonrails.org/association_basics.html#the-has-one-association
why don't you just test before the user tries to add a car?
if worker.car
raise "sorry buddy, no car for you"
else
car = Car.create(user_id: worker.id)
end

Help understanding polymophic associations (rails)

I know there are plenty of resources on this but I'm having a tough time relating any of them to my situation so I was hoping someone could help me clarify how this works:
Basically I have a model Action, (which gets created anytime a user does something that affects another user, like commenting on their article or voting on someones photo, for example), these actions will be listed in the users dashboard page as all the actions that have taken place that relate to them, like a stream... sort of like Github's "News Feed"
I've decided to go with creating a polymorphic association, here is what my model looks like:
class Action < ActiveRecord::Base
belongs_to :instigator, :polymorphic => true
belongs_to :victim, :polymorphic => true
end
I used instigator and victim because anyone can create these actions, which in turn always affect another user, here is my User model
class User < ActiveRecord::Base
has_many :actions, :as => :instigator
has_many :actions, :as => :victim
end
And this is where I think I'm going wrong, because ultimately I want to have a query which when I run something like User.find(1).actions to actually return all the instances in which the user is both an instigator or a victim, I think I can't have both of those have_many's in there, because when used like this I only get the instances where the user is the victim.
Here is my migration:
create_table :actions do |t|
t.references :instigator, :polymorphic => true
t.references :victim, :polymorphic => true
t.string :action_performed
t.references :resource, :polymorphic => true
t.timestamps
end
Thanks for any help, I always love the great suggestions and help the SO community gives.
This reminds of classic Friendship model problem. Polymorphic association is besides the point.
Rails version agnostic solution:
class User < ActiveRecord::Base
has_many :instigator_actions, :class_name => "Action", :as => :instigator
has_many :victim_actions, :class_name => "Action", :as => :victim
has_many :actions, :finder_sql => '
SELECT a.*
FROM actions a
WHERE (a.instigator_type = "User" AND instigator_id = #{id}) OR
(a.victim_type = "User" AND victim_id = #{id})'
end
While creating the Actions create them using one of the first two associations.
u1.instigator_actions.create(:victim => u2)
OR
u1.victim_actions.create(:instigator => u2)
At the same time you can get a list of actions associated with an user using the actions association.
u1.actions
Firstly I suggest you use roles through Single table Inheritance. In your user table , you can have a type column which identifies someone as an instigator or as a victim. (Of course if someone is both , he will have 2 rows , so you will have to make sure you dont have the name as the primary key.)
So now you have a more structured layout. Coming to the polymorphism problem,try using a different interface. As in,
class Action < ActiveRecord::Base
belongs_to :actionable, :polymorphic => true
end
actionable need not be a separate class. Its just a name given to the interface.Like wise on the other side of the association.
The Rails Way by Obie Fernandez gives you a clear picture on this, so you can refer it for more dope on polymorphic associations.

Ruby on Rails: Is it possible to :include the other leg of a circular join table?

I'm working on an application that models friendships between users.
class User
has_many :friendships
has_many :friends,
:through => :friendships,
:conditions => "status = #{Friendship::FULL}"
end
class Friendship
belongs_to :user
belongs_to :friend, :class_name => "User", :foreign_key => "friend_id"
end
When two users become friends, two instances of the friendship class are created, one for each direction of the friendship. This is necessary because different permissions can be set in each direction, and also for ease of lookups so we always search by the user_id without creating a second index and needing to double up searches.
Is it possible, using either find or a named scope to pull up a friend along with the counter-friendship? I'd like to do this because I'm allowing users to edit friendship permissions which are stored in their own leg of the friendship, and thus when I'm displaying a friend's info to the user I need to check the opposite leg to see how permissions are set. (As an aside, does this seem like a logical way to do things? Either you store into the opposite side, or you have to consult the opposite side before display, both kind of unpleasant.)
I've managed to whip up a batch of SQL that does the job:
def self.secure_friends(user_id)
User.find_by_sql("SELECT u.*, cf.permissions
FROM users u
INNER JOIN friendships f ON u.id = f.friend_id
INNER JOIN friendships cf ON u.id = cf.user_id AND cf.friend_id = #{user_id}
WHERE ((f.user_id = #{user_id}) AND (f.status = #{Friendship::FULL}))")
end
The function returns all of a user's friends names and ids, along with the permissions. However, this really doesn't seem ideal, and means I have to access the permissions by calling friend.permissions, where permissions isn't actually a member of friend but is just merged in as an attribute by find_by_sql. All in all, I'd really rather be doing this with a named_scope or a find call, but unfortunately, every attempt I've made to use that approach has resulted in errors as Rails chokes on having the same Friendship join table getting joined to the query twice.
I'm not sure how much you want to do, and how much you'd allow a plugin to do -- but I've used this has_many_friends plugin with great success: has_many_friends plugin at Github
You can look at the source and be inspired... Just need a Friendship model that has things like:
belongs_to :friendshipped_by_me, :foreign_key => "user_id", :class_name => "User"
belongs_to :friendshipped_for_me, :foreign_key => "friend_id", :class_name => "User"
and then later, in your user class, have:
has_many :friends_by_me,
:through => :friendships_by_me,
:source => :friendshipped_for_me
Or, just
/app/models/user
has_many_friends
Later:
#current_user.friends.each{|friend| friend.annoy!}

Set-up Many-to-Many in Rails with destroy allowed for only one of the many

I am trying to model a very simple system, but the Rails way of doing this is escaping me. I'd like to have two entities, a User and a Bet. A bet has two users and a user has many bets. I'd like the creating User of the bet to be able to mark it as resolved (do any form of update/delete on it).
My initial thinking was to have a User act two ways, as a better or a responder. many_to_many associations in rails may be the right option, with an extra column on the bet table to note who owns the model. But, then you are repeating one of the User's id's twice.
Thanks in advance!
I think this is what you want:
class User
has_many( :bets, :foreign_key => 'bettor_id', :class_name => 'Bet' )
has_many( :responses, :foreign_key => 'responder_id', :class_name => 'Bet' )
end
class Bet
belongs_to( :bettor, :class_name => 'User' )
belongs_to( :responder, :class_name => 'User' )
end

Resources