Using attr_accessible in a join model with has_many :through relationship - ruby-on-rails-3

I have a USER that creates a COMPANY and become an EMPLOYEE in the process. The employees table has an :user_id and a :company_id.
class User
has_many :employees
has_many :companies, :through => :employees
class Employee
belongs_to :user
belongs_to :company
attr_accessible :active
class Company
has_many :employees
has_many :users, :through => employees
Pretty basic. But here's the thing, the resource EMPLOYEE has other attributes than its foreign keys, like the boolean :active. I would like to use attr_accessible, but this causes some problems. The attribute :user_id is set right, but :company_id is nil.
#user.companies << Company.new(...)
Employee id:1 user_id:1 company_id:nil
So my question is: if :user_id is set right, despite it is not an attr_accessible, why :company_id isn't set right just the same? It shouldn't be an attr_accessible.
I'm using Rails 3.0.8, and have also tested with 3.0.7.

There are a lot of bits working together here.
You definitely want to use attr_accessible on all models. (Google "hack rails mass assignment" and read the Rails Guide on mass assignment.)
Once you add attr_accessible to a model, all assignments from hashes (mass assignments) are disabled except those you explicitly allow. However, you can still assign values directly, one at a time.
Foreign keys seem like a good thing to exclude from mass assignment, so don't list them in attr_accessible.
The .create and .build methods are not using mass assignment so they can set the value of one foreign key association. If there are several associations, as best I can tell, you'll have to set all but the first separately.
Finally, the actual IDs for the foreign keys are created by the database, not by ActiveRecord. So you'll either have to create parent and child records simultaneously, or you'll have to save the child first before you can assign the foreign key in the parent. Otherwise there is no ID available for the assignment.
It's not clear to me from your example how Employee is getting instantiated. But since the Employee belongs to both User and Company, I think something like this might work, assuming #user already exists:
company = #user.companies.create(..) # fills in company.user_id and saves to DB
employee = #user.employees.build(..) # fills in employee.user_id but does NOT save yet
employee.company = company # fills in employee.company_id
employee.save # now save to DB

The company_id is nil simply because the Company hasn't been saved to the database yet - Company.new simply creates the object in memory without saving it yet.
If you do:
#user.companies << Company.create(..)
or
#user.companies << Company.first
They should both work. There's even a shorter method which I think should work too:
#user.companies.create(..)
It all depends at which point you want to save the association. In some cases, it may be better not to save the employee and company models straight away, and instead wait for when the parent model (User) is saved. In which case you can use:
#user.companies.build(..)
(which is similar to the code in your example).
In terms of your active boolean attribute on the Employee model, if this is a column in the database, you don't need to explicitly declare attr_accessible for it - it'll be accessible by default.

Related

has_many association ignores restricting condition

I'm working on a Rails 3.0.x application (actually it's Hobo 1.3.x but that's not material to this question). Among the models, there are GraphPanes, GraphLabels, and LabelSets. A GraphPane can have GraphLabels and LabelSets. GraphLabels can belong to GraphPanes or LabelSets, but not both. So if a GraphLabel belongs to a LabelSet, I'd like to keep it from being associated to a GraphPane.
I am trying to enforce that with this code in the GraphPane model:
has_many :graph_labels, :conditions => 'label_set_id = NULL'
However, I'm still able to associate GraphLabels with not-null label_set_id with GraphPanes. Why? How can I stop this?
This question is superficially similar, but my relationship isn't polymorphic, so the nominal solution there doesn't help me.
The functionality of :conditions on has_many is to filter the results that are passed back via the graph_labels, not to protect objects from being added to the association.
If you add a graph_label with no label_set_id, the association will build, but if you then ask for graph_pane.graph_labels, it will not return that non-condition-matching graph_label.
The has_many/belongs_to relationship is saved on the belongs_to model, graph_label, and so the parent/has_many/graph_pane does not stop the graph_label from writing whatever it wants to its graph_pane_id attribute. This delegation of responsibility is correct, although frustrating, I agree.
Now, as for how to stop this, I'm not sure. It sounds like you need some sort of validation on the graph_label object, something along the lines of not allowing a graph_pane_id to be set on a graph_label if that graph_label's label_set_id is nil. Since the has_many/belongs_to relationship is saved on the graph_label, you should write the validation on the graph_label. That way, the graph_label will not be able to be saved with a new graph_panel_id unless it fulfills the condition.
Thoughts? Questions?
Reference:
has_many
Alternate Solution
I've reread your question and I think want you want here is a polymorphic association.
def GraphPane < ActiveRecord::Base
has_many :label_sets
has_many :graph_labels, as: :parent
end
def LabelSet < ActiveRecord::Base
belongs_to :graph_pane
has_many :graph_labels, as: :parent
end
def GraphLabel < ActiveRecord::Base
belongs_to :parent, polymorphic: true
end
That way, a GraphLabel can only have a single parent, which is what your “spec” above requires. Is there any reason not to implement the relations in this way?

Rails Form with has_many through - Which model to choose?

