rails model doubts has_one, has_many, belongs_to - ruby-on-rails-3

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.

Related

Assistance building correct multi-level polymorphic data structure

I am struggling with my data structures for a storage application. Consider the issue equivalent in terms of a private library that doesn’t use public library techniques.
A book is stored in a row on a shelf.
When a book is checked out, it is owned by the patron.
When a book is returned, I need to know where it should be stored by row and shelf.
I need to track the book so that I know that, if it is stored, where it is stored and, if it is checked out, who has it and what to do with it when it is returned. How I see it is as follows:
Book
has_one :location
has_one :patron, through: :location, source: :library, source_type: 'Patron'
has_one :row, through: :location, source: :library, source_type: 'Row'
Location
belongs_to :book
belongs_to :library, polymorphic: true
Patron
has_many :locations, as: :library
has_many :books, through: :locations
Row
has_many :locations, as: :library
has_many :books, through: :locations
I had already built the structure to this point only I had some has_many/belongs_to relationships turned backward which caused my nested forms to fail. I am trying to make sure I get it right this time. This appears okay to me as far as it goes. Is it?
However, it only goes down to rows and I need to identify shelves within rows. I’m at kind of a loss of how to build that data structure. I think that the shelf relationship with location must be polymorphic but that the row and shelf relationship can be direct. Does that make sense? I am unsure as to whether it is correct because location seems to be a shelf, not a shelf within a row. I am looking at the following:
Book
has_one :location
has_one :patron, through: :location, source: :library, source_type: 'Patron'
has_one :shelf, through: :location, source: :library, source_type: 'Shelf'
Location
belongs_to :book
belongs_to :library, polymorphic: true
Patron
has_many :locations, as: :library
has_many :books, through: :locations
Shelf
has_many :locations, as: :library
has_many :books, through: :locations
belongs_to :row
Row
has_many :shelves
Thanks for your help.
The solution presented in the question is working as designed. There is an issue regarding sorting on the foreign polymorphic columns, such as sorting the books by location, but that seems to be just an expected outcome. See question and answers to 24001705 if you are interested.

Rails active record query basics

I'm very new, and in the process of learning database stuff as well as Rails. Right now I have the following statement:
def tournaments_played
#tournaments_played = Tournament.count
end
With these models:
class User < ActiveRecord::Base
has_one :profile, dependent: :destroy
has_many :tournaments, through: :profile
class Profile < ActiveRecord::Base
belongs_to :user, class_name: "User"
has_many :tournaments
class Tournament < ActiveRecord::Base
belongs_to :profile
belongs_to :user
Obviously tournaments_played is returning a count of every record. I only want the tournaments for that user. I can paste in the scheme if needed!
I was going to give you a more specific answer, but you haven't indicated how you set up your Tournament model, so I can't give you actual code.
Take a look at this tutorial. You will find the answer to your question there.

Rails Belongs to with different column names

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

Unique Association :through

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.

Rails basic association

I'm trying to do a basic model association in rails.
Basically I have a List table which stores item_id and user_id.
One user can create multiple "list-items."
Is this the correct way to do it?
Thanks.
class Item < ActiveRecord::Base
has_many :users, :through => :lists
end
class User < ActiveRecord::Base
has_many :items, :through => :lists
end
class List < ActiveRecord::Base
belongs_to :user
belongs_to :item
end
Depending on what you want to reach, your solution is the right one (or not). I see the following cases:
You want to create an n:m association between items and users. So each item could be referenced by many users, and each user references many items. If this is the right context, then your solution is the right one. See the Rails Guides: Associations for more information on that.
An alternative for that situation could be to use the has_and_belongs_to_many Association. The situation is the same, but it does not make sense to talk about lists, there will be no model object for it.
If each users may have many lists, and each list may have many items, your solution would be wrong. This would be no n:m association with list as the join table in between, but two 1:n relations.
The code for the third example would look like that:
class User < ActiveRecord::Base
has_many :items, :through => :lists
has_many :lists
end
class List < ActiveRecord::Base
has_many :items
belongs_to :user
end
class Item < ActiveRecord::Base
belongs_to :list
end
In the first solution, you should add the relations for users to lists and items to list:
class Item < ActiveRecord::Base
has_many :lists
has_many :users, :through => :lists
end
class User < ActiveRecord::Base
has_many :lists
has_many :items, :through => :lists
end
If the "list" entity truly is a pure association/join, that is, it has no inherent attributes of its own, then you can simplify a bit and use has_and_belongs_to_many. Then you don't need a "List" class.
class Item < ActiveRecord::Base
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
has_and_belongs_to_many :items
end
Rails will look for the references in a "items_users" table, so in your migration, you need to create it a la:
create_table :items_users, :id => false do |t|
t.references :users, :items
end
Many people will tell you to always use has_many :through, but others (like me) will disagree - use the right tool for job.