bonafernando
bonafernando

Reputation: 1139

Ruby on Rails Forms has_many with dynamic data from other model

the main objective here is to allow each role of users to view and edit only some fields of other users. But roles will be in constant growth, so it needs to be dynamic.

I'm having trouble creating a new role listing all user's field even if the relationship doesn't exists yet.

enter image description here

I would like this:

  <%= form.fields_for :role_assignment do |role_assignment_form| %>
    <tr>
      <td><%= role_assignment_form.text_field :user_field, disabled: true %></td>
      <td><%= role_assignment_form.check_box :show_permission, id: :role_show_permission %></td>
      <td><%= role_assignment_form.check_box :edit_permission, id: :role_edit_permission %></td>
    </tr>
  <% end %>

To load like this:

User.attribute_names.map do |att|
  role.role_assignment.find_by_user_field(att) || RoleAssignment.new(role: @role,user_field: att)
end

What is shown is only the current registers. I don't even know where to put the code above...

Database diagrams for Role and RoleAssignment:

+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int(12)      | NO   | PRI | NULL    | auto_increment |
| name  | varchar(255) | NO   |     | NULL    |                |
+-------+--------------+------+-----+---------+----------------+

+-----------------+--------------+------+-----+---------+----------------+
| Field           | Type         | Null | Key | Default | Extra          |
+-----------------+--------------+------+-----+---------+----------------+
| id              | int(12)      | NO   | PRI | NULL    | auto_increment |
| id_role         | int(12)      | NO   |     | NULL    |                |
| user_field      | varchar(255) | NO   |     | NULL    |                |
| show_permission | tinyint(1)   | NO   |     | 0       |                |
| edit_permission | tinyint(1)   | NO   |     | 0       |                |
+-----------------+--------------+------+-----+---------+----------------+

Models:

class Role < ApplicationRecord
  self.table_name = "role"
  has_many :user_role, foreign_key: :id_role
  has_many :user,  through: :user_role
  has_many :role_assignment, foreign_key: :id_role
  accepts_nested_attributes_for :role_assignment, :reject_if => :all_blank, :allow_destroy => true
end

class RoleAssignment < ApplicationRecord
  self.table_name = "role_assignment"
  belongs_to :role, foreign_key: :id_role
end

Thank you very much.

Upvotes: 0

Views: 73

Answers (2)

bonafernando
bonafernando

Reputation: 1139

@fongfan999's train of thought have helped a lot! But this is the complete answer for the specific question:

/app/controllers/roles_controller.rb:

class RolesController < ApplicationController
  def edit
    role_assignment = @role.role_assignment

    blacklist_attributes = %w(encrypted_password email_token forgot_token token provider uid oauth_token oauth_expires_at)
    user_attributes = User.attribute_names - blacklist_attributes

    (user_attributes - role_assignment.pluck(:user_field)).each do |user_field|
      @role.role_assignment << RoleAssignment.new(user_field: user_field)
    end
  end
end

/app/views/roles/_form.html.erb:

<%= form_with(model: role, local: true) do |form| %>

  <div class="field">
    <%= form.label :name %>
    <%= form.text_field :name, id: :role_name %>
  </div>
  <% unless role.new_record? %>
    <div class="field">
      <table>
        <tr>
          <th>Field</th>
          <th>Show?</th>
          <th>Edit?</th>
        </tr>
        <%= form.fields_for :role_assignment do |role_assignment_form| %>
          <tr>
            <td><%= role_assignment_form.text_field :user_field, disabled: true %></td>
            <td><%= role_assignment_form.check_box :show_permission, id: :role_show_permission %></td>
            <td><%= role_assignment_form.check_box :edit_permission, id: :role_edit_permission %></td>
          </tr>
        <% end %>
      </table>
    </div>
  <% end %>

  <br>

  <div class="actions">
    <%= form.submit %>
  </div>

<% end %>

Then I have redirected the save button to the edit form:

format.html { redirect_to edit_role_path(@role), notice: 'Role was successfully created now you can edit the permissions.' }

And the user experience is like this:

enter image description here

Then:

enter image description here

Thank you everyone for helping me solve it.

Upvotes: 0

fongfan999
fongfan999

Reputation: 2624

Try to the following:

# app/controllers/roles_controller.rb
role_assignment = @role.role_assignment

# You might not show these attributes, right?
blacklist_attributes = %w(id created_at updated_at)
user_attributes = User.attribute_names - blacklist_attributes

# Do not build existing user fields
(user_attributes - role_assignment.pluck(:user_field)).each do |user_field|
  @role.role_assignment.build(user_field: user_field)  
end

Should we use role_assignments (plural form) instead of role_assignment (singular form) in :has_many?

Upvotes: 1

Related Questions