I have the following models:
Student has_many :subjects, :through => :classes
Subject has_many :students, :through => :classes
Class belongs_to :subject
belongs_to :student
The model class has an extra attribute (among the foreign keys to subject and students table) called level.
Basically I want to be able to have a form that will let the student to choose a subject and relate that subject to its record. So, I have this:
ClassesController < ApplicationController
def new
#list_of_subjects = Subject.all
# What should I do here?
end
My question is: How should I create the object for the form? From which model it should be, subject, student or class? I want to be able to create a record in the class table that would relate the student and the subject that the student has chosen, but I don't know if I am doing it wrong.
Thanks
I didn't think you could create a model called Class since it's a keyword, but that's neither here nor there...
First I think your controller and view should be using Student since it's the student that's selecting these things. Next, I think what you want to do is to add accepts_nested_attributes_for :class in your Student model which allows you to create an instance of the Class connector model from Student.
What you're trying to do sounds a little like something I tried to do. I have my full code there.
Using nested attributes to easily select associations in a form
I later refined it a bit in this question too to make the code less hideous:
Rails: How do I prepend or insert an association with build?
I know it's late, but I hope that helps.

Rails conditional validation based on polymorphic type

I am working with a Rails polymorphic inheritance configuration - I have the following setup:
User < ActiveRecord::Base
belongs_to :rolable, :polymorphic => true
Student < ActiveRecord::Base
has_one :user, :as => :rolable
accepts_nested_attributes_for :user
Teacher < ActiveRecord::Base
has_one :user, :as => :rolable
accepts_nested_attributes_for :user
I want to be able to capture email address for teachers and a username for students (who won't typically have an email address). I defined those as attributes of the User model, but now I'm stuck when I try to do validations for Student and Teacher. I didn't define them in their respective models because I'm using Devise and there will be other user types. Abstracting what is currently type to a Role pattern isn't a good fit for my particular scenario either.
Since username and email are properties of User what I basically want to do is check if the rolable_type field from the polymorphic relationship is type student and if so, make username required and email not, but in the new method that property isn't set. However Rails 'knows' this is a Student, so it feels like there's some way to check the instance type. The closest link I've found to what I'm shooting for is the third comment to the accepted answer in this question: How to apply different validation rule according to polymorphic association type (Rails)?, but I'm having trouble getting the method_missing syntax correct as I'm not experienced with metaprogramming. Am I on the right track with this? Or is there a simpler way? Or should I move the properties to the polymorphic models instead?

Has_many :through association

I made a relationship with the three models using has_many :through:
class Curriculum class < ActiveRecord::Base
has_many :interests
has_many :vacancies,: through => :interests
end
class Vacancy class < ActiveRecord::Base
has_many :interests
has_many :resumes,: through => :interests
end
class Interest < ActiveRecord:: Base
belongs_to :vacancy
belongs_to :curriculum
end
And to create curriculum and vacancy, I create them by administrative, i need to know how can i create the interest to the id of the vacancy, and how it will be logged on the system I have to get the id of it and make the relationship in creating a new bank interest. I wonder how I can program it to do so, and I wonder how the controller will get the create action, and what better way to do this.
First, try to read the whole "Guide to Rails on Associations", especially the part about has_many :through. Then check your schema if your db is migrated and contains for the table interests the necessary foreign keys to curriculums and vacancies called curriculum_id and vacancy_id.
If that is all in place, the following code will create the relationship between two objects:
#curr = Curriculum.find(1)
#vac = Vacancy.find(1)
#curr.interests << #vac
#curr.save
The last two lines creates an interest between #curr and #vac and store that on the database. So you should not use IDs and handle them directly, but work with objects instead.
The second part now is to provide a UI to allow the definition (and removal) of interests between curricula and vacancies. The base flow here is:
You have one curriculum in focus.
You have a link to add / remove curricula.
The view that opens shows a list of possible vacancies, where every vacancy has a checkbox.
By selecting (or deselecting) the check boxes, the IDs of the vacancies will be held in the params of the request sent to the controller.
See the (older) podcast Railscast #52 how to do that in a similar context. Or see the example for has_many :through with checkboxes.
An alternative way would be to use JQuery autocomplete, and add so interests one-by-one. See the nice podcast Railscast #258 which uses JQuery Tokeninput for that.
I think this is what your looking for:
HABTM Checkboxes
That's the best way to use an Has and Belongs to many association.

Rails: Make different references to a DB row refer to the same Ruby object

Suppose I have the following model relationship:
class Player < ActiveRecord::Base
has_many :cards
end
class Card < ActiveRecord::Base
belongs_to :player
end
I know from this question that Rails will return me a copy of the object representing a database row, meaning that:
p = Player.find(:first)
c = p.cards[0]
c.player.object_id == p.object_id # => false
...and therefore if the Player model modifies self, and the Card model modifies self.player in the same request, then the modifications won't take any notice of each other and the last-saved one will overwrite the others.
I'd like to work around this (presumably with some form of caching), so that all requests for a Player with a given id would return the same object (identical object_ids), thereby allowing both models to edit the same object without having to perform a database save-and-reload. I have three questions:
Is there already a plugin or gem to do this?
Are there good reasons why I shouldn't do this?
Can anyone give me some pointers on how to go about doing this?
This is supported in Rails 3.x. You can use the :inverse_of option for the has_many association for example. Documentation here (search for :inverse_of and Bi-directional associations).