I am developing some voting software where a user gets to vote for 3 different entries. For the votes model I was thinking of something like:
Votes:
Email Address - String
Verification Code - String
Verified - Boolean
First - Entry
Second - Entry
Third - Entry
Where Entry is another model in my rails application. How could I have a belongs to relationship for this? As i'd like to run queries like get the entries with the highest number of votes for first place. Is this the best approach to this problem?
User Model:
has_many :votes, :dependent => :destroy
has_many :entries, :through => :votes
Entry Model
has_many :votes, :dependent => :destroy
has_many :users, :through => :votes
Vote Model
belongs_to :user
belongs_to :entry
I would design the Vote object such that it contains just a user_id and an entry_id. Then your application logic would enforce that if they've already voted three times, they don't get to vote again. That way, when you query for the highest number of votes, you don't have to write special cases for the first, second and third entries.
As far as the belongs_to declaration, I would say that a vote belongs_to both User and Entry, and should be destroyed when either dependent object is destroyed, thus:
class Vote < ActiveRecord::Base
belongs_to :user, :dependent => :destroy
belongs_to :entry, :dependent => :destroy
end
Related
Before ask that, I must say that I really searched the web trying to find the answer, but I didn't found any that solved my problem. Answers like that doesn't solve my problem, because I don't want specify source_type, I want a generic thing - you'll understand.
Okay, let's go. I'm using a "many-to-many polymorphic" relationship in my project, but I can't get it to work. Before try to use polymorphic, this was just a "many-to-many" relationship and worked very well, so I want keep the structure, just adding polymorphic.
Structure:
Many-to-many: A user and visitor has many subject, and vice versa. We assume that user and visitor are followers and subject is followed.
Relationship table: This objects have a relationship table called "psrelations" (person-subject relations). This table stores the many-to-many relationship.
Models:
psrelations.rb:
attr_accessible :followed_id, :follower_type, :follower_id
belongs_to :follower, polymorphic: true
belongs_to :followed, class_name: "Subject"
user.rb:
has_many :psrelations, as: :follower, dependent: :destroy
has_many :followed_subjects, through: :psrelations, source: :followed
visitor.rb:
has_many :psrelations, as: :follower, dependent: :destroy
has_many :followed_subjects, through: :psrelations, source: :followed
subject.rb
has_many :psrelations, as: :followed, dependent: :destroy
has_many :followers, through: :psrelations, source: :follower
I want that "followers" return all "visitors" and "users", not just one or other, like specifying source_type.
Everything appears to be okay, but when I try to self.followers.include?(person) (where person can be either user or visitor), I got:
Cannot have a has_many :through association 'Subject#followers' on the polymorphic object 'Follower#follower'.
What am I doing wrong?
I have a many to many :through relationship between a set of classes like so:
class Company
has_many :shares
has_many :users, :through => :shares, :uniq => true
end
class User
has_many :shares
has_many :companys, :through => :shares, uniq => true
end
class Share
belongs_to :company
belongs_to :user
end
I want to ensure a unique relationship so that a user can only have one share in any one company, which is what I have tried to achieve using the "uniq" argument.
At first I thought this was working, however it seems the behaviour os the "uniq" is to filter on the SELECT of the record, not pre-INSERT so I still get duplicate records in the database, which becomes an issue if I want to start dealing with the :shares association directly, as calling user.shares will return duplicate records if they exist.
Can anyone help with an approach which would force truely uniq relationships? so that if I try adding the second relationships between a user and a company it will reject it and only keep the original?
Have you tried adding this to your Share class?
validates_uniqueness_of :user, scope: :company
Also, in your User class I think it should be:
has_many :companies, through: :shares
I hope that helps.
I have created 3 models for a small system. The intent here is to handle users, companies and their relationships. I anticipate to do a lot of lookups over the intersections and also want to have strict controls over the joins available, in that a user can only belong to one company, but company can have many users. I feel the best way to represent this is with the below. I would like some feedback on this preposition. So far my tests have worked fine validating my thesis and some small implementation efforts have gone fine, but when I look through a list of users to find their company I somehow run into issues and I have a feeling it is related to the has_one relationship, but not sure. Again thanks for constructive feedback.
class User < ActiveRecord::Base
has_one :companyrelationship, foreign_key: "user_id"
has_one :company, :through => :companyrelationship, dependent: :destroy
class Company < ActiveRecord::Base
has_many :companyrelationships
has_many :users, :through => :companyrelationships
class CompanyRelationship < ActiveRecord::Base
belongs_to :company
belongs_to :user
validates :user_id, presence: true, uniqueness: true
validates :company_id, presence: true
I do see one thing that I'm pretty sure is an error and won't work. I can also try to add a couple points of constructive criticism.
The error is that the CamelCase model name is converted to underscorey (sorry, I can't remember the term for that right now) case. All your references to companyrelationship should add an underscore. You can override the convention by setting the :class_name attribute, but I don't think that's what you wanted.
Another issue I see which isn't technically wrong, but is almost certainly not what you want is that the dependent: :destroy would delete the Company when you deleted a User. (Actually, I'm not sure dependent: :destroy works in a through: relationship.) I assume what you wanted was just to delete the CompanyRelationship. You can read the section on Dependent associations at this link:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
Next point is that the default foreign_key is the model + _id so you don't need to specify it in this case.
My last point is purely a style preference. I like doing my validations by validation type. You're doing them by variable. They are functionally equivalent, so feel to take it with a grain of salt. (I'm pretty sure user and user_id are equivalent, but feel free to stick to user_id if you're more comfortable with it.)
If it were completely up to me, I would code it this way:
class User < ActiveRecord::Base
has_one :company_relationship, dependent: :destroy
has_one :company, through: :company_relationship
class Company < ActiveRecord::Base
has_many :company_relationships
has_many :users, through: :company_relationships
class CompanyRelationship < ActiveRecord::Base
belongs_to :company
belongs_to :user
validates_presence_of :user, :company
validates_uniqueness_of :user
I hope that helps you get further.
I have a content model represented by class: content. Now users can rate content, review content or do both. I want to find all the content that a user have either rated, reviewed or rated and reviewed. The reviews table has a many-to-one association with the content table (meaning a content can be reviewed many times). A similar relationship exists between the ratings table and the content table.
I'm thinking I should do separate queries to find all rated content by a user, then all reviewed content by a user, then do a union. But I can't find out how to do a union that returns an active record relation. I need a relation because I want to paginate the results.
Thank you.
Ok, so first let's set up your models. From your explanation I'm thinking you'll want something like this:
class Content < ActiveRecord::Base
has_many :reviews
has_many :reviewing_users, :through => :reviews, :class_name => "User"
has_many :ratings
has_many :rating_users, :through => :ratings, :class_name => "User"
end
class User < ActiveRecord::Base
has_many :reviews
has_many :reviewed_contents, :through => :reviews, :class_name => "Content"
has_many :ratings
has_many :rated_contents, :through => :ratings, :class_name => "Content"
end
class Review < ActiveRecord::Base
belongs_to :content
belongs_to :user
end
class Rating < ActiveRecord::Base
belongs_to :content
belongs_to :user
end
And then for a given user you can find all the content that they've reviewed and/or rated with:
( user.reviewed_contents + user.rated_contents ).uniq
(user.reviewed_contents + user.rated_contents).uniq returns an array, not a relation, so beware. You can test this by attempting to call a class method on #posts (other than paginate).
You can still paginate though. just use #posts.paginate, as the will_paginate gem adds a paginate method to the array class.
I am trying to extract all Posts for a given User in the bellow relationships. Not sure whether I got them right, so I'll better explain. A User has Ownerships and Memberships in some Groups. A User can be either a Member or an Owner of the Group, but not both. Every Post has an id of the user and of the group. I think the problem is due to the relationships noted below. How can I get around it? One more thing. I have to also find all posts that were posted by other users in the user's groups. In other words, I have to pass through groups.
/-- Owner ---\
User -- -- Group -- Post
| \-- Member --/ |
|_______________________________|
class User < ActiveRecord::Base
has_many :posts, :dependent => :destroy
has_many :ownerships, :foreign_key => "user_id", :dependent => :destroy
has_many :memberships, :foreign_key => "user_id", :dependent => :destroy
# Problem with these two? I think so.
has_many :groups, :through => :memberships, :source => :user
has_many :groups, :through => :ownerships, :source => :user
class Ownership < ActiveRecord::Base
belongs_to :users, :class_name => "User"
belongs_to :groups, :class_name => "Group"
has_many :posts, :through => :groups, :source => :posts
class Membership < ActiveRecord::Base
belongs_to :users, :class_name => "User"
belongs_to :groups, :class_name => "Group"
has_many :posts, :through => :groups, :source => :posts
class Group < ActiveRecord::Base
has_many :posts, :dependent => :destroy
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :groups
The errors is coming from the line:
_groups = user.groups
The error as following:
Could not find the source association(s) :user in model Ownership. Try 'has_many :groups, :through => :ownerships, :source => '. Is it one of :users, :groups, or :postings?
First up: you're getting that error you're seeing because you've defined the associations in the Membership and Ownership table as this:
belongs_to :users
When they should belong to only one user, i.e. singular user:
belongs_to :user
But even then you will run into problems!
I think having a Membership model and an Ownership model are what will trip you up next. I don't understand what the purpose of having an Ownership model provides, other than signifying ownership of a group, which could be done by a field on the memberships table's records called owner for instance. It's over-engineering.
The problem with the Rails code you've got there is that you're defining that you have many posts through one association and then you're telling it that you have many posts through another association. In effect, you're doing this:
def posts
# find posts for the groups that I own
end
def posts
# find posts for the groups I belong to
end
It is not mistake here that there are two identically-named methods. This is exactly what you are doing by defining two has_many associations with the same name.
So hopefully now you can see why having an Ownership and a Membership model is the path to madness.
I would really recommend that you just have a Membership model that has a boolean attribute declaring an owner for a group. This would also mean that, if you wanted to, you could have new owners for a group in a very easy fashion: just flip the boolean. No need to create another record in another table.
One Membership model to rule them all.