Rails possible problem with has_many polymorphic association - ruby-on-rails

In my application, I have the following models:
User
Psychologist has_many classrooms
Teacher has_many classrooms
Parent has_many children
Director belongs_to school
Coordinator belongs_to school
My user model looks like the following:
class User < ActiveRecord
has_many :roles
def has_role? role_name
self.roles.where(role_type: role_name).any?
end
end
The Role model is polymorphic:
class Role < ApplicationRecord
belongs_to :user
belongs_to :roleable, polymorphic: true
end
And the other models are rollable.
My question is the following, it seems that this is not correct, because some models like: Director, Psychologists, Teachers, Coordinator and Parent. They only have relationships and their database tables do not have any other column except created_at, updated_at.
Is it okay to only create these models with relationships and have their tables without data?

Maybe you intend to use single table inheritance and not polymorphic relationships. If a Psychologist is a User, it should be the case.
Then you need to add a type column of type VARCHAR to the users table, and set up your models like:
class User < ApplicationRecord
has_many :roles
def has_role? role_name
self.roles.where(role_type: role_name).any?
end
end
class Psychologist < User
# no need to set up roles
end
class Teacher < User
# no need to set up roles
end
class Role < ApplicationRecord
belongs_to :user
# no polymorphic
end
The type column will be populated with the name of the actual class, like "Teacher" etc.
As an instance of Teacher is also an instance of User, it will have teacher.has_role?('foo') and teacher.roles, and you will be able to create a role like Role.create(user: teacher, name: 'bar').

Related

Active Record - Joining same table multiple times

