Build and update using nested models using strong Rails options

Here are my 3 models.

User has_many :memberships has_many :teams, through: :memberships, dependent: :destroy accepts_nested_attributes_for :memberships Team has_many :memberships has_many :users, through: :memberships, dependent: :destroy accepts_nested_attributes_for :memberships Membership belongs_to :team belongs_to :user 

Here are some parts of my Team controller. My goal here is to add / update members of a specific team. Note that the source for adding items already exists as a user group.

 TeamsController def create @team = Team.new(team_params) @team.users << User.find(member_ids) #add leader and members to team if @team.save #redirect to created team else #show errors end end def update #TO DO: update team roster here if @team.update(team_params) #redirect to updated team else #show errors end end 

Strong parameters for the command controller

 #parameters for team details def team_params params.require(:team).permit(:name, :department) end #parameters for members (includes leader) def members_params params.require(:team).permit(:leader, members:[]) end #get id values from members_params and store in an array def member_ids members_params.values.flatten end 

For the form, I only have:

  • Name (text box)
  • Department (combo box)
  • Leader (a combo box, with the parameters generated depending on the selected department, is the selected user ID)
  • Members (combo box, several, with parameters generated depending on the selected department, is an array of user identifiers of selected users)

I can successfully create a team along with passing validation (both the team and the membership model) in my creation. However, I have no idea how to update the team, because if I use @team.users.clear and then just do the same from the creation (I know it's a little silly to do this), it will check, but he will save it regardless of the presence of an error or not.

FORM CODE

 <%= form_for(@team, remote: true) do |f| %> <%= f.label :name, "Name" %> <%= f.text_field :name %> <%= f.label :department, "Department" %> <%= f.select :department, options_for_select(["Architectural", "Interior Design"], department), include_blank: true %> <%= f.label :leader, "Leader" %> <%= f.select :leader, select_leaders(department, @team.id), {include_blank: true, selected: pre_select(:leader)} %> <%= f.label :members, "Members" %> <%= f.select :members, select_members(department, @team.id), {include_blank: true}, {id: "team_members", multiple: :multiple, data: {member_ids: pre_select(:members)}}%> <% end %> 

Note for the form:

  • This form works for both empty and completed forms.
  • The :members field is select2.

So my questions are:

  • How to update team members? Is it possible to update based on my strong parameters or should they be reviewed?
  • Should I reconsider my creation method?

SOME OPTIONS LEADING TO THE DECISION

Option number 1 (the best solution so far)

I just took advantage of this for first aid, so I think there is a better approach than what I did below. What I did here is to create user parameters with users from member_ids as values.

 TeamsController def create team = Team.new(team_params.merge({users: User.find(member_ids)})) ... end def update ... if @team.update(team_params.merge({users: User.find(member_ids)})) .. end 

Option number 2

Regardless of solution 1, I only had team_params as a strong parameter.

 TeamsController ... private def team_params params.require(:team).permit(:name, :department, :leader, members:[]) end 

I have created customization methods for both the leader and the members. But it seems that the members are overwriting the leader installer, because I used the update method for both setters, and the update uses the same resource as the users. A workaround seems possible with this option.

 Team ... def leader=(leader_id) #self.update(users: User.find(leader_id)) end def members=(members_ids) #self.update(users: User.find(members_id)) end 
+5
source share
1 answer

Since the leader and members are not so different from your scenario. You can change your models and view the form like this:

 class Team < ActiveRecord::Base has_many :memberships has_many :users, through: :memberships, dependent: :destroy accepts_nested_attributes_for :memberships end class User < ActiveRecord::Base has_many :memberships has_many :teams, through: :memberships, dependent: :destroy end class Membership < ActiveRecord::Base belongs_to :team belongs_to :user accepts_nested_attributes_for :user end 

and form submission code:

 <%= form_for(@team) do |f| %> <% if @team.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(@team.errors.count, "error") %> prohibited this team from being saved:</h2> <ul> <% @team.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= f.label :name %><br> <%= f.text_field :name %> </div> <%= f.fields_for :memberships do |m| %> <div class="field"> <%= m.label :memberships_name %><br> <%= m.text_field :name %> </div> <%= m.fields_for :user do |u| %> <div class="field"> <%= u.label :user_name %><br> <%= u.text_field :name %> </div> <% end %> <% end %> <div class="actions"> <%= f.submit %> </div> <% end %> 

Also, make sure you change this in your controller:

 # GET /teams/new def new @team = Team.new 3.times do # number of members you need to generate! @team.memberships.build{ |m| m.build_user } end end # GET /teams/1/edit def edit end # POST /teams # POST /teams.json def create @team = Team.new(team_params) respond_to do |format| if @team.save format.html { redirect_to @team, notice: 'Team was successfully created.' } format.json { render action: 'show', status: :created, location: @team } else format.html { render action: 'new' } format.json { render json: @team.errors, status: :unprocessable_entity } end end end private # Use callbacks to share common setup or constraints between actions. def set_team @team = Team.find(params[:id]) end # Never trust parameters from the scary internet, only allow the white list through. def team_params params.require(:team).permit(:name, memberships_attributes: [:id, :name, user_attributes: [:id, :name]]) end 

Although you can do this in the Rails console to do a quick code check:

 team_params = {"name"=>"Team", "memberships_attributes"=>{"0"=>{"name"=>"Membership 1", "user_attributes"=>{"name"=>"User 1"}}, "1"=>{"name"=>"Membership 2", "user_attributes"=>{"name"=>"User 2"}}, "2"=>{"name"=>"Membership 3", "user_attributes"=>{"name"=>"User 3"}}}} team = Team.new(team_params) team.save team.users #=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 1, name: "User 1", email: nil, created_at: "2014-09-04 11:25:48", updated_at: "2014-09-04 11:25:48">, #<User id: 2, name: "User 2", email: nil, created_at: "2014-09-04 11:25:48", updated_at: "2014-09-04 11:25:48">, #<User id: 3, name: "User 3", email: nil, created_at: "2014-09-04 11:25:48", updated_at: "2014-09-04 11:25:48">]> 

Hope this helps.

+7
source

Source: https://habr.com/ru/post/1201467/


All Articles