Update has_many attributes through associations and work with an unsaved object

This is somehow related to my last question about unsaved objects , but now we are talking about the specific problem of using rails.

Models that I have:

class User < ActiveRecord::Base has_many :project_participations has_many :projects, through: :project_participations, inverse_of: :users end class ProjectParticipation < ActiveRecord::Base belongs_to :user belongs_to :project enum role: { member: 0, manager: 1 } end class Project < ActiveRecord::Base has_many :project_participations has_many :users, through: :project_participations, inverse_of: :projects accepts_nested_attributes_for :project_participations end 

In these models, when I create a new project, I can do it in the form ( fields_for , etc.), and then I can call update_attributes in the controller. Therefore, if I have users in the database, I can do this:

 u = Users.create # save one user in database (so we have at least one saved user) p = Project.new # add the user to the project as a manager # the attributes could come from a form with `.fields_for :project_participations` p.update_attributes(project_participations_attributes: [{user_id: u.id, role: 1}]) => true 

This works fine until I want to do something with the users project. For example, I want to add confirmation that there must be at least one user for the project:

 class Project < ActiveRecord::Base ... validates :users, presence: true # there must be at least one user in a project ... end 

Now it gives:

 u = Users.create p = Project.new p.update_attributes(project_participations_attributes: [{user_id: u.id, role: 1}]) => false p.errors => #<ActiveModel::Errors:... @base=#<Project id: nil>, @messages={:users=>["can't be blank"]}> p.users => #<ActiveRecord::Associations::CollectionProxy []> p.project_participations => #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: 1, project_id: nil>]> 

So, on unsaved projects .users empty. This already bothers me (see my last question about unsaved objects ). But in this case, of course, I can now get around this by doing validates :project_participations, presence: true instead of validates :users, presence: true , and that should mean the same thing.

But that would mean that I should never use the .users method (in any helper, model, view, ...), if I'm not sure that I am working with a saved object. What actually makes the .users method unusable (for example, when checking for the presence of a user).

If I call update_attributes as follows, checks are performed and saved:

 p.update_attributes(users: [u]) 

In doing so, he creates project_participation himself, so p.users works as expected. But here I can not set data such as role for project_participation this user.

So my questions are: can I make the .users method work regardless of whether the object is saved (I think not)? But how can I add users to an unsaved project as a manager / member and work with an unsaved project?

I hope my problem is clear.

0
validation ruby-on-rails activerecord has-many-through
source share
1 answer

I think I understand your question, and you are right in believing that you cannot use the .users method regardless of whether the project model is saved. The reason for this is that when defining the association in project (i.e. has_many :users, through: :project_participations, inverse_of: :projects ) you say that the rails read the users attribute from the database through the project_participations join table, and when there is no saved project, you have nothing to read from the database.

To add User to your project in a specific role, you will need to create a new ProjectParticipation model, which you will then associate with your project. If you then remove the users association and write your own users method, you must have access to your user collection regardless of whether the project was saved.

 class Project < ActiveRecord::Base has_many :project_participations ... def users project_participations.collect { |pp| pp.user } end end 

Then something like:

 u = Users.create p = Project.new pp = ProjectParticipation.new({user: u, project: p, role: 1}) p.project_participations << pp p.users 

Hope this helps.

0
source share

All Articles