I have a model A which belongs to my User model. I also have a model B, which belongs to my B model and also my User model (those are two different users, like doctor and patient). What I would like to do is some query like this:
B.joins(:user, {a: :user}).where("patient.name = 'some condition' or doctor.name='some other condition'")
The point here is: how can I specify that the users.name I'm querying is the one which is associated to A or B model?
Any help will be appreciated, thanks!
Perhaps something like this could work? (I haven't tested the code)
class User < ApplicationRecord
has_many :doctors
has_many :patients, through: :doctors
end
class Doctor < ApplicationRecord
belongs_to :user
has_many :patients
end
class Patient < ApplicationRecord
belongs_to :doctor
belongs_to :user
end
Patient.joins(user: {doctors: :user}).where("patient.name = 'some condition' or doctor.name='some other condition'")
Hope this helps!
You can create new classes named Patient and Doctor that inherit from User, then refer to users as patients and doctors instead of generic users.
class Doctor < User
has_many :a
end
class Patient < User
has_many :b
end
This is Single Table Inheritance:
https://api.rubyonrails.org/classes/ActiveRecord/Inheritance.html

create relationships for STI child

I have User, Teacher and ClassRomm model using STI as following
class User < ApplicationRecord
end
class Teacher < User
has_many :class_rooms
end
class Student < User
has_many_and_belongs_to :class_rooms
end
class ClassRoom < ApplicationRecord
belongs_to :teachers
has_many_and_belongs_to :students
end
my question how can i create migration for all relationships between user,teacher,Student and classRooms ?
for example should class_rooms has forignKey column for instructor_id or user_id
first of all
in ClassRoom association should be singular belongs_to :teacher, without s
also ClassRoom mast have column teacher_id, than create "third" table with name ClassRoomsStudents for has_many_and_belongs_to association
and it should works

Rails HABTM or has_many?

I have two models: Person, and Property. In the Person model I have a field which stores the role of the person(tenant, owner, admin, contractor, etc). Since each property will belong to an owner and potentially have one or more tenants, I thought this would be a good opportunity to use the HABTM model relation.
Do I have this right?
Also, how do I reference the attached model? Assuming my join model is named PropertiesPeople, and I wanted to fetch the tenants for a particular property, would that be the following?
#property.people.where(:role => "tenant")
If the same Person can have more than one Property, you should can use HABTM. Something like this:
class Person < ActiveRecord::Base
# in the people table you are storing the 'role' value
has_and_belongs_to_many :properties, join_table: 'people_properties'
end
class Property < ActiveRecord::Base
has_and_belongs_to_many :people, join_table: 'people_properties'
end
You should create the intermidiate table people_properties with the foreign keys, person_id and property_id.
The problem of this approach is that if a Person can be "tenant" in one property and "contractor" in another, for example, you can't store that information. In that case I will suggest using an intermidiate model, like this
class Person < ActiveRecord::Base
has_many :property_people
has_many :properties, through: :property_people
end
class PropertyPerson
# now you store here the 'role' value
belongs_to :person
belongs_to :property
end
class Property < ActiveRecord::Base
has_many :property_people
has_many :people, through: :property_people
end
I don't know for sure if the class names are successfully inferred from the relationships names, in that case you can always indicate class_name or even the foreign_key for the associations. Also you can indicate the table for a model, using self.table_name=

How to build associations for multiple roles?

I'm building a Rails application with multiple roles. I can't use inheritance against the user model because each role has very different properties and behavior (ie - different page access and abilities once logged in). So I've decided to build separate models for each role (ie - customer, service provider, customer representative, etc).
How would the associations for this work? I came up with the following but the Role class just looks bad to me. It wouldn't be so bad if the User could have multiple roles but since they only have one I'd have to write a custom validation in Role ensuring that only one role was selected. What do you guys think?
class User < ActiveRecord::Base
has_one :role
end
class Role < ActiveRecord::Base
has_one :customer
has_one :service_provider
has_one :customer_representative
end
class Customer < ActiveRecord::Base
end
class ServiceProvider < ActiveRecord::Base
end
class CustomerRepresentative < ActiveRecord::Base
end
I think the problem here is with your "Role" model. The role isn't actually a physical thing, but an interface that other objects should adhere to. Therefore, its a polymorphic relationship.
class User < ActiveRecord::Base
belongs_to :role, polymorphic: true
end
class Customer < ActiveRecord::Base
has_one :user, as: :role
end
class ServiceProvider < ActiveRecord::Base
has_one :user, as: :role
end
class CustomerRepresentative < ActiveRecord::Base
has_one :user, as: role
end
Then you need to add role_id and role_type to your user table.
This should get your what you want I believe.
Joe

Rails 3, CanCan to limit all query results throughout the app based on User's InstanceID

I have two tables
Users (name, email, password, instance_id, etc...)
example: james bond, james#abc.com, 1
Instance (id, domain)
example: 1, abc.com
Through out the application I want to make sure James Bond only sees data that is assigned to his instance = 1
So if I have a books table with (name, desc, instance_id), he only sees his instance's books.
I'm told CanCan is the way to go for this but I don't see how to make something set globally like the above, it seems more role based like admin, moderator, etc... Which I'll need but at a level lower than the data rules mentioned above.
Thoughts?
you could try using scopes like:
class Books < ActiveRecord::Base
# .... code ....
scope :personal, proc {|user| where(:instance_id => user.instance_id) }
# .... code ...
end
Now you can do:
#personal_books = Books.personal(#user)
you can read more about scopes here: http://www.railsdispatch.com/posts/rails-3-makes-life-better (scroll down a little bit)
Another way is to make an association like
User has_many Books through Instance
Then you'd have an additional legacy table mapping user to books over their instance.
It's an n:m relation. So a book could belong to multiple instances.
It would look like this:
User Table:
Users (name, email, password, instance_id, etc...)
Book Table:
Books (name, desc) # no more instance_id !
Mapping Table:
Assignments (instance_id, book_id)
And your Models would look like:
class Assignment < ActiveRecord::Base
belongs_to :book # foreign key - book_id
belongs_to :instance # foreign key - instance_id
end
class Instance < ActiveRecord::Base
has_many :assignments
has_many :books, :through => :assignments
end
class Books < ActiveRecord::Base
has_many :assignments
has_many :users, :through => :assignments
end
class User < ActiveRecord::Base
belongs_to :instance
end
The code to get all books of an user's instance would look like:
#user = User.find(:first)
#books = #user.instance.books.find(:all)
(I recommend you to read http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html )
Edit
You could try this if a book always belongs to only one instance:
class User < ActiveRecord::Base
belongs_to :instance
end
class Instance < ActiveRecord::Base
has_many :users
has_many :books
end
class Books < ActiveRecord::Base
belongs_to :instance
end
So you grab a user's books like:
#user.instance.books.find(:all)

Resources