ruby on rails model - ruby-on-rails

I have two models as subject and teacher
Subject model as
class Subject < ActiveRecord::Base
belongs_to :sclass
has_many :subject_teachers
attr_accessible :sub_name
end
and Teacher model as
class Teacher < ActiveRecord::Base
# attr_accessible :title, :body
has_many :sclass_teachers
has_many :subject_teachers
attr_accessible :fname, :lname, :mob, :email
end
and created their join table as subject_teacher as many to many relationship
class SubjectTeacher < ActiveRecord::Base
belongs_to :subject
belongs_to :teacher
end
but i want to access teacher name in subject model / table how can i do it.
what and where did i write the perfect code so that i get specific teacher name to specific
subject as there are MANY TO MANY relationship?

You would do:
has_many :teachers through => :subject_teachers
as #Hugo said

You can access the teachers data from subject using the has_many :through relationship
http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association

What you say is a bit strange: you set up a many to many relationship, and require a specific teacher for a subject. So for a many to many relationship, you can get a list of teachers. And from that list you could take the first one. But if you know there is at max one teacher for a subject, just a has_many is a lot more convenient.
You can access the teachers by using #subject.subject_teachers.teachers, where #subject is a Subject instance.
If you use a has-many-and-belongs-to-many relationship, you can omit the intermediate class. You only need that is the relationship has additional data, like a date and time.

Related

Rails: has_many and belongs_to

I have two models in my app:
Position:
class Position < ActiveRecord::Base
belongs_to :report_to_position, class_name: 'Position', foreign_key: 'report_to_position_id'
has_many :employees
end
Employee:
class Employee < ActiveRecord::Base
belongs_to :position
def boss
self.position.report_to_position.employee
end
end
As you can see in boss getter I need to get one employee in this relation. The problem is Position class has many employees. How can I get just one object (boss for many employees) with this model logic?
Thanks!
You can't get the boss with this association setup, but you can get a boss. That is: you can get a superior. The Position class has_many :employees, which gives you two problems:
There's nothing in the Employee class to say "this is my direct superior".
There's nothing in the whole system to say "only one person can hold a position".
There's nothing in the Position class to say "this person is the boss".
That might be OK; if it's OK to get an (essentially random) superior for a given Employee, your boss method could read as follows:
def boss
self.position.report_to_position.employees.first
end
Overall, though, I'd rethink your schema. It's OK to keep your org chart and employee data separate, but you should consider moving the boss/subordinates associations to the Employee itself via a self join:
class Employee < ActiveRecord::Base
has_many :subordinates, class_name: "Employee", foreign_key: "boss_id"
belongs_to :boss, class_name: "Employee"
belongs_to :position
end
Structure
To help define Alex P's answer, you'll need to look at how you're defining the boss record in your associations, and tables.
The problem I can see is that because all your employee associative data is all the same, you have no way to determine who is the "boss" or not, hence your issue
There are two ways to ensure you have the boss association defined -
In the ActiveRecord associations
In the database / Model
--
Association
Probably the most reliable way to do this is to use an association, as described by Alex P:
#app/models/employee.rb
Class Employee < ActiveRecord::Base
belongs_to :boss, class_name: "Employee"
has_one :boss, class_name: "Employee", foreign_key: "boss_id"
end
--
Model
The way I would do it is to use the Ancestry gem -
#app/models/employee.rb
Class Employee < ActiveRecord::Base
has_ancestry
end
This has to be coupled with a datatable column called ancestry (string):
This will allow you to give each employee a Parent (who could be a Boss or a Manager etc). The beauty of this setup will be that you can create a real "tree" structure - employees will be able to have multiple managers / bosses as required

Model that has_many other model more than once

