I am getting unpermitted params when I am passing values from UI. The association is many to many between models.
class User < ApplicationRecord
has_many :user_posts
has_many :posts, through: :user_posts
end
class Post < ApplicationRecord
has_many :user_posts
has_many :users, through: :user_posts
accepts_nested_attributes_for :user_posts
end
class UserPost < ApplicationRecord
belongs_to :user
belongs_to :post
accepts_nested_attributes_for :user
end
posts/_form.html.erb
<%= form_with(model: post) do |form| %>
<% if post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% post.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%#= form.label :name %>
<%#= form.text_field :name %>
</div>
<div class="field">
<%= form.fields_for :user_posts do |f| %>
<%= f.collection_select :user_id, User.all, :id, :username, {include_blank: false, include_hidden: false }, {:multiple => true, :class=>""} %>
<% end %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
class PostsController < ApplicationController
before_action :set_post, only: %i[ show edit update destroy ]
# GET /posts or /posts.json
def index
@posts = Post.all
end
# GET /posts/1 or /posts/1.json
def show
end
# GET /posts/new
def new
@post = Post.new
@post.user_posts.build
end
# GET /posts/1/edit
def edit
end
# POST /posts or /posts.json
def create
@post = Post.new(post_params)
respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: "Post was successfully created." }
format.json { render :show, status: :created, location: @post }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /posts/1 or /posts/1.json
def update
respond_to do |format|
if @post.update(post_params)
format.html { redirect_to @post, notice: "Post was successfully updated." }
format.json { render :show, status: :ok, location: @post }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1 or /posts/1.json
def destroy
@post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: "Post was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
@post = Post.find(params[:id])
end
# Only allow a list of trusted parameters through.
def post_params
params.require(:post).permit(:username, user_posts_attributes: [:user_id])
end
end
When I run the below line it gives unpermitted params
@post = Post.new(post_params)
Unpermitted parameter: :user_id
Schema
\d users
Table "public.users"
Column | Type | Collation | Nullable | Default
------------+--------------------------------+-----------+----------+-----------------------------------
id | bigint | | not null | nextval('users_id_seq'::regclass)
username | character varying | | |
created_at | timestamp(6) without time zone | | not null |
updated_at | timestamp(6) without time zone | | not null |
\d posts
Table "public.posts"
Column | Type | Collation | Nullable | Default
------------+--------------------------------+-----------+----------+-----------------------------------
id | bigint | | not null | nextval('posts_id_seq'::regclass)
name | character varying | | |
created_at | timestamp(6) without time zone | | not null |
updated_at | timestamp(6) without time zone | | not null |
\d user_posts
Table "public.user_posts"
Column | Type | Collation | Nullable | Default
------------+--------------------------------+-----------+----------+----------------------------------------
id | bigint | | not null | nextval('user_posts_id_seq'::regclass)
user_id | bigint | | not null |
post_id | bigint | | not null |
created_at | timestamp(6) without time zone | | not null |
updated_at | timestamp(6) without time zone | | not null |
Am I missing something crucial? Any help is appreciated
rails - 6.1
ruby - 3.0
You do not need or even want to use nested attributes here.
If you just want to associate existing records you just need a select for the user_ids
attribute:
<%= form_with(model: post) do |form| %>
...
<div class="field">
<%= form.label :user_ids, 'Select the users' %>
<%= f.collection_select :user_ids, User.all, :id, :username, { include_blank: false, include_hidden: false }, { multiple: true, class: ""} %>
</div>
...
<% end %>
These setters and getters are created by has_many :users, through: :user_posts
.
And to whitelist the post[user_ids]
parameter as an array:
def post_params
params.require(:post).permit(
:foo,
:bar,
:baz,
user_ids: []
)
end
As you can see you don't need to explitly deal with the join table either as user_ids=
will do all the work for you.
Creating join table entities with nested attributes is only necissary if the join table actually contains additional data that must be input by the user.