Rails4 model ignores parent_id on 'create' action - ruby-on-rails

I have Subcategories which belong to Categories. My app saves (build) all my Subcategories under Subcategory with id = 1, even though my code seems to be ok and it is not supposed to do that:
Subcategories controller:
def create
#category = Category.find_by(params[:id])
#subcategory = #category.subcategories.build(subcategory_params)
if #subcategory.save
flash[:success] = "added subcategory"
redirect_to admin_categories_url
else
render :new
end
end
...
private
def subcategory_params
params.require(:subcategory).permit(:name, :category_id)
end
Subcategory.rb
class Subcategory < ActiveRecord::Base
belongs_to :category
has_many :products
validates :name, presence: true
validates :category_id, presence: true
end
Form:
<h3>Add a subcategory</h3>
<%= form_for [#category, #subcategory] do |f| %>
<%= f.text_field :name, placeholder: "Name" %>
<%= f.submit "Add a subcategory" %>
<% end %>
router:
resources :categories do
resources :subcategories
end
URL:
Adding a new subcategory to category with id = 3
http://localhost:3000/categories/3/subcategories/new
Logs:
Started POST "/categories/1/subcategories" for 127.0.0.1 at 2014-04-16 16:04:47 +0400
Processing by SubcategoriesController#create as HTML
Parameters: {"utf8"=>"✓", authenticity_token"=>"74U3VyAN6NqEjhkuHGNHnPda/yzpc+dIcn2xBJ6Zi2A=", "subcategory"=>{"name"=>"A name"}, "commit"=>"Add subcategory", "category_id"=>"1"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
Category Load (0.2ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = $1 LIMIT 1 [["id", 1]]
(0.1ms) BEGIN
SQL (0.2ms) INSERT INTO "subcategories" ("category_id", "created_at", "name", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["category_id", 1], ["created_at", "2014-04-16 12:04:47.151729"], ["name", "A name"], ["updated_at", "2014-04-16 12:04:47.151729"]]
(0.8ms) COMMIT
Id is 1
What can be the problem here?
Update
When doing
#category = Category.find_by(params[:category_id])
this error comes up:
ERROR: argument of WHERE must be type boolean, not type integer
LINE 1: SELECT "categories".* FROM "categories" WHERE (1) LIMIT 1
If doing
#category = Category.find(params[:category_id])
problem presists

In your create action you are picking up the id of the subcategory, not the category. Change it to:
def create
#category = Category.find(params[:category_id])
...
The same change to setting #category needs to be made in the new method too so that category_id comes back as 3 in the params from the view. Of course this duplication can be also DRYed up using a helper method in the controller.
To see the difference in the id, you can use rake routes and you should see something like:
new_category_subcategory GET /categories/:category_id/subcategories/new subcategories#new
This shows you that it expects the parameter category_id for the id of the category.

Look at your parameters it consist category_id not id
Parameters: {"utf8"=>"✓", authenticity_token"=>"74U3VyAN6NqEjhkuHGNHnPda/yzpc+dIcn2xBJ6Zi2A=", "subcategory"=>{"name"=>"A name"}, "commit"=>"Add subcategory", "category_id"=>"1"}
#=> "category_id"=>"1"
Category.find_by(category_id: params[:category_id])
So you need to do
def create
#category = Category.find_by(category_id: params[:category_id])
#subcategory = #category.subcategories.build(subcategory_params)
if #subcategory.save
flash[:success] = "added subcategory"
redirect_to admin_categories_url
else
render :new
end
end

Related

Rails 5.2.3 - Validation errors are displayed in console but are not passed to the view

When I submit a form with blank fields that are required I see the ROLLBACK and ActiveModel::Errors in the console but for some reason I can't render the errors in the view.
This is happening on both the new and edit templates.
Needless to say, creating a new record or editing an existing one with the required fields works fine.
This is what my code looks like:
Model: transactions.rb
class Transaction < ApplicationRecord
belongs_to :account
belongs_to :category
validates :date, :description, :amount, :category_id, :account_id, presence: true
end
Controller: transactions_controller.rb
def new
#transaction = Transaction.new
end
def create
#transaction = Transaction.create(transaction_params)
if #transaction.save
redirect_to account_path(#account), notice: "Transaction created"
else
render :new
end
end
def edit
if #transaction.amount > 0
#transaction_type = "income"
else
#transaction_type = "expense"
end
render :edit
end
def update
if #transaction.update(transaction_params)
redirect_to account_path(#account)
else
render :edit
end
end
private
def transaction_params
params.require(:transaction).permit(:date, :description, :amount, :category_id, :account_id)
end
View: new.html.erb (or edit.html.erb)
<ul>
<% #transaction.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
Log after submitting the form:
Started POST "/accounts/15/transactions" for 192.168.55.1 at 2019-09-07 03:00:24 +0000
Cannot render console from 192.168.55.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by TransactionsController#create as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"CJd+/rB6LcUqQAz0ZvY0cf8Dcu+2qzUYLCQHLWE2GElZtJrKbKq2EODFeVvKG6NkE2MxtIIjRreqHAnKu2sJ9A==", "transaction"=>{"date(1i)"=>"2019", "date(2i)"=>"9", "date(3i)"=>"7", "description"=>"", "category_id"=>"1", "amount"=>"0"}, "transaction_type"=>"Expense", "commit"=>"Add Transaction", "account_id"=>"15"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /var/lib/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98
Account Load (0.1ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT $2 [["id", 15], ["LIMIT", 1]]
↳ app/controllers/transactions_controller.rb:64
(0.1ms) BEGIN
↳ app/controllers/transactions_controller.rb:24
Category Load (0.2ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/controllers/transactions_controller.rb:24
(0.2ms) ROLLBACK
↳ app/controllers/transactions_controller.rb:24
(0.1ms) BEGIN
↳ app/controllers/transactions_controller.rb:26
(0.1ms) ROLLBACK
↳ app/controllers/transactions_controller.rb:26
Rendering transactions/new.html.erb within layouts/application
If I add p #transaction.errors in the create method or go into the Rails console and run #transaction.errors I do get the errors:
irb(main):026:0> #transaction.errors
=> #<ActiveModel::Errors:0x0000000006447158 #base=#<Transaction id: nil, date: nil, description: nil, amount: nil, account_id: nil, created_at: nil, updated_at: nil, category_id:
nil>, #messages={:account=>["must exist", "can't be blank"], :category=>["must exist", "can't be blank"], :date=>["can't be blank"], :description=>["can't be blank"], :amount=>["can't be blank"]}, #details={:account=>[{:error=>:blank}, {:error=>:blank}], :category=>[{:error=>:blank}, {:error=>:blank}], :date=>[{:error=>:blank}], :description=>[{:error=>:blank}], :amount=>[{:error=>:blank}]}>
Something else I tried is adding these 2 lines to the new.html.erb:
<%= #transaction.errors %> renders: #<ActiveModel::Errors:0x00007ff650570500>
<%= #transaction.errors.full_messages %> renders: []
Processing by TransactionsController#create as JS, your form is being submitted using ajax, you need to render a js view or use a non-ajax request. Are you using form_with for your form? form_with defaults to remote forms, use form_for instead or update the view using a js response.

Devise after_sign_in_path_for not working; being ignored when model has validations on: :update

My method is executing, but Devise is not using the return value at all. On the sign in page, it just reloads the page with a 'Signed in successfully' notice. It doesn't redirect to the value returned from the method.
Log
Started POST "/users/sign_in" for 127.0.0.1 at 2018-03-05 22:19:50 -0500
Processing by Users::SessionsController#create as HTML
Parameters: {"utf8"=>"√", "authenticity_token"=>"tQd5a43StP85oyyCpEmFU8cAkFXdJL2OLpuAK1+sqQC6/rIqcd+fB2iE4RT0RoPKPCqreNBYlv2bxjl9gZFrWg==", "user"=>{"email"=>"test11#example.com", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Log in"}
User Load (2.0ms) SELECT "users".* FROM "users" WHERE "users"."email" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["email", "test11#example.com"], ["LIMIT", 1]]
(5.0ms) BEGIN
User Exists (3.0ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = $1 AND ("users"."id" != $2) LIMIT $3 [["email", "test11#example.com"], ["id", 23], ["LIMIT", 1]]
Sector Load (0.0ms) SELECT "sectors".* FROM "sectors" INNER JOIN "sectors_users" ON "sectors"."id" = "sectors_users"."sector_id" WHERE "sectors_users"."user_id" = $1 [["user_id", 23]]
Region Load (0.0ms) SELECT "regions".* FROM "regions" INNER JOIN "regions_users" ON "regions"."id" = "regions_users"."region_id" WHERE "regions_users"."user_id" = $1 [["user_id", 23]]
Criterium Load (0.0ms) SELECT "criteria".* FROM "criteria" INNER JOIN "criteria_users" ON "criteria"."id" = "criteria_users"."criterium_id" WHERE "criteria_users"."user_id" = $1 [["user_id", 23]]
AssetType Load (0.0ms) SELECT "asset_types".* FROM "asset_types" INNER JOIN "asset_types_users" ON "asset_types"."id" = "asset_types_users"."asset_type_id" WHERE "asset_types_users"."user_id" = $1 [["user_id", 23]]
Company Load (1.0ms) SELECT "companies".* FROM "companies" WHERE "companies"."id" = $1 LIMIT $2 [["id", 42], ["LIMIT", 1]]
(5.0ms) ROLLBACK
############### /users/23/edit
Rendering users/sessions/new.haml within layouts/application
Rendered users/shared/_links.html.erb (3.0ms)
Rendered users/sessions/new.haml within layouts/application (251.2ms)
Rendered layouts/_footer.haml (15.0ms)
Completed 200 OK in 6554ms (Views: 3364.9ms | ActiveRecord: 86.1ms)
Notice it is rendering users/sessions/new.haml instead of the edit page?
Code
class ApplicationController < ActionController::Base
...
def after_sign_in_path_for(resource)
logger.debug '############### ' + edit_user_path(resource) if resource.is_a?(User) && resource.signature.blank?
return edit_user_path resource if resource.is_a?(User) && resource.signature.blank?
stored_location_for(resource) ||
if resource.is_a?(User)
dashboard_path
elsif resource.is_a?(Facilitator) && resource.name.nil?
edit_facilitator_path resource
elsif resource.is_a?(Facilitator)
facilitator_path resource
else
super
end
end
I completely commented out the method and it still reloaded the login page.
Started POST "/users/sign_in" for 127.0.0.1 at 2018-03-05 22:25:21 -0500
...
Rendering users/sessions/new.haml within layouts/application
Devise 4.4.0
Documentation:
https://github.com/plataformatec/devise/wiki/How-To%3A-Redirect-to-a-specific-page-on-successful-sign-in-and-sign-out
http://www.rubydoc.info/github/plataformatec/devise/master/Devise/Controllers/Helpers:after_sign_in_path_for
I added
def after_sign_in_path_for(resource)
logger.debug '############# ' + resource.errors.full_messages.join(', ')
And did discover validation errors like
############# Title can't be blank, Country can't be blank, Signature can't be blank, ...
But it does show the notice
Signed in successfully.
And I do have a session and can navigate elsewhere. My validations are on: :update.
validates :email, :name, :title, :phone, :address1, :city, :state, :zip, :country, :type, :signature, presence: true, on: :update
This should not cause log in behavior errors.
I commented all validations on the model and it does work, but this is highly unusual! Validations should not affect login behavior. There has to be a workaround.
Started POST "/users/sign_in" for 127.0.0.1 at 2018-03-05 23:11:43 -0500
SQL (15.0ms) UPDATE "users" SET "current_sign_in_at" = $1, "last_sign_in_at" = $2, "current_sign_in_ip" = $3, "sign_in_count" = $4, "updated_at" = $5 WHERE "users"."id" = $6 [["current_sign_in_at", "2018-03-06 04:11:44.225501"], ["last_sign_in_at", "2017-11-09 01:22:28.245231"], ["current_sign_in_ip", "127.0.0.1/32"], ["sign_in_count", 6], ["updated_at", "2018-03-06 04:11:44.230506"], ["id", 23]]
Redirected to http://localhost:3000/users/23/edit
Completed 302 Found in 2183ms (ActiveRecord: 48.0ms)
As you only want your validations on update, I guess that you only need them for a specific form, since your users are still valid even without this validations.
In that case I would use a so called form object, that does the on update validations for you and remove the on update validations on your user model. In that case your validations don't affect other parts of your app.
Here is a good guide on how to do that with just using ActiveModel.
Example:
app/models/user.rb
class User < ApplicationRecord
# remove the validations here
end
app/forms/user_edit_form.rb
class UserEditForm
include ActiveModel::Model
ATTRIBUTES = :email, :name, :title, :phone,
:address1, :city, :state, :zip,
:country, :type, :signature
attr_accessor *ATTRIBUTES
validates *ATTRIBUTES, presence: true
def update(user)
if valid?
user.update(self.attributes)
end
end
def self.for_user(user)
new(user.slice(*ATTRIBUTES)
end
end
users_controller.rb
class UsersController
def edit
#user = User.find(params[:id])
#user_edit_form = UserEditForm.for_user(#user)
end
def update
#user = User.find(params[:id])
#user_edit_form = UserEditForm.new(user_update_params).update(#user)
if #user_edit_form.errors?
render :edit
else
redirect_to user_path(#user)
end
end
def user_update_params
# ...
end
end
edit.html.erb
<%= form_for #user_edit_form, url: user_path(#user), method: :patch do |f| %>
# ...
<%= f.submit %>
<% end %>
Alternative
An alternative could be to add a virtual attribute to the model and run your validations conditionally in the user controller.
class User < ApplicationRecord
attr_accessor :profile_complete
with_options if: -> { profile_complete } do
validates :email, :name, :title, :phone, :address1, :city, :state, :zip, :country, :type, :signature, presence: true
end
end
users_controller.rb
class UsersController < ApplicationController
def update
#user = User.find(params[:id])
#user.profile_complete = true
if #user.update(user_update_params)
redirect_to #user
else
render :edit
end
# ...
end
end
Note: Instead of using a virtual attribute (attr_accessor) you could also use a real DB attribute, so you can also actually know which users filled out their profile completely.
Alternative 2
In some other projects I also used state machine gems (there are a couple e.g. aasm or statemachines-activerecord) to do somehing similar. Some of the state machine gems even support having validations only for certain states or transisions.
Check this documentation https://github.com/plataformatec/devise/wiki/How-To:-redirect-to-a-specific-page-on-successful-sign-in. They have clearly mentioned when you will go in loop and solution for it. Check Preventing redirect loops section in above doc.
You might need conditional validation on your model. Something like this:
validates :email, :name, :title, :phone, :address1, :city, :state, :zip, :country, :type, :signature, presence: true, on: :update, unless: Proc.new {|user| user.current_sign_in_at.present? }
Devise will update sign_in_at whenever sign_in happens. Which will trigger update action and related validations.
Also Documentation said the allow_nil: true instruct the model to validate the fields ONLY if it exists on the submitted form.

Rails params for nested forms

I've been battling for days now to get data to save for my nested form. I basically want to be able to store a users reason for cancelling a project, along with the last stage of the project before it was cancelled. But I just can't seem to get the actions cancel, cancel_save, and cancel_params to play nicely!
Controller
before_action :correct_user, only: [:show, :edit, :update, :destroy, :cancel, :cancel_save]
...
def cancel
#project.build_project_close_reason
end
def cancel_save
#project.build_project_close_reason(cancel_params)
#project.update(project_status_id: 10)
redirect_to root_path, notice: 'Project has been successfully cancelled.'
end
private
def correct_user
#user = current_user
#project = current_user.projects.find_by(id: params[:id])
end
redirect_to user_projects_path, notice: "You are not authorised to view this project" if #project.nil?
end
def cancel_params
params.require(:project).permit(project_close_reason_attributes: [:comment]).merge(project_close_reason_attributes: [project_id: #project.id, last_status_id: #project.project_status_id ])
end
Models
class Project < ApplicationRecord
belongs_to :user
has_one :project_close_reason
accepts_nested_attributes_for :project_close_reason #adding this seemed to make no difference?
end
class ProjectCloseReason < ApplicationRecord
belongs_to :project
end
class User < ApplicationRecord
... # All standard devise stuff
has_many :projects
end
Nested form in the view
<%= form_for([#user, #project], url: {action: "cancel_save"}, method: :post) do |f| %>
<%= fields_for :project_close_reason do |pcr| %>
<div class="field entry_box">
<%= pcr.label "Why are you cancelling this project? (This helps us improve!)" %>
<%= pcr.text_area :comment, class: "form-control entry_field_text" %>
</div>
<% end %>
<div class="actions center space_big">
<%= f.submit "Cancel Project", class: "btn btn-lg btn-warning" %>
</div>
<% end %>
Routes
devise_for :users
devise_for :admins
resources :users do
resources :projects do
get "cancel", :on => :member
post "cancel" => 'projects#cancel_save', :on => :member
end
end
This is the error I get when I try and submit the form:
ActionController::ParameterMissing in ProjectsController#cancel_save
param is missing or the value is empty: project. It references the cancel_params action
Thanks for any help!
UPDATE
Here is the log when I call cancel_save
Started POST "/users/2/projects/10/cancel" for ::1 at 2016-09-29 10:03:44 +0200
Processing by ProjectsController#cancel_save as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"h6K+VFyjW/dV189YOePWZm+Pmjey50xAMQIJb+c3dzpEaMv8Ckh3jQGOWfVdlfVH/FxolbB45fXvTO0cdplhkg==", "project_close_reason"=>{"comment"=>"b"}, "commit"=>"Cancel Project", "user_id"=>"2", "id"=>"10"}
User Load (11.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 2], ["LIMIT", 1]]
Project Load (0.7ms) SELECT "projects".* FROM "projects" WHERE "projects"."user_id" = $1 AND "projects"."id" = $2 LIMIT $3 [["user_id", 2], ["id", 10], ["LIMIT", 1]]
ProjectCloseReason Load (0.2ms) SELECT "project_close_reasons".* FROM "project_close_reasons" WHERE "project_close_reasons"."project_id" = $1 LIMIT $2 [["project_id", 10], ["LIMIT", 1]]
Completed 400 Bad Request in 22ms (ActiveRecord: 12.1ms)
ActionController::ParameterMissing (param is missing or the value is empty: project):
The error is saying that there is no params with name project, which you are trying to require in params.require(:project) line in cancel params. I think this is happening beacause in form_for you have mentioned [#user, #project], that means user's project. So the project params must be inside user's. Check your log when you call cancel_save. There must be something like {user => {project => { } }.
In the end I got it to work without requiring projects at all. This is my revised cancel_params in my projects_controller. For anyone else looking at doing this kind of additive databasing, I highly recommend skipping params for nested form if you can do it this way. So much simpler!
private
def cancel_params
params.require(:project_close_reason).permit(:comment).merge(project_id: #project.id, last_status_id: #project.project_status_id )
end

Doing a json put request saves unedited field as blank

In my Rails app I have made an api for an iOS app to consume.
I have a user model with a association to a profile:
class User < ActiveRecord::Base
has_one :personal_profile
accepts_nested_attributes_for :personal_profile
end
class PersonalProfile < Profile
belongs_to :user
end
The users_controller.rb looks like this:
class Api::V1::UsersController < Api::V1::ApiController
before_filter :authenticate_api_user, only: [:show, :update]
def create
#user = User.new(user_params)
if #user.save
#user
else
render json: { errors: #user.errors.full_messages }, status: 422
end
end
def show
#user
end
def update
if #user.update(user_params)
#user
else
render json: { errors: #user.errors.full_messages }, status: 422
end
end
private
def user_params
params.require(:user).permit(:name, :email, :birthday, :gender, :password, :password_confirmation, personal_profile_attributes: [:website, :location, :description, :tagline, :tag_tokens, :image, :image_cache])
end
def authenticate_api_user
authenticate_or_request_with_http_token do |token, options|
#user = User.find_by(auth_token: token)
end
end
end
When I do a put request like this:
curl -H 'Authorization: Token token=AUTH_TOKEN' -H 'Content-Type: application/json' -X PUT -d '{"user": {"name": "Frank", "personal_profile_attributes": { "tagline": "New tagline" }}}' http://localhost:3000/api/user
I get this response:
{"errors":["Personal profile title can't be blank."]}
And it's like the server doesn't recognize my params:
Started PUT "/api/user" for ::1 at 2016-01-19 10:16:33 +0100
Processing by Api::V1::UsersController#update as JSON
Parameters: {"user"=>{"name"=>"Frank", "personal_profile_attributes"=>{"tagline"=>"New tagline"}}}
User Load (1.0ms) SELECT "users".* FROM "users" WHERE "users"."auth_token" = $1 LIMIT 1 [["auth_token", AUTH_TOKEN]]
(0.8ms) BEGIN
PersonalProfile Load (1.2ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."type" IN ('PersonalProfile') AND "profiles"."user_id" = $1 ORDER BY title asc LIMIT 1 [["user_id", 1]]
SQL (1.3ms) UPDATE "profiles" SET "user_id" = $1, "updated_at" = $2 WHERE "profiles"."id" = $3 [["user_id", nil], ["updated_at", "2016-01-19 09:16:33.629907"], ["id", 1]]
User Exists (1.3ms) SELECT 1 AS one FROM "users" WHERE (LOWER("users"."email") = LOWER('d#friis.me') AND "users"."id" != 1) LIMIT 1
(0.8ms) ROLLBACK
Completed 422 Unprocessable Entity in 19ms (Views: 0.3ms | ActiveRecord: 6.5ms)
I have another controller setup for the web app, which is pretty standard and works just fine!
Any help is much appreciated!
Okay, so the problem was that I did not pass an ID for the associated record with the request params. This meant that a new associated record was created with the attributes I was trying to update.
Setting the ID solved the problem and updated the record as expected.
{
"user" => {
"personal_profile_attributes" => {
"id" => 1,
"tagline" => "New tagline"
}
}
}

best_in_place with a many to many relationship in rails 4

I have made a github repo that you can find here just for this question. I have 3 models:
class User < ActiveRecord::Base
has_many :user_countries
has_many :event_countries,
-> { where(user_countries: {:event => true}) },
:through => :user_countries,
:source => :country
has_many :research_countries,
-> { where(user_countries: {:research => true}) },
:through => :user_countries
:source => :country
end
class UserCountry < ActiveRecord::Base
belongs_to :country
belongs_to :user
end
class Country < ActiveRecord::Base
# ...
end
So a user should be able to choose event_countries and research_countries.
here's my users controller (nothing complicated):
class UsersController < ApplicationController
respond_to :html, :json
before_action :get_user, only: [:show, :edit, :update]
before_action :get_users, only: [:index]
def index
end
def show
end
def edit
end
def update
#user.update_attributes(user_params)
respond_with #user
end
private
def get_user
#user = User.find(params[:id])
end
def get_users
#users = User.all
end
def user_params
params.require(:user).permit(:first_name, :event_countries => [:id, :name])
end
end
And here's my user show page:
<%= best_in_place #user, :first_name %>
<p> event countries: </p>
<%= best_in_place #user, :event_countries, place_holder: "click here to edit", as: :select, collection: Country.all.map {|i| i.name} %>
<%= link_to "users index", users_path %>
So there's really nothing complicated here. I can also succesfully edit my users first name, best_in_place is working fine.
The question is: how do I edit the event_countries ? As you can see I tried to use the collection option with the countries but when I try to select a country I get the following:
Processing by UsersController#update as JSON
Parameters: {"user"=>{"event_countries"=>"3"}, "authenticity_token"=>"l5L5lXFmJFQ9kI/4klMmb5jDhjmtQXwn6amj1uwjSuE=", "id"=>"6"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 6]]
(0.1ms) begin transaction
(0.1ms) rollback transaction
Completed 500 Internal Server Error in 3ms
NoMethodError (undefined method `each' for nil:NilClass):
app/controllers/users_controller.rb:17:in `update'
I don't understand what it's doing, I know it must be a problem with the collection option. If you need to see any file please check my repo here:
https://github.com/spawnge/best_in_place_join_models_twice . I have a spent a lot of time on this any answer/suggestion would be greatly appreciated :)
update:
I have tried this:
<%= best_in_place #user, :event_country_ids, as: :select, collection: Country.all.map { |i| i.name }, place_holder: "click here to edit", html_attrs: { multiple: true } %>
and I have added :event_country_ids to my user params:
params.require(:user).permit(:first_name, :event_country_ids)
And now I can see all the countries but when I select one here's what I get:
Started PUT "/users/3" for 127.0.0.1 at 2014-12-18 01:19:27 +0000
Processing by UsersController#update as JSON
Parameters: {"user"=>{"event_country_ids"=>"1"}, "authenticity_token"=>"aZAFIHgzdSL2tlFcGtyuu+XIJW3HX2fwQGHcB9+iYpI=", "id"=>"3"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 3]]
(0.0ms) begin transaction
Country Load (0.0ms) SELECT "countries".* FROM "countries" WHERE "countries"."id" = ? LIMIT 1 [["id", 1]]
Country Load (0.0ms) SELECT "countries".* FROM "countries" INNER JOIN "user_countries" ON "countries"."id" = "user_countries"."country_id" WHERE "user_countries"."event" = 't' AND "user_countries"."user_id" = ? [["user_id", 3]]
SQL (0.1ms) DELETE FROM "user_countries" WHERE "user_countries"."user_id" = ? AND "user_countries"."country_id" = 2 [["user_id", 3]]
SQL (0.0ms) INSERT INTO "user_countries" ("country_id", "event", "user_id") VALUES (?, ?, ?) [["country_id", 1], ["event", "t"], ["user_id", 3]]
(20.9ms) commit transaction
Completed 204 No Content in 28ms (ActiveRecord: 21.3ms)
As you can see it seems that it insert the right content: INSERT INTO "user_countries" ("country_id", "event", "user_id") VALUES (?, ?, ?) [["country_id", 1], ["event", "t"], ["user_id", 3]] However I get the Completed 204 No Content just after that. I don't understand when I refresh the page the input is empty. Any suggestion ?
Update 2:
I checked in the console and it works, I can add event_countries to a user. However it doesn't display the user's event_countries when I refresh the page, I guess that's because I'm using the event_country_ids object.
I think the following code should work:
<%= best_in_place #user, :event_country_ids, as: :select,
collection: Country.all.each_with_object({}) { |i, memo| memo[i.id] = i.name },
place_holder: "click here to edit",
html_attrs: { multiple: true } %>
Assuming you want the user to be able to assign multiple event_countries.
Reference
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many, specifically the collection_singular_ids= method created by has_many.
https://github.com/bernat/best_in_place#select, the structure of the collection needs to be a hash. For each key => value pair, the key is what's submitted with the form and the value is what's displayed to the user.
http://ruby-doc.org/core-2.1.5/Enumerable.html#method-i-each_with_object, each_with_object is a part of the core Ruby library.

Resources