I have a Movie model, and a Person model.
The Movie model should have actors, writers and producers groups.
The actors, writers and producers are groups of persons, all from the same Person model.
What would be the best way to model this?
Thanks.
EDIT: Each person could be an actor, a writer and a producer at the same time. And they all have the same attributes.
EDIT 2:
What I want to do is something like this:
Class Movie < ActiveRecord::Base
attr_accessible :name, :image, :information, :duration, :director, etc...
has_many :persons, as: :writers <-- (IDK if this is possible)
has_many :persons, as: :producers <-- (IDK if this is possible)
has_many :persons, as: :actors <-- (IDK if this is possible)
end
Class Person < ActiveRecord::Base
attr_accessible :birthdate, :birthplace, :height, :information, :name, etc..
end
and creating groups in the Movie model, so I can call them like this:
#writers = #movie.writers
#actors = #movie.actors
#producers = #movie.producers
all made-up by Person objects, persons which could be any of the 3 types.
A Person could be involved in many other movies.
If you don't want different models, why not just add a profession column to your Person (or Movie) model? Assuming they have pretty much the same attributes, they can all be handled by the same table. You could use multiple: true, to allow to choose multiple professions per person.
P.S. Could you elaborate why you use a separate Movie model for these professions?
Edit:
If you have many professions and a person can have multiple professions at the same time, you might consider using a has_many :through relationship. As in:
class Person
has_many :assignments
has_many :professions, through: assignments
end
class Assignment
belongs_to :person_id
belongs_to :profession_id
end
class Profession
has_many :assignments
has_many :persons, through: assignments
end
This way, you could add additional attributes in the join model if necessary.
It all depends on how different the attributes are for your actors, writers and producers. If they will all have the same attributes (or mostly the same attributes), you could use single table inheritance. Have one of the attributes in your Person model be an attribute called type and this will trigger STI.
The use of STI or not depends on your tolerance for null values in your database. If the number of shared attributes between actors, writers and producers is low, you will end up with a number of null values and it might be better to have a different class for each one.
The official docs are limited on STI but I found a couple of interesting blog posts that go into more detail on implementation:
http://blog.thirst.co/post/14885390861/rails-single-table-inheritance
http://www.christopherbloom.com/2012/02/01/notes-on-sti-in-rails-3-0/
Given the new information you can do
Class Movie < ActiveRecord::Base
has_many :writers, :class_name => 'Person', :conditions => ['role = "writer"']
has_many :producers, :class_name => 'Person', :conditions => ['role = "producer"']
has_many :actors, :class_name => 'Person', :conditions => ['role = "actor"']
end
the conditions inside the :conditions will be different depending how you implement the roles assignation.
You have all the information here:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
Use activerecord :through option
http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association
You can use STI - Single Table Inheritance. For this you need to have type attribute in your Person model which will store the type of the Person.
class Person < ActiveRecord::Base
end
class Actor < Person
end
class Writer < Person
end
class Producer < Person
end
Given that all the roles have the same attribute and therefore can use the same class and model. If you want to have multiple roles at the same time your best bet to my understanding is to use an atribute role in the Person model. You can use a has_many association.

Model association between Company, Employee, and department

I am working in Ruby on Rails 3. And trying to map out three models which mimic the data of a Company its employees and their respective departments.
In arrived at the following solution:
class Company < ActiveRecord::Base
has_many :departments
has_many :employees, through => :departments
end
class Department < ActiveRecord::Base
belongs_to :company
has_many :employees
has_one :department_description
end
class DepartmentDescription < ActiveRecord::Base
belongs_to :department
end
class Employee < ActiveRecord::Base
belongs_to :department
end
Is this the 'correct' way to associate these models?
I think your last response may explain why you are struggling to find a correct way to associate these models.
It seems that you see your Department merely as a join_table, and that may be due to the fact that you don't fully understand the has_many => :through construction and that it actually allows your Department to be a proper model with many attributes and methods in it, hence also a 'description' attribute.
To create a separate DepartmentDescription model is actually a waste of resource. Chad Fowler has a few good examples for :has_many => through and nested resources in his Rails Recipes... so check that out.

