Assistance building correct multi-level polymorphic data structure - sql

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.

Related

Generic many-to-many polymorphic association using through?

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?

Rails - Using multple has_many through: associations joining same two models. Redundant has_many through:

Would association person and company through employment AND through interviewing be bad practice?
Why, specifically?
And does that answer apply to databases in general, not just rails?
Example:
employment.rb
class Employment < ActiveRecord::Base
belongs_to :people
belongs_to :companies
end
interview.rb
class Interview < ActiveRecord::Base
belongs_to :people
belongs_to :companies
end
person.rb
class Person < ActiveRecord::Base
has_many :employments
has_many :interviews
has_many :companies, through: :employments
has_many :companies, through: :interviews
end
company.rb
class Company < ActiveRecord::Base
has_many :employments
has_many :interviews
has_many :companies, through: :employments
has_many :companies, through: :interviews
end
Person and Company are associated through Employment, but also redundantly through Interview.
There's nothing wrong with having two models having multiple associations, including multiple associations of the same type. You will want to give the associations unique names, though - otherwise when you called (for instance) #person.companies, you wouldn't know what you were going to get - companies through employments, or companies through interviews.
I believe this question has a decent example: ruby on rails has_many :through association which has two columns with same model

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 model doubts has_one, has_many, belongs_to

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.