What's wrong with my form in Ruby on Rails? - html

I'm trying to implement a prayer request and prayer response form, much like a micropost with a comment on it. The prayer requests are created and deleted as expected, but when I try to post a prayer response, it just reloads the page with no errors and no flash saying that the post was successful. This behavior happens wether the post is valid or not (no errors for a blank response, just a reload of the page) When I use the rails console, I'm able to build and save a prayer response successfully, and it shows up on rails server. What's wrong with my form? Based on the server log, I see that when I try to post a prayer response, it's using the RequestsController instead of the PrayerResponsesController. How do I fix this?
Here's my server log errors:
Started POST "/requests" for 127.0.0.1 at 2023-01-18 16:24:36 -0600
Processing by RequestsController#create as TURBO_STREAM
Parameters: {"authenticity_token"=>"[FILTERED]", "content"=>"Prayer response...", "request_id"=>"150", "commit"=>"Respond in Prayer"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ app/helpers/sessions_helper.rb:18:in `current_user'
CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ app/helpers/sessions_helper.rb:18:in `current_user'
Completed 400 Bad Request in 4ms (ActiveRecord: 0.1ms | Allocations: 2115)
ActionController::ParameterMissing (param is missing or the value is empty: request
Did you mean? request_id):
Here's my code:
controllers/prayer_responses_controller.rb
class PrayerResponsesController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
def create
#prayer_response = current_user.prayer_responses.build(prayer_response_params)
if #prayer_response.save
flash[:success] = "Prayer Response created!"
redirect_back(fallback_location: root_url)
else
render 'static_pages/requests', status: :unprocessable_entity
end
end
def destroy
#prayer_response.destroy
flash[:success] = "Prayer Response deleted"
redirect_back_or_to( root_url, status: :see_other )
end
private
def prayer_response_params
params.require(:prayer_response).permit(:content, :request_id)
end
def correct_user
#prayer_response = current_user.prayer_responses.find_by(id: params[:id])
redirect_to root_url, status: :see_other if #prayer_response.nil?
end
end
views/static_pages/requests.html.erb
<% provide(:title, "Prayer Requests") %>
<div class="row">
<aside class="col-md-4">
<section class="request-form">
<%= render 'shared/request_form' %>
</section>
</aside>
<div class="col-md-8">
<h3>Prayer Requests</h3>
<% if Request.all != nil %>
<%= render Request.all %>
<% end %>
</div>
</div>
views/requests/_request.html.erb
<li id="request-<%= request.id %>" class="requests">
<%= link_to gravatar_for(request.user, size: 50), request.user %>
<span class="user"><%= link_to request.user.name, request.user %></span>
<span class="content"><%= request.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(request.created_at) %> ago.
<% if current_user?(request.user) %>
<%= link_to "delete", request, data: { "turbo-method": :delete,
"turbo-confirm": "Are you sure?"} %>
<% end %>
</span>
<% if current_user != nil %>
<span>
<%= render 'shared/prayer_response_form', request_id: request.id %>
</span>
<span>
<% if request.prayer_responses.any? %>
<ol class="prayer_responses">
<% request.prayer_responses.reverse.each do |prayer_response| %>
<%= render prayer_response %>
<% end %>
</ol>
<% end %>
</span>
<% end %>
</li>
views/prayer_responses/_prayer_response.html.erb
<li id="prayer_response-<%= prayer_response.id %>">
<%= link_to gravatar_for(prayer_response.user, size: 30), prayer_response.user %>
<span class="user"><%= link_to prayer_response.user.name, prayer_response.user %></span>
<span class="content"><%= prayer_response.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(prayer_response.created_at) %> ago.
<% if current_user?(prayer_response.user) %>
<%= link_to "delete response", prayer_response, data: { "turbo-method": :delete,
"turbo-confirm": "Are you sure?"} %>
<% end %>
</span>
</li>
views/shared/_prayer_response_form.html.erb
<%= form_with(model: #prayer_response) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<center>
<div class="field">
<%= f.text_area(:content, placeholder: "Respond to this prayer request...") %>
</div>
<div><%= f.hidden_field :request_id, value: request_id %></div>
<%= f.submit "Respond in Prayer", class: "btn btn-primary" %>
</center>
<% end %>
models/prayer_response.rb
class PrayerResponse < ApplicationRecord
belongs_to :user
belongs_to :request
default_scope -> { order( created_at: :desc) }
validates :user_id, presence: true
validates :request_id, presence: true
validates :content, presence: true, length: { maximum: 500 }
end
models/request.rb
class Request < ApplicationRecord
belongs_to :user
has_many :prayer_responses, dependent: :destroy
default_scope -> { order(created_at: :desc) }
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 280 }
end
db/migrate/20230113172701_create_prayer_responses.rb
class CreatePrayerResponses < ActiveRecord::Migration[7.0]
def change
create_table :prayer_responses do |t|
t.text :content
t.references :user, null: false, foreign_key: true
t.references :request, null: false, foreign_key: true
t.timestamps
end
add_index :prayer_responses, [:user_id, :request_id, :created_at]
end
end
db/migrate/20230107204925_create_requests.rb
class CreateRequests < ActiveRecord::Migration[7.0]
def change
create_table :requests do |t|
t.text :content
t.references :user, null: false, foreign_key: true
t.timestamps
end
add_index :requests, [:user_id, :created_at]
end
end
config/routes.rb
root "static_pages#home"
get "/help", to: "static_pages#help"
get "/about", to: "static_pages#about"
get "/contact", to: "static_pages#contact"
get "/new_public_prayers", to: "static_pages#new_public_prayers"
get "/signup", to: "users#new"
get "/login", to: "sessions#new"
post "/login", to: "sessions#create"
delete "/logout", to: "sessions#destroy"
resources :users do
member do
get :following, :followers
end
end
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
resources :prayers, only: [:create, :destroy]
resources :requests, only: [:create, :destroy]
resources :prayer_responses, only: [:create, :destroy]
resources :comments, only: [:create, :destroy]
resources :private_prayers, only: [:create, :destroy]
resources :relationships, only: [:create, :destroy]
get "/private_prayers", to: "static_pages#private_prayers"
get "/prayers", to: "static_pages#home"
get "/comments", to: "static_pages#home"
get "/requests", to: "static_pages#requests"
get "/prayer_responses", to: "static_pages#requests"
end

The answer, actually, was in the static_pages_controller.rb file... I needed to add #prayer_response = current_user.prayer_responses.build to the requests page's function...
class StaticPagesController < ApplicationController
def home
if logged_in?
#prayer = current_user.prayers.build
#comment = current_user.comments.build
#feed_items = current_user.feed.paginate(page: params[:page])
end
end
def help
end
def about
end
def contact
end
def new_public_prayers
end
def private_prayers
if logged_in?
#private_prayer = current_user.private_prayers.build
else
redirect_to root_url
end
end
def requests
if logged_in?
#request = current_user.requests.build
#prayer_response = current_user.prayer_responses.build
else
redirect_to root_url
end
end
end
This made sure that the proper controller was followed when building a new prayer response, etc.

Related

undefined method ` for nil:NilClass

I am trying to display the new form page from another controller's page, I am used nested resources.
I am getting this error
undefined method `sublet_posts' for nil:NilClass
Student Model
class Student < ApplicationRecord
has_many :sublet_posts, :dependent => :destroy
end
Sublet_post model
class SubletPost < ApplicationRecord
belongs_to :student
end
route.rb
resources :students do
resources :sublet_posts
end
Student show view
<p id="notice"><%= notice %></p>
...
...
...
<h3>Sublet Post</h3>
<% #student.sublet_posts.each do |sublet_post| %>
<%= sublet_post.description %>
<%= link_to "Edit", edit_student_sublet_post_path(#student, sublet_post) %>
<% end %>
<h3>Add Sublet Post</h3>
<%= link_to "Add", new_student_sublet_post_path(#student)%>
Sublet_Post Controller
class SubletPostsController < ApplicationController
before_action :set_sublet_post, only: [:edit, :update, :destroy]
# POST /sublet_posts
# POST /sublet_posts.json
def create
#student = Student.find(params[:student_id])
#sublet_post = #student.create(sublet_post_params)
#sublet_post.student_id = current_member_id
redirect_to student_path(#student)
end
# PATCH/PUT /sublet_posts/1
# PATCH/PUT /sublet_posts/1.json
def update
#student = Student.find(params[:student_id])
#sublet_post = #student.sublet_posts.update(sublet_post_params)
redirect_to student_path(#student)
end
private
# Use callbacks to share common setup or constraints between actions.
def set_sublet_post
#sublet_post = SubletPost.find(params[:id])
end
# Only allow a list of trusted parameters through.
def sublet_post_params
params.require(:sublet_post).permit(......)
end
end
Sublet Post form view
<%= form_for [#student, #student.sublet_posts.build] do |form| %>. <----------------- Error
<% if sublet_post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(sublet_post.errors.count, "error") %> prohibited this sublet_post from being saved:</h2>
<ul>
<% sublet_post.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
Any help will be very appreciated. I didn't think the student controller was needed so I left it out.

How to show all the Users who have liked my post

I apologize for the newbie questions still relatively new to rails. I'm trying to show all the users who have liked my specific posts. I took a look at this similar question How to list the users who LIKE a given Post but it couldn't solve my problem. I listed below all the relevant simple code - thank you so much guys!!
Items Controller
class ItemsController < ApplicationController
before_action :authenticate_user!, only: [:new, :create]
before_action :set_item, only: [:show, :edit, :update, :destroy, :share]
def index
#items = Item.order("created_at DESC")
end
end
Index.html.erb
<% if user_signed_in? %>
<%= image_tag current_user.avatar, width: 70, class: "css-style" %>
<br>
<strong><%= link_to current_user.username, current_user, class: "profile-style" %></strong>
<ul><!--Trying to show all the users who have liked my specific posts here -->
<% item.likes.each do |like| %>
<li> <%= link_to(like.user.username, like.user) %></li>
<% end %>
</ul>
<br>
<br>
<% else %>
<%= link_to 'Login/SignUp', new_user_session_path %>
<% end %>
<% #items.each do |item| %>
<%= image_tag item.avatar.url(:medium), class: "block" %>
<div>
<%= render partial: "likes", locals: {item: item} %></span><%= item.likes_count %>
</div>
<% end %>
Items.rb
class Item < ApplicationRecord
has_many :likes, :counter_cache => true
belongs_to :user
has_attached_file :avatar, styles: { medium: "300x300>", thumb: "100x100>" }, default_url: "/images/:style/missing.png"
validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\z/
end
User.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, omniauth_providers: [:facebook]
has_many :likes
has_attached_file :avatar, styles: { medium: "300x300>", thumb: "100x100>" }, default_url: "/images/:style/missing.png"
validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\z/
def likes?(post)
post.likes.where(user_id: id).any?
end
end
Users Controller
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy, :share]
def index
#users = User.all
end
def show
#user = User.find(params[:id])
#items = Item.all
end
private
def set_user
#user = User.find(params[:id])
end
def item_params
params.require(:item).permit(:product, :amount, :city_id, :avatar)
end
end
Like.rb
class Like < ApplicationRecord
belongs_to :item, :counter_cache => true
belongs_to :user
end
Likes Controller
class Items::LikesController < ApplicationController
before_action :authenticate_user!
before_action :set_book
def create
#item.likes.where(user_id: current_user.id).first_or_create
respond_to do |format|
format.html {redirect_to #item}
format.js
end
end
def destroy
#item.likes.where(user_id: current_user.id).destroy_all
respond_to do |format|
format.html {redirect_to #item}
format.js
end
end
private
def set_book
#item = Item.find(params[:item_id])
end
end
Logs
Processing by ItemsController#index as HTML
Rendering items/index.html.erb within layouts/application
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
Item Load (0.3ms) SELECT "items".* FROM "items" ORDER BY created_at DESC
Rendered items/index.html.erb within layouts/application (287.6ms)
Completed 500 Internal Server Error in 313ms (ActiveRecord: 0.6ms)
ActionView::Template::Error (undefined local variable or method `item' for #<#:0x007f9afbfd14f0>
Did you mean? item_url
items_url
item_path
#items):
5:
6: <%= link_to current_user.username, current_user, class: "profile-style" %>
7:
8: <% item.likes.each do |like| %>
9: <%= link_to(like.user.username, like.user) %>
10: <% end %>
11:
app/views/items/index.html.erb:8:in `_app_views_items_index_html_erb___2312434021832006771_70151814680620'
Rendering /usr/local/lib/ruby/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb within rescues/layout
Rendering /usr/local/lib/ruby/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_source.html.erb
Rendered /usr/local/lib/ruby/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_source.html.erb (8.3ms)
Rendering /usr/local/lib/ruby/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb
Rendered /usr/local/lib/ruby/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (3.3ms)
Rendering /usr/local/lib/ruby/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb
Rendered /usr/local/lib/ruby/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (1.2ms)
Rendered /usr/local/lib/ruby/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb within rescues/layout (94.4ms)
In items/index.html.erb you are referencing #item.likes.each on line 9, but the instance variable #item has not been set in items_controller#index. You have only defined #items, hence the error you are receiving about nil not responding to #likes.
If you want to see the likes for all items, there are a number of ways to achieve this with Arel.
It's a simple mistake. I think the problem is with your partial.. you're calling the partial with locals and inside your template code you are referring to it with #item instead of item, here item is not an instance variable.
<% #items.each do |item| %>
<%= image_tag item.avatar.url(:medium), class: "block" %>
<div>
<%= render partial: "likes", locals: {item: item} %></span><%= item.likes_count %>
</div>
<% end %>
The thing is when you render a partial with locals you need to use the locals as normal variables not as instance variables.
This should work.
<% item.likes.each do |like| %>
<li> <%= link_to(like.user.username, like.user) %></li>
<% end %>
You need one more table(model) "likes" and association has-many-through. Something like this:
class Item
has_many :likes
has_many :users, through: :likes
end
class Like
belongs_to :user
belongs_to :item
end
class User
has_many :likes
has_many :items
has_many :liked_items, through: :likes
end

How to set up a Count from my Controller in Rails

I'm new to ruby on rails so please forgive the question. I tried following this example How to "count" user inputs from a Controller but kept getting nothing in my view. I am trying to create a count functionality on my share feature (similar to retweet which works perfectly now). >> So whenever a user shares a tweet there is a counter that changes with it as well. I have listed below all my relevant code. Thank you so much guys!!
tweets_controller.rb
class TweetsController < ApplicationController
before_action :set_tweet, only: [:show, :edit, :update, :destroy, :share]
def index
#tweets = Tweet.all
end
def share
tweet = current_user.tweets.create(tweet_id: #tweet.id)
if tweet.save
redirect_to tweets_path
else
redirect_to :back, alert: "Unable to share"
end
end
_share.html.erb
<div>
<div>Shared by <%= tweet.user.username %></div>
<%= render partial: "tweets/tweet", locals: {tweet: tweet.tweet} %>
</div>
_tweet.html.erb
<div class="row">
<div class="col-md-3"><%= tweet.watches_count %><%= render partial: "watches", locals: {tweet: tweet} %></div>
<div class="col-md-3"><%= link_to '<i class="fa fa-share fa-lg" aria-hidden="true"></i>'.html_safe, share_tweet_path(tweet), method: :post %></div>
<div class="col-md-6"><span class="pull-right"><%= tweet.likes_count %><%= render partial: "likes", locals: {tweet: tweet} %></span></div>
</div>
</div>
routes.rb
Rails.application.routes.draw do
resources :tweets do
resource :like, module: :tweets
resource :watch, module: :tweets
member do
post :share
end
end
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
resources :users, :controllers => "users_controller.rb"
root to: "tweets#index"
end
Migration CreateTweets
class CreateTweets < ActiveRecord::Migration[5.0]
def change
create_table :tweets do |t|
t.text :body
t.integer :user_id
t.integer :tweet_id
t.timestamps
end
end
end
Relevant Rake Routes
share_tweet POST /tweets/:id/share(.:format) tweets#share
tweets GET /tweets(.:format) tweets#index
POST /tweets(.:format) tweets#create

Hash password not saved in the password column

I am trying to store hash password in my users table while registration. Please see my code:
users_controller.rb
def login
#title = 'Login'
#render layout: 'login'
end
def create_login
user = User.authenticate(params[:user][:username], params[:user][:password])
if user
log_in user
redirect_to '/admin'
else
flash[:danger] = 'Invalid email/password combination' # Not quite right!
redirect_to :back
end
end
def register
#user = User.new
#title = 'Register'
end
def create_register
params[:user][:uniq_id] = generate_uniq
#user = User.new(create_user_params)
#raise #user.inspect
respond_to do |format|
if #user.save
format.html { redirect_to :success, success: 'Registration was successfully created.' }
format.json { redirect_to :register, status: :created, location: #users }
else
format.html { render :register }
format.json { render json: #users.errors, status: :unprocessable_entity }
end
end
end
private
def create_user_params
params.require(:user).permit(:uniq_id, :name, :username, :email, :password, :password_confirmation, :password_salt, :dob, :address)
end
register.html.erb
<%= form_tag("/register", method: "post") do %>
<%#= form_tag(#user) do |f| %>
<% if #user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= text_field :user, :name, placeholder:'NAME', required: true %>
<div style="position: relative;">
<span id="chk-username" style="position: absolute;font-size: 12px;right: 2%; bottom: 5%; z-index: 9; display: block;"></span>
<%= text_field :user, :username, placeholder:'USERNAME', 'data-validate':"/users/check_username", required: true %>
</div>
<div style="position: relative;">
<span id="chk-email" style="position: absolute;font-size: 12px;right: 2%; bottom: 5%; z-index: 9; display: block;"></span>
<%= text_field :user, :email, placeholder:'EMAIL', 'data-validate':"/users/check_email", required: true %>
</div>
<%= password_field :user, :password, placeholder:'PASSWORD', required: true %>
<%= password_field :user, :password_confirmation, placeholder:'CONFIRM PASSWORD', required: true %>
<div class="submit">
<input type="submit" value="REGISTER" >
<input type="button" onclick="location.href = '<%= request.base_url %>/login'" value="LOGIN" >
</div>
<p>Forgot Password ?</p>
<% end %>
user.rb
class User < ActiveRecord::Base
#has_secure_password
attr_accessor :password
before_save :encrypt_password
validates :name, presence: true
validates :name, length: { minumum:2, maximum: 30 }
validates :password, :presence =>true,
:length => { :minimum => 6, :maximum => 40 },
:confirmation =>true
validates :username, :presence => true,
:uniqueness => { :case_sensitive => false }
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
def self.authenticate(input_username, input_password)
user = find_by_username(input_username)
if user && user.password == BCrypt::Engine.hash_secret(input_password, user.password_salt)
user
else
nil
end
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
routes.rb
get 'register' => 'users#register'
post 'register' => 'users#create_register'
Here is my database table.
users.sql (customize table)
+----+----------+------------+-----------+----------------+
| id | name | username | password | password_salt |
+----+----------+------------+-----------+----------------+
| 1 | chinmay | chinu | NULL |$2a$10$15fWDt.. |
| 2 | sanjib | sanjib | NULL |$2a$10$85DyMr.. |
+----+----------+------------+-----------+----------------+
I get NULL value in my password column. Please help me and let me know where the error is in my code.
Your main error is that your are using attr_accessor :password to create a getter/setter for the password attribute that overrides the getter and setter that ActiveRecord creates from the database schema.
However your whole approach to password encryption is flawed - you should have password as a purely virtual attribute and name your database column password_digest or encrypted_password.
Unless its for pure learning purposes should use the has_secure_password macro that Rails provides instead of reinventing the password encryption wheel and getting hacked.
1. Add a password_digest column to user:
rails g migration AddPassWordDigestToUser password_digest:string:index
You might want to remove the password_salt column as well as it is not used by ActiveModel::SecurePassword.
class AddPassWordDigestToUser < ActiveRecord::Migration
def change
add_column :users, :password_digest, :string
add_index :users, :password_digest
remove_column :users, :password_salt
remove_column :users, :password
end
end
2. Add has_secure_password to the User model:
class User < ActiveRecord::Base
has_secure_password
end
3. RESTful routes
You may want to correct your routes so they are resource oriented and not action oriented and follow the rails conventions:
GET /registrations/new registations#new - sign up form
POST /registrations registations#create - create user
GET /sessions/new sessions#new - sign in form
POST /sessions sessions#create - sign in user
You can setup the routes with just:
resources :registrations, only: [:new, :create]
resources :sessions, only: [:new, :create]
See Rails Routing from the Outside In.
4. Binding forms and controllers.
You are setting up the controller properly however your form is not bound to the #user model instance you are creating in your controller.
This means that the values the user enters disappear after a unsuccessful form submission.
Also pay attention to the pluralization and naming of your variables! You are inconsistently using #user and #users. In this case #users will always be nil and cause an error.
app/controllers/registrations_controller.rb:
class RegistationsController < ApplicationController
def new
#user = User.new
end
def create
# Use a block instead of messing with the incoming params.
#user = User.new(user_params) do |u|
u.uniq_id = generate_uniq
end
if #user.save
respond_to do |format|
format.html { redirect_to root_path, success: "Welcome #{#user.email}" }
format.json { status: :created, location: #user }
end
else
respond_to do |format|
format.html { redirect_to :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
app/views/registrations/new.html.erb:
<%= form_for(#user, url: registrations_path) do |f| %>
<div class="row">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class="row">
<%= f.label :password %>
<%= f.password_field :password %>
</div>
<div class="row">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
</div>
<% end %>

Many-to-Many Nested Attributes in Rails 4 (with strong parameters)

I have been trying to figure this one out for a few days now. I am using Rails 4 (with the updated mass assignment technique) and trying to use nested attributes with a many-to-many relationship. My record is saving to the DB but everything is nil and I'm getting an "Unpermitted parameters: school, alumnis, prospects" error in my logs.
Here's what I have:
referral.rb
class Referral < ActiveRecord::Base
belongs_to :school
belongs_to :alumni
belongs_to :prospect
end
alumni.rb
class Alumni < ActiveRecord::Base
has_many :referrals
has_many :prospects, through: :referrals
accepts_nested_attributes_for :referrals
end
school.rb
class School < ActiveRecord::Base
has_many :referrals
has_many :prospects, through: :referrals
has_many :alumnis, through: :referrals
accepts_nested_attributes_for :referrals
end
prospect.rb
class Prospect < ActiveRecord::Base
has_many :referrals
has_many :alumnis, through: :referrals
accepts_nested_attributes_for :referrals
end
referrals_controller.rb
def create
#referral = Referral.create(referral_params)
respond_to do |format|
if #referral.save
# ReferralMailer.referrer_email(#referral).deliver
# ReferralMailer.referral_email(#referral).deliver
format.html { redirect_to #referral, notice: 'Referral was successfully created.' }
format.json { render action: 'show', status: :created, location: #referral }
else
format.html { render action: 'new' }
format.json { render json: #referral.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_referral
#referral = Referral.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def referral_params
params.require(:referral).permit(prospects_attributes: [:first_name, :last_name, :email], alumnis_attributes: [:first_name, :last_name, :email], schools_attributes: [:name])
end
_form.html.erb
<%= form_for(#referral) do |f| %>
<% if #referral.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#referral.errors.count, "error") %> prohibited this referral from being saved:</h2>
<ul>
<% #referral.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.fields_for :school do |builder| %>
<%= builder.label :name, "School Name" %>
<%= builder.text_field :name %>
<% end %>
<%= f.fields_for :alumnis do |builder| %>
<%= builder.label :first_name, "First Name" %>
<%= builder.text_field :first_name %>
<%= builder.label :last_name, "Last Name" %>
<%= builder.text_field :last_name %>
<%= builder.label :email, "Email" %>
<%= builder.text_field :email %>
<% end %>
<%= f.fields_for :prospects do |builder| %>
<%= builder.label :first_name, "First Name" %>
<%= builder.text_field :first_name %>
<%= builder.label :last_name, "Last Name" %>
<%= builder.text_field :last_name %>
<%= builder.label :email, "Email" %>
<%= builder.text_field :email %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
server log output
Processing by ReferralsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Ee+rREUU/0wGzNFTEaMxr8oRStaA53X9fmDrlVRyrD8=", "referral"=>{"school"=>{"name"=>"asdf"}, "alumnis"=>{"first_name"=>"asdf", "last_name"=>"asfd", "email"=>"asdf"}, "prospects"=>{"first_name"=>"asdf", "last_name"=>"asdf", "email"=>"asdf"}}, "commit"=>"Create Referral"}
Unpermitted parameters: school, alumnis, prospects
(0.1ms) begin transaction
SQL (0.5ms) INSERT INTO "referrals" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Fri, 12 Jul 2013 03:49:06 UTC +00:00], ["updated_at", Fri, 12 Jul 2013 03:49:06 UTC +00:00]]
(0.6ms) commit transaction
(0.1ms) begin transaction
(0.1ms) commit transaction
Redirected to http://localhost:3000/referrals/68
Referral Record
=> #<Referral id: 68, created_at: "2013-07-12 03:49:06", updated_at: "2013-07-12 03:49:06", school_id: nil, alumni_id: nil, prospect_id: nil>
You should pass also 'id' in each of your nested model params try :
def referral_params
params.require(:referral).permit(prospects_attributes: [:id,:first_name, :last_name, :email], alumnis_attributes: [:id,:first_name, :last_name, :email], schools_attributes: [:id,:name])
end
Have swing
Cheers
You parameters are not being past to the controller as strong parameters is expecting.
From you server log:
"referral" => {
"school" => {
"name" => "asdf" },
"alumnis" => {
"first_name" => "asdf",
"last_name" => "asfd",
"email" => "asdf"
},
"prospects" => {
"first_name" => "asdf",
"last_name" => "asdf",
"email" => "asdf"
}
}
Strong parameters is expecting prospects_attributes, alumnis_attributes and schools_attributes so prospects, alumnis and school are getting blocked and the objects are getting created without any attributes.