I would like to do conditionally show/hide some elements in one of my views depending on if a #project.update changed attributes or not.
I am trying to understand WHY my model instances are not being marked as changed after a successful attribute update.
It appears that ActiveModel::Dirty is not properly indicating my model's attributes have changed or more likely I'm not using it properly.
Here is an annotated log of a PATCH request I make to my ProjectController#update action. In it you will see that the attributes change but the model instance does not reflect it. For what it's worth, the controller was generated by a Rails scaffold operation. There's nothing out of the ordinary.
#
# FORM SUBMITTED FROM BROWSER WITH A CHANGE TO THE ATTRIBUTE NAMED "title"
#
Started PATCH "/projects/2" for 127.0.0.1 at 2016-04-23 15:47:38 -0700
Processing by ProjectsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0JH/fEKx+Qk6mOY+eVTteKQUKrZUVXroKzMxuztrTzE/voI+PtzmQnJLGVM5bgdmzJyHDpAon3dzcvvjJ3yEtQ==", "project"=>{"title"=>"changed"}, "commit"=>"Update Project", "id"=>"2"}
Project Load (0.1ms) SELECT "projects".* FROM "projects" WHERE "projects"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
#
# DEBUGGER KICKS IN BEFORE THE UPDATE HAS HAPPENED
#
[40, 49] in app/controllers/projects_controller.rb
40:
41: # PATCH/PUT /projects/1
42: # PATCH/PUT /projects/1.json
43: def update
44: byebug
=> 45: respond_to do |format|
46: if #project.update(project_params)
47: format.html { redirect_to #project, notice: 'Project was successfully updated.' }
48: format.json { render :show, status: :ok, location: #project }
49: else
#
# PROJECT TITLE IS STILL UNMOLESTED
#
(byebug) #project
<Project id: 2, title: "ORIGINAL_TITLE", created_at: "2016-04-23 22:47:30", updated_at: "2016-04-23 22:47:30">
# PROVE PARAMS CONTAIN A CHANGED ATTRIBUTE
(byebug) project_params
<ActionController::Parameters {"title"=>"changed"} permitted: true>
#
# TRIGGER UPDATE AND PERSIST NEW TITLE
#
(byebug) #project.update(project_params)
(0.2ms) begin transaction
SQL (0.9ms) UPDATE "projects" SET "title" = ?, "updated_at" = ? WHERE "projects"."id" = ? [["title", "changed"], ["updated_at", 2016-04-23 22:48:13 UTC], ["id", 2]]
(3.5ms) commit transaction
true
#
# WAT?
#
(byebug) #project.changes
{}
(byebug) #project.changed?
false
(bye bug)
Here is my ProjectsController#update action (standard Rails scaffold):
# app/controllers/projects_controller.rb
# PATCH/PUT /projects/1
# PATCH/PUT /projects/1.json
def update
byebug
respond_to do |format|
if #project.update(project_params)
format.html { redirect_to #project, notice: 'Project was successfully updated.' }
format.json { render :show, status: :ok, location: #project }
else
format.html { render :edit }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
The corresponding view form (also from generated scaffolding):
# app/views/projects/_form.html.erb
<%= form_for(project) do |f| %>
<% if project.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(project.errors.count, "error") %> prohibited this project from being saved:</h2>
<ul>
<% project.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
It seems like you are trying to achieve something if there were any changes made during an update.
If that's the case, you could do something like this :
Use a after_save or after_update callback as necessary for the model and within your callback, if you check self.changes or self.changed? you'd get expected results.
Related
I dont't know why but when i'm trying to create post, i got this error.
I created a scaffold "post"
Then i wanted to get my posts in a different controller, in my Controller home.
I put "belongs_to :home" in my Post.rb
In this step, all is fine.
But when i'm trying to create post, i got this "Please review the problems below:"
Processing by PostsController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"0QgNSnhZWnsa+U9iYi1RB2Yk+qoW1be0Mj/o3579Es74oKBD452HQxZF144KBhR+in7UaSf9OLpzAyn8aJrB6A==", "post"=>{"title"=>"sfsf", "content"=>"sdfsfsfs", "author"=>"sdfsfsdf"}, "commit"=>"Create Post"} (0.4ms) BEGIN (0.3ms) ROLLBACK Rendering posts/new.html.erb within layouts/application Rendered posts/new.html.erb within layouts/application (14.6ms) User Load (0.7ms) SELECT users. FROM users WHERE users.id = 2 ORDER BY users.id ASC LIMIT 1 Rendered layouts/_header.html.erb (6.8ms) Rendered layouts/_footer.html.erb (0.9ms) Completed 200 OK in 1512ms (Views: 381.2ms | ActiveRecord: 1.4ms)*
post_controller.rb
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
# GET /posts
# GET /posts.json
def index
#posts = Post.all.order("created_at DESC")
end
# GET /posts/1
# GET /posts/1.json
def show
#posts
end
# GET /posts/new
def new
#post = Post.new
end
# GET /posts/1/edit
def edit
end
# POST /posts
# POST /posts.json
def create
#post = Post.new(post_params)
respond_to do |format|
if #post.save
format.html {redirect_to #post, notice: 'Article crée.'}
format.json {render :show, status: :created, location: #post}
else
format.html {render :new}
format.json {render json: #post.errors, status: :unprocessable_entity}
end
end
end
# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
respond_to do |format|
if #post.update(post_params)
format.html {redirect_to #post, notice: 'Article édité.'}
format.json {render :show, status: :ok, location: #post}
else
format.html {render :edit}
format.json {render json: #post.errors, status: :unprocessable_entity}
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#post.destroy
respond_to do |format|
format.html {redirect_to posts_url, notice: 'Article supprimé.'}
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
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:title, :content, :author)
end
end
post/new.html.erb
<div class="container left-box">
<h1 class="left-box-title">Ajouter un article</h1>
<%= simple_form_for #post, wrapper: :vertical_form do |f| %>
<%= f.error_notification %>
<%= f.input :title, label: 'Titre' %>
<p>Contenu</p>
<%= f.text_area :content, class: 'col-md-12 form-control content_post_create' %>
<%= f.input :author, label: 'Auteur' %>
<%= f.button :submit, class: "btn-primary", label: 'Mettre en ligne' %>
<%= f.button :button, "Recommencer", type: "reset", class: "btn-outline-secondary" %>
<% end %>
<%= link_to "Retour à l'accueil", posts_path %>
</div>
Your error comes from belongs_to :home in Post.rb:
The belongs_to association creates a one-to-one match with another
model. In database terms, this association says that this class
contains the foreign key. (source)
If you add the error full messages in your form like this:
<%= simple_form_for #post, wrapper: :vertical_form do |f| %>
<ul>
<% #post.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
...
<% end %>
you should see this error: Home must exist
To fix your issue you can remove the belongs_to association or, if Home is really a model, add the home_id to #post in your create action before saving.
i got some error when trying to make relation between Post and Hashtag by m : n
all things fine but when try to save log prints like this
Started POST "/posts" for 127.0.0.1 at 2018-03-05 21:43:00 +0900
Processing by PostsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"DxRb+4PAfOsDTCEMx+BcBNKK/LWAH5NxwVh6n7OChtwBL/svUQVEBj7mrWdCYB2BIUHO/Gql9UC6mKLMMoPqEg==", "post"=>{"title"=>"please", "content"=>"please!!!!!", "hashtags_attributes"=>{"0"=>{"title"=>"hash1"}, "1"=>{"title"=>"hash2"}, "2"=>{"title"=>"hash3"}}}, "commit"=>"Create Post"}
Unpermitted parameter: hashtags_attributes
Unpermitted parameters: title, content
Hashtag Load (0.2ms) SELECT "hashtags".* FROM "hashtags" WHERE "hashtags"."title" = ? LIMIT ? [["title", "hash1"], ["LIMIT", 1]]
Unpermitted parameters: title, content
Hashtag Load (0.1ms) SELECT "hashtags".* FROM "hashtags" WHERE "hashtags"."title" = ? LIMIT ? [["title", "hash2"], ["LIMIT", 1]]
Unpermitted parameters: title, content
Hashtag Load (0.1ms) SELECT "hashtags".* FROM "hashtags" WHERE "hashtags"."title" = ? LIMIT ? [["title", "hash3"], ["LIMIT", 1]]
(0.0ms) begin transaction
SQL (0.3ms) INSERT INTO "posts" ("title", "content", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["title", "please"], ["content", "please!!!!!"], ["created_at", "2018-03-05 12:43:00.937624"], ["updated_at", "2018-03-05 12:43:00.937624"]]
SQL (0.1ms) INSERT INTO "hashtags_posts" ("hashtag_id", "post_id") VALUES (?, ?) [["hashtag_id", 1], ["post_id", 2]]
(0.3ms) rollback transaction
Completed 500 Internal Server Error in 12ms (ActiveRecord: 1.4ms)
ActiveModel::MissingAttributeError (can't write unknown attribute `id`):
app/controllers/posts_controller.rb:36:in `block in create'
app/controllers/posts_controller.rb:35:in `create'
Rendering /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout
Rendering /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_source.html.erb
Rendered /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_source.html.erb (7.4ms)
Rendering /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb
Rendered /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (2.0ms)
Rendering /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb
Rendered /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (1.1ms)
Rendered /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/actionpack-5.0.6/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout (124.9ms)
i don't know why title content, and hashtags_attributes are unpermitted parameters. i set them in white list correctly
this is my codes
posts_controller.rb
def create
#post = Post.new(post_params)
3.times do |x|
tag = hashtag_params[:hashtags_attributes]["#{x}"]["title"]
a = Hashtag.find_or_create_by(title: tag)
#post.hashtags << a
end
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 }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
def hashtag_params
params.require(:post).permit(hashtags_attributes: [:title])
end
this is my post.rb
class Post < ApplicationRecord
has_and_belongs_to_many :hashtags
accepts_nested_attributes_for :hashtags
end
this is my hashtag.rb
class Hashtag < ApplicationRecord
has_and_belongs_to_many :posts
end
At last, my _form.html.erb
<%= form_for(post) do |f| %>
<% if post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% post.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :content %>
<%= f.text_area :content %>
</div>
<div class="field">
<%= f.fields_for :hashtags do |h|%>
<%=h.label :title, "해시태그"%>
<%=h.text_field :title%>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You only need to specify post_params correctly:
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 }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
def post_params
params.require(:post).permit(:title, :content, hashtags_attributes: [:id, :title])
end
You can read more here
And you need to change your migration
class CreateJoinTableHashtagsPosts < ActiveRecord::Migration[5.0]
def change
create_join_table :hashtags, :posts do |t|
t.index :hashtag_id
t.index :post_id
end
end
end
After it you need to run rake db:rollback && rake db:migrate.
Or you can use rake db:drop && rake db:create && rake db:migrate to recreate db from scratch, in this case you lose all existing data
Edit: Main problem was that when I added the reference fields, I did theater:reference and not theater:references so the field was not marked as a foreign key. Once I undid those migrations and redid them correctly, I was able to make this work.
In my showtimes controller, I am trying to automatically set the theater id to whatever theater owns the screen that the user inputed, but when I try to save it as an integer or a string, I get an error. Yet, when I try to save it as a theater object, I get "Unpermitted parameter: theater" from the console and a "Theater must exist" error from the rails application.
showtimes_controller:
class ShowtimesController < ApplicationController
before_action :set_theater, only: [:create, :edit]
before_action :set_showtime, only: [:show, :edit, :update, :destroy]
# GET /showtimes
# GET /showtimes.json
def index
#showtimes = Showtime.all
end
# GET /showtimes/1
# GET /showtimes/1.json
def show
end
# GET /showtimes/new
def new
#showtime = Showtime.new
end
# GET /showtimes/1/edit
def edit
end
# POST /showtimes
# POST /showtimes.json
def create
#showtime = Showtime.new(showtime_params)
respond_to do |format|
if #showtime.save
format.html { redirect_to #showtime, notice: 'Showtime was successfully created.' }
format.json { render :show, status: :created, location: #showtime }
else
format.html { render :new }
format.json { render json: #showtime.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /showtimes/1
# PATCH/PUT /showtimes/1.json
def update
respond_to do |format|
if #showtime.update(showtime_params)
format.html { redirect_to #showtime, notice: 'Showtime was successfully updated.' }
format.json { render :show, status: :ok, location: #showtime }
else
format.html { render :edit }
format.json { render json: #showtime.errors, status: :unprocessable_entity }
end
end
end
# DELETE /showtimes/1
# DELETE /showtimes/1.json
def destroy
#showtime.destroy
respond_to do |format|
format.html { redirect_to showtimes_url, notice: 'Showtime was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_showtime
#showtime = Showtime.find(params[:id])
end
def set_theater
screenInfo = Screen.where("id = ?", params[:showtime][:screen])
params['showtime']['theater'] = Theater.find(screenInfo[0]['theater_id'])
end
# Never trust parameters from the scary internet, only allow the white list through.
def showtime_params
params.require(:showtime).permit(:date, :time, :archived, :movie_id, :theater, :screen)
end
end
showtimes model:
class Showtime < ApplicationRecord
belongs_to :movie
belongs_to :theater
end
Showtimes _form
<%= form_for(showtime) do |f| %>
<% if showtime.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(showtime.errors.count, "error") %> prohibited this showtime from being saved:</h2>
<ul>
<% showtime.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :date %>
<%= f.date_select :date %>
</div>
<div class="field">
<%= f.label :time %>
<%= f.time_select :time %>
</div>
<div class="field">
<%= f.label :archived %>
<%= f.check_box :archived %>
</div>
<div class="field">
<%= f.label :movie_id %>
<%= f.text_field :movie_id %>
</div>
<div class="field">
<%= f.label :screen %>
<%= f.text_field :screen %>
</div>
<%= f.hidden_field :theater, :value => "" %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Error when trying to save as integer:
Theater(#70015922237640) expected, got Fixnum(#11723820)
Error when trying to save as string:
Theater(#70015868755420) expected, got String(#11739240)
Logs when trying to save as Theater object:
Started POST "/showtimes" for IP at 2016-11-08 20:22:37 +0000
Processing by ShowtimesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"nENPV5d6YRXdcx3H+Xa9ZypGtyFlaTg+zyENGB10TmW9OyWxLR9Dsl7nDoG9irq+3qApiNA2/oEqL5RZ0SXorA==", "showtime"=>{"date(1i)"=>"2016", "date(2i)"=>"11", "date(3i)"=>"8", "time(1i)"=>"2016", "time(2i)"=>"11", "time(3i)"=>"8", "time(4i)"=>"20", "time(5i)"=>"22", "archived"=>"0", "movie_id"=>"2", "screen"=>"1", "theater"=>""}, "commit"=>"Create Showtime"}
[1m[36mScreen Load (0.3ms)[0m [1m[34mSELECT "screens".* FROM "screens" WHERE (id = '1')[0m
[1m[36mTheater Load (0.2ms)[0m [1m[34mSELECT "theaters".* FROM "theaters" WHERE "theaters"."id" = ? LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
Unpermitted parameter: theater
[1m[35m (0.1ms)[0m [1m[36mbegin transaction[0m
[1m[36mMovie Load (0.2ms)[0m [1m[34mSELECT "movies".* FROM "movies" WHERE "movies"."id" = ? LIMIT ?[0m [["id", 2], ["LIMIT", 1]]
[1m[35m (0.2ms)[0m [1m[31mrollback transaction[0m
Rendering showtimes/new.html.erb within layouts/application
Rendered showtimes/_form.html.erb (13.6ms)
Rendered showtimes/new.html.erb within layouts/application (16.4ms)
Completed 200 OK in 323ms (Views: 86.5ms | ActiveRecord: 3.9ms)
How the hell do I save this parameter?
Have you tried assigning your object to an instance variable, and assigning it before saving?
On your before_action
def set_theater
#theather = ... # Code to find the theather
end
On your create action
def create
#showtime = Showtime.new(showtime_params)
#showtime.theather = #theather
... # Code to save and handle errors
end
You use theater instead of theater_id in several places in your code, and you'll need to change it in all the places, in order for this to work.
Firstly - you can't select a theater in our form... html doesn't recognise a type of theaterand will not pass one through - so your form needs to pass the theater_id instead (which will be an integer that it happily can deal with).
# eg here make sure it's a theater_id
<%= f.hidden_field :theater_id, :value => #theater.id %>
next - your require/permit is probably what's throwing some errors - you need that to be theater_id as well:
def showtime_params
params.require(:showtime).permit(:date, :time, :archived, :movie_id, :theater_id, :screen)
end
Now you need to fetch the theater out, using the screen-info param - but also keep in mind that this might come through as nil some times (so a guard-clause is always good):
def set_theater
if params[:showtime].present? && params[:showtime][:screen_id].present?
screen_info = Screen.find(params[:showtime][:screen_id])
#theater = Theater.find(screenInfo.theater_id)
end
end
Note: I have updated naming-schemes to be rail-standard and removed the thing where you try to set the theatre in params as below:
params['showtime']['theater'] = Theater.find(screenInfo[0]['theater_id'])
I don't know what you're actually trying to do with this line of code, but whatever it is, params doesn't work that way - consider that params is "the set of things that were passed through to us from the user, and are then thrown away" - we don't use it to store new values that we create ion the controller. That's what #variables are for
Can you explain more what you're trying to do and we'll figure out the right way to do it :)
I have a Client and Office Address Model, I want to create the office address when the client is created, so to do this I have decided to go the nested attribute direction.
When I try to create the Client with the Office Address I get this in the server output, doesn't give me much to go on and not sure how to proceed.
Started POST "/clients" for 127.0.0.1 at 2016-10-26 21:57:06 -0600
Processing by ClientsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"oC4Bwgw8zQrQCGU6RVGXXVwgWGIbOGmyP9gmJYUbyKXVXzgdeRGrp/wMnsmbF6spSeNxTpcHLJx+ZceBKjHxvQ==", "client"=>{"account_id"=>"", "name"=>"Test Client", "client_type"=>"Corp", "client_ident"=>"1234567890", "office_address_attributes"=>{"client_id"=>"", "unit_number"=>"317", "street_number"=>"1717", "street_name"=>"60 st SE", "city"=>"Clagary", "prov_state"=>"Alberta", "postal_zip"=>"T2A7Y7", "country"=>"CA"}}, "commit"=>"Create Client"}
Account Load (0.1ms) SELECT "public"."accounts".* FROM "public"."accounts" WHERE "public"."accounts"."subdomain" = $1 LIMIT $2 [["subdomain", "shawnwilson"], ["LIMIT", 1]]
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.1ms) BEGIN
(0.1ms) ROLLBACK
Rendering clients/new.html.erb within layouts/application
Rendered clients/_form.html.erb (32.8ms)
Rendered clients/new.html.erb within layouts/application (34.4ms)
Rendered shared/_signed_in_nav.html.erb (0.7ms)
Completed 200 OK in 109ms (Views: 102.0ms | ActiveRecord: 1.2ms)
So when I create the client I want to associate the client to the account and I want to associate the OfficeAddress to the Client.
My Client Model
class Client < ApplicationRecord
belongs_to :account, required: true
has_one :office_address
validates :office_address, presence: true
accepts_nested_attributes_for :office_address
end
My Office Address Model
class OfficeAddress < ApplicationRecord
belongs_to :client, required: true
end
My Client Controller
class ClientsController < ApplicationController
before_action :set_client, only: [:show, :edit, :update, :destroy]
# GET /clients
# GET /clients.json
def index
#clients = Client.all
end
# GET /clients/1
# GET /clients/1.json
def show
end
# GET /clients/new
def new
#client = Client.new
#client.build_office_address
end
# GET /clients/1/edit
def edit
end
# POST /clients
# POST /clients.json
def create
#client = Client.new(client_params)
respond_to do |format|
if #client.save
format.html { redirect_to #client, notice: 'Client was successfully created.' }
format.json { render :show, status: :created, location: #client }
else
format.html { render :new }
format.json { render json: #client.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /clients/1
# PATCH/PUT /clients/1.json
def update
respond_to do |format|
if #client.update(client_params)
format.html { redirect_to #client, notice: 'Client was successfully updated.' }
format.json { render :show, status: :ok, location: #client }
else
format.html { render :edit }
format.json { render json: #client.errors, status: :unprocessable_entity }
end
end
end
# DELETE /clients/1
# DELETE /clients/1.json
def destroy
#client.destroy
respond_to do |format|
format.html { redirect_to clients_url, notice: 'Client was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_client
#client = Client.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def client_params
params.require(:client).permit(:account_id, :name, :client_type, :client_ident, office_address_attributes: [:unit_number, :street_number, :street_name, :city, :prov_state, :postal_zip, :country, :client_id])
end
end
My Form
<%= simple_form_for(#client) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :account_id %>
<%= f.input :name %>
<%= f.input :client_type %>
<%= f.input :client_ident %>
</div>
<%= f.fields_for :office_address do |oa| %>
<%= oa.input :client_id %>
<%= oa.input :unit_number %>
<%= oa.input :street_number %>
<%= oa.input :street_name %>
<%= oa.input :city %>
<%= oa.input :prov_state %>
<%= oa.input :postal_zip %>
<%= oa.input :country %>
<% end %>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
Any assistance here would be much appreciated!
EDIT # 1 - Adds Byebug Errors
(byebug) #client.errors
#<ActiveModel::Errors:0x007fb249813488 #base=#<Client id: nil, account_id: nil, name: "Test Client", client_type: "Corp", client_ident: "1234567890", created_at: nil, updated_at: nil>, #messages={}, #details={}>
(byebug)
If you don't want to turn off validation on related models ( which is not ideal in some cases ) then you should set inverse_of like so
has_one :office_address, inverse_of: :client
inverse_of is really worth knowing about, this blog explains it well:
https://www.viget.com/articles/exploring-the-inverse-of-option-on-rails-model-associations
Please change you association as per below:
class OfficeAddress < ApplicationRecord
belongs_to :client, optional: true
end
Rails 5 association belongs to filed validate your client id, so your entry is getting rollback.
I fixed this issue by adding update_attributes to the create method of the client controller. like so:
def create
#client = Client.new(client_params)
respond_to do |format|
if #client.save
### This is what I added ###
#client.update_attributes!(account_id: #current_account.id)
#client.office_address.update_attributes!(client_id: #client.id)
format.html { redirect_to #client, notice: 'Client was successfully created.' }
format.json { render :show, status: :created, location: #client }
else
puts #client.errors
format.html { render :new }
format.json { render json: #client.errors, status: :unprocessable_entity }
end
end
end
My not be the best solution, however it gets it working.
I'm trying to add ajax to my comment form submission and I've run into an error when I'm trying to render a partial and I don't know how to solve it. I have everything set up properly and comments get created fine. But then I try to render the partial for the comments and I get this error:
undefined local variable or method `each' for #<#<Class:0xae4d760>:0xae59a78>
My create.js.erb
$("#comm_form_wrap").html("<%= escape_javascript(render :partial => "statuses/comment_form") %>");
$('#comment_box').val('');
$("#comments_wrap").html("<%= escape_javascript(render :partial => "statuses/comments") %>")
When I try to render statuses/comments is causing the error.
Here's my partial:
<% #comments.each do |comment| %>
<div class="com_con">
<%= Rinku.auto_link(comment.content).html_safe %>
</div>
<% end %>
So then I tried passing the variables like this
$("#comments_wrap").html("<%= escape_javascript(render :partial => "statuses/comments", :locals => {:comment => comment}) %>")
but it gives this error
undefined local variable or method `each' for #<#<Class:0xae4d760>:0xae59a78>
Not sure what I'm missing here, I'm sure it's something small. Can anyone help me?
View
<% if member_signed_in? %>
<div id="comm_form_wrap">
<%= render "comment_form" %>
</div>
<div id="comments_wrap">
<%= render "comments" %>
</div>
<% end %>
**Edit**
comments_controller.rb
class CommentsController < ApplicationController
before_filter :authenticate_member!
before_filter :load_commentable
before_filter :find_member
def index
redirect_to root_path
end
def new
#comment = #commentable.comments.new
end
def create
#comment = #commentable.comments.new(params[:comment])
#comment.member = current_member
respond_to do |format|
if #comment.save
format.html { redirect_to :back }
format.json
format.js
else
format.html { redirect_to :back }
format.json
format.js
end
end
end
def destroy
#comment = Comment.find(params[:id])
respond_to do |format|
if #comment.member == current_member || #commentable.member == current_member
#comment.destroy
format.html { redirect_to :back }
else
format.html { redirect_to :back, alert: 'You can\'t delete this comment.' }
end
end
end
private
def load_commentable
klass = [Status, Medium, Project, Event, Listing].detect { |c| params["#{c.name.underscore}_id"] }
#commentable = klass.find(params["#{klass.name.underscore}_id"])
end
def find_member
#member = Member.find_by_user_name(params[:user_name])
end
end
statuses_controller
def show
#status = Status.find(params[:id])
#commentable = #status
#comments = #commentable.comments.order('created_at desc').page(params[:page]).per_page(15)
#comment = Comment.new
respond_to do |format|
format.html # show.html.erb
format.json { redirect_to profile_path(current_member) }
format.js
end
end
Logs
Processing by StatusesController#show as HTML
Parameters: {"id"=>"86"}
[1m[35mMember Load (1.0ms)[0m SELECT "members".* FROM "members" WHERE "members"."user_name" IS NULL LIMIT 1
[1m[36mStatus Load (0.0ms)[0m [1mSELECT "statuses".* FROM "statuses" WHERE "statuses"."id" = ? LIMIT 1[0m [["id", "86"]]
[1m[35mComment Load (2.0ms)[0m SELECT "comments".* FROM "comments" WHERE "comments"."commentable_id" = 86 AND "comments"."commentable_type" = 'Status' ORDER BY created_at desc LIMIT 15 OFFSET 0
[#<Comment id: 82, content: "and why not try again ha", commentable_id: 86, commentable_type: "Status", member_id: 1, created_at: "2014-06-26 06:27:05", updated_at: "2014-06-26 06:27:05">]
[1m[36mMember Load (1.0ms)[0m [1mSELECT "members".* FROM "members" WHERE "members"."id" = 1 LIMIT 1[0m
[1m[35mCACHE (0.0ms)[0m SELECT "members".* FROM "members" WHERE "members"."id" = 1 LIMIT 1
[1m[36m (0.0ms)[0m [1mSELECT COUNT(*) FROM "comments" WHERE "comments"."commentable_id" = 86 AND "comments"."commentable_type" = 'Status'[0m
Rendered statuses/_comment_form.html.erb (8.0ms)
[1m[35mCACHE (0.0ms)[0m SELECT "members".* FROM "members" WHERE "members"."id" = 1 LIMIT 1
Rendered statuses/_comments.html.erb (95.0ms)
Rendered statuses/show.html.erb within layouts/application (406.0ms)
Rendered layouts/_query.html.erb (108.0ms)
Rendered search/_search.html.erb (22.0ms)
Rendered layouts/_menu.html.erb (592.0ms)
Completed 200 OK in 2956ms (Views: 2312.1ms | ActiveRecord: 10.0ms | Solr: 0.0ms)
Problem is your partial is calling #comments.each:
<% #comments.each do |comment| %>
2 issues:
#comments doesn't exist
Partials need to use local variables (they can't rely on #instance vars)
--
Partials
You'll be best doing this:
<%= render partial"statuses/comments", collection: #comments, as: comment %>
There is a little-known piece of functionality in Rails' partials which allows you to basically "reload" the partial for each member of a collection.
The reason this is important is because it cuts out a LOT of code from your partial. If you use the partial I posted above, you'll only need this code inside the partial:
#app/views/statuses/_comments.html.erb
<div class="com_con">
<%= Rinku.auto_link(comment.content).html_safe %>
</div>
If you set the correct #instance variable, and pass it into the collection option of the partial, Rails will basically reload the partial in a loop, like you have with the .each loop now
This will also work for singular items:
<%= render partial: "statuses/comments", object: #comment, as: comment %>
--
Instance Variable
The second issue is the setting of your instance variable
In your controller, you are not setting the #comments instance variable. This means you cannot load the contents of this variable into your view, consequently causing an issue like you've got
The way to fix this is very simple - use #instance variables you have set in your controller!
--
Escape
You may also need to look at how to escape quotes in your JS:
$("#comments_wrap").html("<%= escape_javascript(render :partial => \"statuses/comments\", :locals => {:comment => comment}) %>")
I'm not sure if this is applicable in this case, but I do know if you encapsulated quotes inside another set, you'll get errors from your JS
The answer above helped me solve this. Rendering the comments as a collection helped me render the partial through ajax and I also needed to define #comments in my create action in my comments_controller as well to ensure it doesn't render blank.