I have two questions about ActiveRecords:
1)
Let's say I have an application with several users, each one of which has a profile photo.
I would like users to be able to have the same profile photo as their friends: this leads to a 1-many relationship where one photo can be the profile photo of many users.
When I have to create the association in rails I have to write something like this:
class User < ActiveRecord::Base
belongs_to :photo
end
class Photo < ActiveRecord::Base
has_many :Users
end
Now this seems a bit weird to me since the photo belongs to multiple users in this case, rather than multiple users belonging to one photo.
I am doing this wrong or am I overthinking about the terminology of rails?
2)
New Case:
Let's say a user can have one profile photo. In this case the photo cannot be shared between users, as a consequence the relationship is of 1-to-1.
I also want to add a post entity which can contain a photo and this photo can be used by a user as a profile photo.
This is how the final implementation would look like:
class User < ActiveRecord::Base
has_one :photo
end
class Photo < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
class Post < ActiveRecord::Base
has_one: Photo
end
By looking at the Rails Documentation it seems that the only way to create the one to one relationship as such is by putting the two foreign keys in the Photo table: one for Post and one for User.
However I would like to instead have one foreign key in the User table for Photo and one foreign key in the post table for Photo as well. In this case it seems that the implementation needs to be as such:
class User < ActiveRecord::Base
belongs_to :photo
end
class Photo < ActiveRecord::Base
has_one :user
has_one :post
end
class Post < ActiveRecord::Base
belongs_to: Photo
end
This also does not make much sense to me, semantically speaking. Again, am I going something wrong or am I overthinking this?
1)
#app/models/user.rb
class User < ActiveRecord::Base
has_one :photo
belongs_to :avatar, class_name: "Photo" #-> set this as the "avatar"
end
#app/models/photo.rb
class Photo < ActiveRecord::Base
belongs_to :user
end
The user can create a photo; he can define which avatar he uses:
#user = User.find x
#photo = #user.avatar
Whilst I still think it looks a little weird, I think it would work.
2)
polymorphic
#app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
has_one :photo, as: :imageable
end
#app/models/user.rb
class User < ActiveRecord::Base
has_many :posts
has_one :photo, as: :imageable
belongs_to :avatar, class_name: "Photo"
end
#app/models/photo.rb
class Photo < ActiveRecord::Base
belongs_to :imageable, polymorphic: true #-> belongs to either post or user
end
In both cases, I would advocate using a foreign_key to define the avatar_id for the user.
This will mean a belongs_to association (not ideal), but will mean that you can upload as many images as you want, referencing one to be the avatar. You'll then be able to call the avatar method as required in your profile.
The best way to do this would be to use a join table / join model but that would be inefficient for what you're asking.
Related
I'm trying to determine the proper ActiveModel realtionship for the following situation: there are pictures and there are different categories of them: foo, bar, baz and qux. A User can set one like and several comments per the picture.
I started with a Catregory and a Foo models. The simpliest approach could be - creatation of likes and comments properties for each of the Foo, Bar, Baz and Qux models. But I feel it's a silly approach... There might be a better one.
What is the best kind of realationship can be chosen for such a case?
Here's what I imagined reading your question (I've decided that your pictures were created by users, because your model looks a lot like a social network):
class User < ActiveRecord::Base
has_many :pictures
has_many :comments
has_one :like
end
class Picture < ActiveRecord::Base
belongs_to :user
belongs_to :category
has_many :likes
has_many :comments
end
class Category < ActiveRecord::Base
has_many :pictures
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :picture
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :picture
end
Categories are rows of the categories Table. Instead of making one Model per category, you should have one model Category for all of them, with attributes like name, color or whatever you want.
Hi I have a comment object, and I use polymorphic association, so it can belongs to many other objects. But I also want them to belong to users.
Now I can, call comment.comment_owner and I get the object that was commented by this comment.
As for the user I have a user_id field in the comment object, I pass the user id through the form. But when I try to get owner user by comment.user I get an error. Right now I`m getting user by User.find(comment.user_id). But this looks bad.
Is there a way to pass the user id. So I can get User owning a comment by comment.user
My associations:
class Comment < ActiveRecord::Base
belongs_to :comment_owner, polymorphic: true
end
class User < ActiveRecord::Base
has_many :comments, as: :comment_owner
end
class Posts < ActiveRecord::Base
has_many :comments, as: :comment_owner
end
Why not just
class Comment < ApplicationRecord
belongs_to :user
end
First of all, in my opinion comment_owner is not a good name for what you're designing here. Comment owner would suggest an ownership relation (rather a person or someone). I'd rather call it commentable as these object are being commented on.
If this relation is meant to be a polymorphic then you should have commentable_type and commentable_id (or comment_owner_type and comment_owner_id if you really prefer the original) as polymorphic => true expects to have those two fields (named as: relation_name_type and relation_name_id).
If you then have a Comment object you get the user commented by calling comment.commentable (or comment.comment_owner in case you decide to keep your naming).
[EDIT]
As you said, you want to have two parents. If I get this right you just want to have two relations - this means that if you just modify your code to:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
belongs_to :author, class_name: 'User'
end
class User < ActiveRecord::Base
has_many :comments, as: :commentable
has_many :notes, class_name: 'Comment'
end
class Post < ActiveRecord::Base
has_many :comments, as: :commentable
end
You will have your polymorphic relation as well as the ownership.
I'm trying setup a Rails app that will be something like a game. The app has Users, each of which have Pawns that they can create. A User can search other users and the Pawns that they created, and challenge another one if they like, using one of their own Pawns. The challenged user can then accept/decline the challenge.
Right now I can add/delete Pawns for a User fine, and my models look like this:
class User < ActiveRecord::Base
has_many :pawns, dependent: :destroy
and
class Pawn < ActiveRecord::Base
belongs_to :user
Now, if User1 wants to challenge a Pawn created by User2, he looks at User2's list of Pawns and clicks a "Challenge" button for the Pawn he wants. User1 then has to select one of his Pawns to use for the challenge and clicks save. Now User2 needs to either accept/decline the challenge.
I'm having a hard time wrapping my head around how the challenges should be setup. My thought is that each Pawn will have a self-referential many-to-many relationship, almost like a friendship relationship would be setup. However, I don't know if I should consider the challenge something related to the User or the Pawn.
Whats the best way to model something like this?
EDIT:
Here's a diagram of what I'm trying to accomplish. I definitley think I need some sort of association setup. Result would hold statistics of that Pawn for that Challenge (something like time_spent, clicks_made, etc.). Challenge would also have a column for winner or something similar.
Your challenge may have association defined for each type of pawn.
class Challenge < ActiveRecord::Base
# attributes :challengee_id, :challenger_id
belongs_to :challengee, class_name: "Pawn"
belongs_to :challenger, class_name: "Pawn"
has_many :results
end
Pawns will have associations for each type of challenge.
class Pawn < ActiveRecord::Base
# attributes :user_id
belongs_to :user
has_many :results
has_many :initiated_challenges, class_name: "Challenge", foreign_key: :challenger_id
has_many :received_challenges, class_name: "Challenge", foreign_key: :challengee_id
end
It's probably ok to denormalize challenge_id for the result records.
class Result < ActiveRecord::Base
# attributes: pawn_id, challenge_id, :result
belongs_to :pawn
belongs_to :challenge
end
Your user can have associations to pawns and to challenges through pawns. A simple way to get both challenge types associated with a user would be to combine the results of the two challenge associations (initiated and received) into one method #challenge.
class User < ActiveRecord::Base
has_many :pawns
has_many :initiated_challenges, through: :pawns, source: :initiated_challenges
has_many :received_challenges, through: :pawns, source: :received_challenges
def challenges
initiated_challenges + received_challenges
end
end
Performance optimizations for this method could include denormalizing the user_ids on to Challenge as :challengee_user_id and :challenger_user_id... or caching a list of challenge ids on the user so you make one query instead of two.
You can set up another table called Challenges with fields challenger_id, challengee_id, status. The challenger and challengee ids would represent the pawns of course, not the user. The status would represent challenge_pending, challenge_on_going, there are other ways to do this obviously, but this one one.
This has the added benefit of allowing you to restrict pawn-to-pawn challenges to one each very easily if that's your desired behavior, among other things.
In your view controller
#challenges = Challenge.where("challengee_id IN (?)", Pawn.find_all_by_owner_id(current_user.id).map{|u| u[:id]})
#challenged = Challenge.where("challenger_id IN (?)", Pawn.find_all_by_owner_id(current_user.id).map{|u| u[:id]})
In your view
<%= #challenges.each do |challenge| %>
whatever
<% end %>
class User < ActiveRecord::Base
has_many :pawns
end
class Pawn < ActiveRecord::Base
belongs_to :user
has_many :challengers, class_name: 'Challenge'
has_many :challengees, class_name: 'Challenge'
belongs_to :result
end
class Challenge < ActiveRecord::Base
belongs_to :challenger, class_name: 'Pawn'
belongs_to :challengee, class_name: 'Pawn'
belongs_to :result
end
class Result < ActiveRecord::Base
has_one :challenge
has_one :winner, class_name: 'Pawn'
end
I have a Statuses table which contains only an id and name field (Active, Inactive, Pending, etc). I then have tables such as Users, Achievements, Badges for which each of these contain a status_id foreign key. Do the associations in my models look correct?
class Status < ActiveRecord::Base
has_many :achievements
has_many :badges
has_many :users
end
class User < ActiveRecord::Base
belongs_to :status
end
class Badge < ActiveRecord::Base
belongs_to :status
end
class Achievement < ActiveRecord::Base
belongs_to :status
end
I am struggling with how to properly read the difference between has_one and has_many in the case of a lookup table. I know that a user has one company and has one profile and a company has many users but this seems backwards to me.
The simplest association setup would be:
class User < ActiveRecord::Base
has_one :status
end
That exactly describes what you have posted. Your solution would work, but it is overkill for what you've described. All the association I posted above would do is add one method to the user model, i.e.
#user = User.find(1)
#user.status
If on the other hand you wanted simple semantics for showing all the users with a particular status, THEN you'd add
class Status < ActiveRecord::Base
has_many :users
end
so now you could do:
#status = Status.find_by_description('Active').first()
#status.users
Note that in BOTH cases, all that is needed is for the users model to have an attribute 'status_id'
Belongs_to is better suited when there is an implicit hierarchy , i,e,
class Child << ActiveRecord::Base
belongs_to :parent
end
Trying to figure out the best way to set up my models. Here's what I've going going on...
Models: Dog, Video, Photo, User
class Dog < ActiveRecord::Base
has_many :videos
has_many :photos
belongs_to :user
end
class Video < ActiveRecord::Base
has_many :dogs
belongs_to :user
end
class Photo < ActiveRecord::Base
has_many :dogs
belongs_to :user
end
class User < ActiveRecord::Base
has_many :dogs
has_many :videos
has_many :photo
end
Should I do has_many :through and a polymorphic?
In my previous iteration of this, I had DogPhoto and DogVideo models, but seems like I could have a single DogItem model that's polymorphic.
Thoughts?
Yes, a polymorphic association would apply here, and it would be a good practice. However, i think that you should have a new model like DogMedia or so, that would be the polymorphic association.
This way a user has a dog and a dog has many DogMedias. A DogMedia is a polymorphic association that can either be a photo, video or anything else you like :)
You could use has many through to get dogmedia for a user's dog directly yes. Something like :
User has many dog_medias through dog (plain language)
or just traverse it through dog :
user.dog.dog_media
If you do the latter, you can even create a Media instead of DogMedia class, create a delegate and execute the neat :
user.dog_media
directly ( The law of demeter : http://en.wikipedia.org/wiki/Law_of_Demeter )