Creating relationship between models. Rails 3

I have two model like so
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :remember_me, :fname, :lname
end
class Course < ActiveRecord::Base
belongs_to :university
attr_accessible :course_code, :department, :name, :path, :university_code, :department_code
set_primary_key :course_code
end
now a user can take many courses and a course could have many user. Also, the join table will have some attributes like the status of course user is taking, lectures he may have completed, etc.
How do I go about creating a relationship?
If a Course has a "uniqueness" notion, then you would need an intermediate model (Eg. UserCourseResults), so at any given state of your database, each Course can be unique. Then, you would just organize as follows:
User:
has_many :user_course_results
has_many :courses, through: user_course_results
Course:
has_many: :user_course_results
has_many: :users, through: user_course_results
UserCourseResult:
attr_accessible :result # So each user can have a result on each course, for instance.
belongs_to :user
belongs_to: :course
Do you see the point of the intermediate model?
HTH,
Pierre.
EDIT: i just saw your edit, and i guess this is exactly what you had in mind. You have to keep in mind that a User model only hold data that are unique per-user, the same notion goes for the Course model. So the best would probably be to have this intermediate "UserCourseResult" model, that would hold any user-course specific data...
This kind of relationship, when you don't need intermediate data in addition to the link between user and courses, is called HABTM (Has and belongs to many). You should have a look at this specific section in the active record doc (See has_many: through => * and has_and_belongs_to_many).

Proper Rails Association to use

I am trying to create an association between two tables. A student table and a computer table.
A computer can only ever be assigned to one student (at any one time) but a student can be assigned to multiple computers.
This is what I currently have in mind. Setting up a has-many through relationship and modifying it a bit.
class Student < ActiveRecord::Base
has_many :assignemnts
has_many :computers, :through => :assignments
end
class Computer < ActiveRecord::Base
has_one :assignment
has_one :student, :through => :assignments
end
class Assignment < ActiveRecord::Base
belongs_to :student
belongs_to :computer
end
Does this seem like the best way to handle this problem? Or something better sound out quickly to the experts here. Thanks!
You need first to decide if a simple one-to many relationship is enough for you.
If yes, it gets a lot easier, because you can get rid of the Assignment-class and table.
Your database-table "computers" then needs a student_id column, with a non-unique index
Your models should look like this:
class Computer < ActiveRecord::Base
belongs_to :student
end
class Student < ActiveRecord::Base
has_many :computers, :dependent => :nullify
end
"dependent nullify" because you don't want to delete a computer when a student is deleted, but instead mark it as free.
Each of your computers can only be assigned to a single student, but you can reassign it to a different student, for example in the next year.
Actually your approach is fine, as one offered by #alexkv. It is more discussion, than question.
Another thing if you want to use mapping table for some other purposes, like storing additional fields - then your approach is the best thing. In has_many :through table for the join model has a primary key and can contain attributes just like any other model.
From api.rubyonrails.org:
Choosing which way to build a many-to-many relationship is not always
simple. If you need to work with the relationship model as its own
entity, use has_many :through. Use has_and_belongs_to_many when
working with legacy schemas or when you never work directly with the
relationship itself.
I can advise you read this, to understand what approach better to choose in your situation:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
http://blog.hasmanythrough.com/2006/4/20/many-to-many-dance-off
You can also use has_and_belongs_to_many method. In your case it will be:
class Student < ActiveRecord::Base
has_many :assignemnts
has_and_belongs_to_many :computers, :join_table => 'assignments',
end
class Computer < ActiveRecord::Base
has_one :assignment
has_and_belongs_to_many :student, :join_table => 'assignments',
end
or you can rename assignments table to computers_students and remove join_table
class Student < ActiveRecord::Base
has_many :assignemnts
has_and_belongs_to_many :computers
end
class Computer < ActiveRecord::Base
has_one :assignment
has_and_belongs_to_many :student
end

Resources