New to rails and can't figure out how to do this. I have implemented a user log in system using devise and I am trying to let a user create multiple "listings". Sort of like a craigslist type site. I can populate the database from the rails console but I cant figure out how to put it on the site.
I have the following models:
listing.rb
class Listing < ActiveRecord::Base
belongs_to :user
default_scope -> { order('created_at DESC') }
#add validations
validates :user_id, presence: true
end
user.rb (used devise)
class User < ActiveRecord::Base
has_many :listings, dependent: :destroy
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
I am trying to create a page which allows a user to create a new listing. I am not exactly sure how to go about doing this. This is what I have currently:
listings_controller.rb
class ListingsController < ApplicationController
def index
#users = User.all
end
def show
#listing = Listing.find(params[:id])
end
def new
#listing = Listing.new
end
def create
#listing = Listing.new(listing_params)
if #listing.save
flash[:success] = "Success"
redirect_to #listing
else
render 'new'
end
end
private
def listing_params
params.require(:listing).permit(:id, :user_id, :title, :general_info)
end
end
models/views/listings/new.html.erb
<h1> POST A NEW LISTING </h>
<%= form_for #listing do |f| %>
Title: <%= f.text_field :title %> <br />
General Info: <%= f.text_field :general_info %> <br />
<%= f.submit %>
<% end %>
Ive been working on this for quite a while with no luck getting the database to populate. Currently the form once submits hits the "else" in def create and just renders the same page.
Here is the log output when I run this:
Started POST "/listings" for 127.0.0.1 at 2013-07-04 17:37:53 -0600
Processing by ListingsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"mlyDb24OMQkniOCFQ1JTvzxjplHk7kMgzEBEFBH8hGw=", "listing"=>{"title"=>"title should go here", "general_info"=>"hope this works"}, "commit"=>"Create Listing"}
[1m[35m (0.1ms)[0m begin transaction
[1m[36m (0.1ms)[0m [1mrollback transaction[0m
[1m[35mUser Load (0.3ms)[0m SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
Rendered listings/new.html.erb within layouts/application (4.4ms)
Completed 200 OK in 17ms (Views: 10.4ms | ActiveRecord: 0.5ms)
The following got this to work for anyone who comes across this problem:
In routes.db I put the listings in a block:
resources :users do
resource :listings
end
For the new/show/create methods I made sure to search for the user first (note since I am using devise the current_user.id)
def show
#listing = Listing.find(current_user.id)
end
def new
#user = User.find(current_user.id)
#listing = #user.listings.build
end
def create
#user = User.find(current_user.id)
#listing = #user.listings.build(listing_params)
if #listing.save
flash[:success] = "Success"
redirect_to root_path
else
render :action => 'new'
end
end
then finally, changed the form_for in new.html.erb to this:
<%= form_for [#user, #listing] do |f| %>
<%= f.label :title, 'Title' %> <br />
<%= f.text_field :title %>
...
<%= f.submit "submit" %>
<% end %>
Related
Could you help me? I got an error Couldn't find User without an ID, I was thinking about make like a blog service, I wanted to implement nest attribute without accepts_nested_attributes_for, so I've been using
form object, but I couldn't send form object user's parameter,
controller
class BlogsController < ApplicationController
before_action :authenticate_user!
def index
#current = current_user
end
def new
#blogs = BlogForm.new
end
def create
#blogs = BlogForm.new(blog_params)
if #blogs.save
redirect_to user_blogs_path
else
end
end
def edit
end
def show
end
private
def blog_params
params.require(:blog_form).permit(:title , :content , :user_id)
end
end
form html
<%= form_with model: #blogs , url: user_blogs_path,local: true do |f| %>
<% f.label :title %>
<%= f.text_field :title %>
<% f.label :content %>
<%= f.text_area :content %>
<% f.label :user_id %>
<% f.hidden_field :user_id , value: current_user.id%>
<%= f.submit "create", class: "btn btn-primary" %>
<% end %>
blog_form
class BlogForm
include ActiveModel::Model
attr_accessor :title, :content, :user_id
def to_model
#user = User.find(user_id)
#blogs = #user.blogs.new(title: title , content: content , user_id: user_id)
end
def save
return false if invalid
to_model.save
end
end
blogs.rb
class Blog < ApplicationRecord
belongs_to :user
validates :title ,presence: true
validates :content ,presence: true
end
user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :blogs
def email_required?
false
end
def email_changed?
false
end
def will_save_change_to_email?
false
end
end
log
ActionView::Template::Error (Couldn't find User without an ID):
1: <%= form_with model: #blogs , url: user_blogs_path,local: true do |f| %>
2:
3: <% f.label :title %>
4: <%= f.text_field :title %>
app/forms/blog_form.rb:6:in `to_model'
app/views/blogs/shared/_form.html.erb:1
app/views/blogs/new.html.erb:4
Started GET "/users/1/blogs/new" for 127.0.0.1 at 2020-01-19 16:29:03 +0900
Processing by BlogsController#new as HTML
Parameters: {"user_id"=>"1"}
Rendering blogs/new.html.erb within layouts/application
Rendered blogs/shared/_form.html.erb (Duration: 3.0ms | Allocations: 1143)
Rendered blogs/new.html.erb within layouts/application (Duration: 10.5ms | Allocations: 1228)
Completed 500 Internal Server Error in 16ms (ActiveRecord: 0.0ms | Allocations: 1715)
ActionView::Template::Error (Couldn't find User without an ID):
1: <%= form_with model: #blogs , url: user_blogs_path,local: true do |f| %>
2:
3: <% f.label :title %>
4: <%= f.text_field :title %>
app/forms/blog_form.rb:6:in `to_model'
app/views/blogs/shared/_form.html.erb:1
app/views/blogs/new.html.erb:4
after, I tried coreyward's way, but I couldn't,
rocessing by BlogsController#new as HTML
Parameters: {"user_id"=>"1"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
Rendering blogs/new.html.erb within layouts/application
Rendered blogs/shared/_form.html.erb (Duration: 6.9ms | Allocations: 1082)
Rendered blogs/new.html.erb within layouts/application (Duration: 9.4ms | Allocations: 1166)
Completed 500 Internal Server Error in 114ms (ActiveRecord: 2.3ms | Allocations: 11134)
ActionView::Template::Error (Couldn't find User without an ID):
1: <%= form_with model: #blogs , url: user_blogs_path(#user),local: true do |f| %>
2:
3: <% f.label :title %>
4: <%= f.text_field :title %>
app/forms/blog_form.rb:6:in `to_model'
app/views/blogs/shared/_form.html.erb:1
app/views/blogs/new.html.erb:4
The route helper user_blogs_path probably expects an argument for the user. Something like this:
user_blogs_path(#user)
Which goes in this line:
<%= form_with model: #blogs , url: user_blogs_path(#user),local: true do |f| %>
This is just a really strange and awkward way of doing a nested resource. This really has very little to do with nested attributes which used when you need to create or update two (or more) associated records in the same request.
# routes.rb
resources :users do
resources :blogs,
only: [:new, :show, :create],
shallow: true
end
class BlogsController
before_action :set_user, only: [:new, :create]
# GET /blogs/1
def show
#blog = Blog.find(params[:id])
end
# GET /users/1/blogs/new
def new
#blogs = #user.blog.new
end
# POST /users/1/blogs
def create
#blogs = #user.blog.new(blog_params)
if #blog.save
redirect_to #blog
else
render :new
end
end
private
def set_user
#user = User.find(params[:user_id])
end
def blog_params
params.require(:blog).permit(:title, :content)
end
end
<%= form_with model: [#user, #blog], local: true do |f| %>
<% f.label :title %>
<%= f.text_field :title %>
<% f.label :content %>
<%= f.text_area :content %>
<%= f.submit "create", class: "btn btn-primary" %>
<% end %>
I am using Rails 5 and everything at its newest stable versions. So I get the following :
You have your association set to required but it's missing.
Associations are set to required by default in rails 5 so if you want
to keep one empty you need to set optional:true on your association in
mode
This is great and I understand what is going on however for the life of me I cannot figure out how to get the parent model to save first so the user_id is translated the nested models record. I see the same answer above everywhere however no one explains a work around other than turning the default in the initializer from true to false. THIS DOES NOT SOLVE THE PROBLEM, because the record sure does save but it does not include the user_id.
Below is what I have for my code base, I would ask rather than responding with the above quote, could someone enlighten me on HOW to get the USER_ID field into the nested attributes while saving. I refuse to disable validation and manually handle the insertion, as this is not the ruby way and breaks from standards!
Thanks in advance for anyone who can answer this question directly and without vague explanations that digress from the ruby way of things!
###Models
#Users
class User < ApplicationRecord
has_one :profile, inverse_of: :user
accepts_nested_attributes_for :profile, allow_destroy: true
end
#Profiles
class Profile < ApplicationRecord
belongs_to :user, inverse_of: :profile
end
###Controller
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /users
# GET /users.json
def index
#users = User.all
end
# GET /users/1
# GET /users/1.json
def show
end
# GET /users/new
def new
#user = User.new
#user.build_profile
end
# GET /users/1/edit
def edit
#user.build_profile
end
# POST /users
# POST /users.json
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# DELETE /users/1
# DELETE /users/1.json
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:username, :password, :user_type_id, profile_attributes: [:user_id, :first_name, :middle_name, :last_name, :phone_number, :cell_number, :email])
end
end
##View
<%= form_for(#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 %>
<!--<li><%= debug f %></li>-->
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :username %>
<%= f.text_field :username %>
</div>
<div class="field">
<%= f.label :password %>
<%= f.text_field :password %>
</div>
<div class="field">
<% if params[:trainer] == "true" %>
<%= f.label :user_type_id %>
<%= f.text_field :user_type_id, :readonly => true, :value => '2' %>
<% else %>
<%= f.label :user_type_id %>
<%= f.text_field :user_type_id, :readonly => true, :value => '1' %>
<% end %>
</div>
<h2>Account Profile</h2>
<%= f.fields_for :profile do |profile| %>
<%#= profile.inspect %>
<div>
<%= profile.label :first_name %>
<%= profile.text_field :first_name %>
</div>
<div>
<%= profile.label :middle_name %>
<%= profile.text_field :middle_name %>
</div>
<div>
<%= profile.label :last_name %>
<%= profile.text_field :last_name %>
</div>
<div>
<%= profile.label :email %>
<%= profile.text_field :email %>
</div>
<div>
<%= profile.label :phone_number %>
<%= profile.telephone_field :phone_number %>
</div>
<div>
<%= profile.label :cell_phone %>
<%= profile.telephone_field :cell_number %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<%= debug params %>
<%= debug user %>
<%= debug user.profile %>
<% end %>
UPDATE
For starters I have figured out that you need to include autosave: true to the relationship like so
class User < ApplicationRecord
has_one :profile, inverse_of: :user, autosave: true
accepts_nested_attributes_for :profile, allow_destroy: true
end
Then the parent record gets saved before the child. Now comes another gotcha that I am just not sure about and is odd when the form is submitted you will notice in the console output I pasted below that the INSERT INTO profiles statement includes the user_id column and the value of 1. It passees validation and looks like it runs properly from the output, however the user_id column in the profiles table is still null. I am going to keep digging, hopefuly one of my fellow rubyiests out there will see this and have some ideas on how to finish fixing this. I love Rails 5 improvements so far but it wouldn't be ROR without small interesting gotchas! Thanks again in advance!
Started POST "/users" for 192.168.0.31 at 2017-03-12 22:28:14 -0400
Cannot render console from 192.168.0.31! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by UsersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"YA7kQnScvlIBy5OiT+BmOQ2bR7J00ANXId38FqNwX37Cejd+6faUyD3rMF4y0qJNKBUYGaxrRZqcLrXonL6ymA==", "user"=>{"username"=>"john", "password"=>"[FILTERED]", "user_type_id"=>"1", "profile_attributes"=>{"first_name"=>"john", "middle_name"=>"r", "last_name"=>"tung", "email"=>"thegugaru#gmail.com", "phone_number"=>"8033207677", "cell_number"=>"8033207677"}}, "commit"=>"Create User"}
(0.1ms) BEGIN
SQL (0.3ms) INSERT INTO `users` (`username`, `password`, `user_type_id`, `created_at`, `updated_at`) VALUES ('john', '0000', 1, '2017-03-13 02:28:14', '2017-03-13 02:28:14')
SQL (0.4ms) INSERT INTO `profiles` (`user_id`, `email`, `first_name`, `middle_name`, `last_name`, `phone_number`, `cell_number`, `created_at`, `updated_at`) VALUES (1, 'thegu#gmail.com', 'john', 'r', 'tung', '8033207677', '8033207677', '2017-03-13 02:28:14', '2017-03-13 02:28:14')
(10.8ms) COMMIT
Redirected to http://192.168.0.51:3000/users/1
Completed 302 Found in 24ms (ActiveRecord: 11.5ms)
Ok, I am answering my own question because I know many people are struggling with this and I actually have the answer and not a vague response to the documentation.
First we will just be using a one to one relationship for this example. When you create your relationships you need to make sure that the parent model has the following
inverse_of:
autosave: true
accepts_nested_attributes_for :model, allow_destroy:true
Here is the Users model then I will explain,
class User < ApplicationRecord
has_one :profile, inverse_of: :user, autosave: true
accepts_nested_attributes_for :profile, allow_destroy: true
end
in Rails 5 you need inverse_of: because this tells Rails that there is a relationship through foreign key and that it needs to be set on the nested model when saving your form data.
Now if you were to leave autosave: true off from the relationship line you are left with the user_id not saving to the profiles table and just the other columns, unless you have validations off and then it won't error out it will just save it without the user_id.
What is going on here is autosave: true is making sure that the user record is saved first so that it has the user_id to store in the nested attributes for the profile model.
That is it in a nutshell why the user_id was not traversing to the child and it was rolling back rather than committing.
Also one last gotcha is there are some posts out there telling you in your controller for the edit route you should add #user.build_profile like I have in my post. DO NOT DO IT THEY ARE DEAD WRONG, after assessing the console output it results in
Started GET "/users/1/edit" for 192.168.0.31 at 2017-03-12 22:38:17 -0400
Cannot render console from 192.168.0.31! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by UsersController#edit as HTML
Parameters: {"id"=>"1"}
User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
Profile Load (0.5ms) SELECT `profiles`.* FROM `profiles` WHERE `profiles`.`user_id` = 1 LIMIT 1
(0.1ms) BEGIN
SQL (0.5ms) UPDATE `profiles` SET `user_id` = NULL, `updated_at` = '2017-03-13 02:38:17' WHERE `profiles`.`id` = 1
(59.5ms) COMMIT
Rendering users/edit.html.erb within layouts/application
Rendered users/_form.html.erb (44.8ms)
Rendered users/edit.html.erb within layouts/application (50.2ms)
Completed 200 OK in 174ms (Views: 98.6ms | ActiveRecord: 61.1ms)
If you look it is rebuilding the profile from scratch and resetting the user_id to null for the record that matches the current user you are editing.
So be very careful of this as I have seen tons of posts making this suggestion and it cost me DAYS of research to find a solution!
I'm fairly new to Rails, and having an issue understanding where I'm missing something.
I'm using Rails 4, Devise and I have a main User model that holds just password, email, and user_name that are common fields to my additional models: Artist, Fan.
I have the relationships set up as so:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :account, polymorphic: true
validates_uniqueness_of :user_name
end
class Artist < ActiveRecord::Base
has_one :user, as: :account, dependent: :destroy
has_many :tracks, dependent: :destroy
accepts_nested_attributes_for :user
end
class Fan < ActiveRecord::Base
has_one :user, as: :account, dependent: :destroy
accepts_nested_attributes_for :user
end
And there is also one model that belongs_to Artist:
class Track < ActiveRecord::Base
belongs_to :artist
end
I have managed to get the polymorphic connections working, creating an Artist and it's user on creation. Now I am trying to create the Artist/Track connection.Currently I'm a getting it to
create the new Song, but it's not making the association on create. I'm getting a Track with no associated Artist_id = nil
My Song Controller looks something like this:
class TracksController < ApplicationController
before_action :authenticate_user!, :except => [:index, :show]
before_action :set_track, only: [:show, :edit, :update, :destroy]
def new
#track = Track.new
#track.build_artist
end
def create
#track = Track.new(track_params)
if #track.save
flash[:notice] = "Track was successfully created."
redirect_to #artist
else
redirect_to new_artist_track_path
end
end
Also... after successful create redirect_to #artist gives me an ActionControllerError of
"Cannot redirect to nil!
Any help would be appreciated
Edit: solved the redirect_to issue with changing redirect_to #artist to
redirect_to artist_path(current_user)
Still can't figure out how to save the association with Artist in Track on create
Edit #2: Here is my track_params
def track_params
params.require(:track).permit(:track_title, :description, :track_type)
end
Edit #3:
Here's the form:
<%= bootstrap_form_for #track, :html => { :multipart => true } do |f| %>
<div class="field">
<%= f.label :track_title %><br>
<%= f.text_field :track_title %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<div class="field">
<%= f.label :track_type %><br>
<%= f.text_field :track_type %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And the following is the log after create:
Started POST "/tracks" for 127.0.0.1 at 2014-07-10 00:03:08 -0600
Processing by TracksController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"TzzOJrRmCzAcqdJcm+X5vSfKoHiL36T/yqUsdrpP+Ew=",
"track"=>{"track_title"=>"Track", "description"=>"Test track ", "track_type"=>"Demo"}, "commit"=>"Create Track"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1 (0.0ms) begin transaction SQL (261.5ms) INSERT INTO "tracks" ("created_at", "description", "track_title", "track_type", "updated_at") VALUES (?, ?, ?, ?, ?) [["created_at", "2014-07-10 06:03:08.377238"], ["description", "Test track "], ["track_title", "Track"], ["track_type", "Demo"], ["updated_at", "2014-07-10 06:03:08.377238"]] (45.2ms) commit transaction
Redirected to http://localhost:3000/artists/1
Completed 302 Found in 312ms (ActiveRecord: 306.9ms)
The #artist member variable is only available on each request, so the instance that you created in the new request is not available in the create request, well technically its nil. And you don't redirect to the record, but to a route.
Assuming you have the parameters set up correctly, and the association is being made in create, you could redirect to artist_path(#track.artist) but would need to see your routes.rb file to be sure
You can set the artist to the current user in the controller after the create
def create
#track = Track.new(track_params)
if #track.save
self.update_attributes(artist: current_user)
flash[:notice] = "Track was successfully created."
redirect_to artist_path(#track.artist)
else
redirect_to new_artist_track_path
end
end
I am trying to create a new record for 'campaign' via the 'customer' as follows. The form submits correctly, however no new campaign record is created for the customer. Any advise would be greatly appreciated.
Models
class Customer::Customer < User
has_one :library, dependent: :destroy
has_many :campaigns, dependent: :destroy
accepts_nested_attributes_for :campaigns, :library, allow_destroy: true
end
class Customer::Campaign < ActiveRecord::Base
belongs_to :customer
end
Customer Controller - Update Method only shown
class Customer::CustomersController < ApplicationController
layout "customer_layout"
def update
session[:return_to] ||= request.referer
#customer = User.find(params[:id])
if #customer.update_attributes(customer_params)
flash[:success] = "Profile updated"
redirect_to #customer
else
flash[:notice] = "Invalid"
redirect_to session.delete(:return_to)
end
end
def customer_params
params.require('customer_customer').permit(:name, :email, :password, :password_confirmation, :country_code, :state_code, :postcode, :first_name, :surname, campaigns_attributes:[:name, :description, :start_date, :complete_date ])
end
end
And the form I am using
<%= form_for #customer do |f| %>
<%= render 'shared/customer_error_messages' %>
<%= f.fields_for :campaign do |builder| %>
<%= builder.label :name, "Campaign Name" %></br>
<%= builder.text_field :name %>
<% end %>
<div class="actions">
<%= f.submit class: "btn btn-large btn-primary"%>
</div>
<% end %>
And the console output (with error, is as follows)
Started PATCH "/customer/customers/38" for 127.0.0.1 at 2014-05-26 23:13:10 +1000
Processing by Customer::CustomersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"9ChLYna0/VfZSMJOa2yGgvWTT61XkjoYwlVup/Pbbeg=", "customer_customer"=>{"campaign"=>{"name"=>"My new campaign"}}, "commit"=>"Update Customer", "id"=>"38"}
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = '0e8b4ba6dc957e76948fc22bae57673404deb9fe' LIMIT 1
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", "38"]]
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", "38"]]
Unpermitted parameters: campaign
If this form performs adding only one campaign i guess you should handly add this exect campaign to customer. If you update all campaign model in such way, you should have all your campaigns in array. So it implies another controller action for this.
Another way: to check if params contains field campain and then just add it to customer object:
def update
session[:return_to] ||= request.referer
#customer = User.find(params[:id])
if new_campaign_customer_params.has_key? :campaign
#customer.campaigns << Campaign.new(new_campaign_customer_params)
if #customer.save
flash[:success] = "Profile updated"
redirect_to #customer
else
flash[:notice] = "Invalid"
# you should rerender your view here, set it path
render 'your_view'
end
else
if #customer.update_attributes(customer_params)
flash[:success] = "Profile updated"
redirect_to #customer
else
flash[:notice] = "Invalid"
redirect_to session.delete(:return_to)
end
end
end
def new_campaign_customer_params
params.require('customer_customer').permit(campaign_attributes:[:name, :description, :start_date, :complete_date ])
end
Check this out, not sure it works.
Or maybe it would work how to suggests in comment: change campaigns_attributes => campaign_attributes in customer_params method, and f.fields_for :campaigns in form (belongs to #Nikita Chernov)
I am having troubles saving data that is related to a joined table. I am using Rails 4.01 with Devise 3.23 and rolify. I'm fairly new to Rails so this may be a noob question although I've searched everything on SO I can.
Basically when I try to change a role, it doesn't save the data in the joined table, but it doesn't seem to be throwing any errors. I can't find anything in the logs either.
I have the exact same issue with a second joined table. The common denominator is they both join to my users table and I'm using the update action on my User Controller.
I've spent the last two hours trying to find a solution. I thought it might be a strong parameters issue but I can't find the problem. I have tried downgrading to Rails 4.0.0 just in case but no good. I have another app with essentially the same code and no problems.
Here's my User Controller:
class UsersController < ApplicationController
before_filter :authenticate_user!
before_action :set_user, only: [:show, :edit, :update, :destroy]
def index
authorize! :index, #user, :message => 'Not authorized as an administrator.'
#users = User.all
end
def show
end
def update
authorize! :update, #user, :message => 'Not authorized as an administrator.'
if #user.update_attributes(user_params[:user])
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
authorize! :destroy, #user, :message => 'Not authorized as an administrator.'
user = User.find(params[:id])
unless user == current_user
user.destroy
redirect_to users_path, :notice => "User deleted."
else
redirect_to users_path, :notice => "Can't delete yourself."
end
end
# new function to set the password without knowing the current password used in our confirmation controller.
def attempt_set_password(params)
p = {}
p[:password] = params[:password]
p[:password_confirmation] = params[:password_confirmation]
update_attributes(p)
end
# new function to return whether a password has been set
def has_no_password?
self.encrypted_password.blank?
end
# new function to provide access to protected method unless_confirmed
def only_if_unconfirmed
unless_confirmed {yield}
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit( :name, :role_ids, :qualification_id, :role, :qualification )
end
end
My User Model:
class User < ActiveRecord::Base
belongs_to :qualification
rolify
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
validates :phone, presence: true, format: { with: /1.\d{3}.\d{3}.\d{4}/, message: "Must be in 1.555.555.5555 format" }
validates :name, presence: true
validates :email, presence: true
validates :acknowledgement, presence: true
# new function to set the password without knowing the current password used in our confirmation controller.
def attempt_set_password(params)
p = {}
p[:password] = params[:password]
p[:password_confirmation] = params[:password_confirmation]
update_attributes(p)
end
# new function to return whether a password has been set
def has_no_password?
self.encrypted_password.blank?
end
# new function to provide access to protected method unless_confirmed
def only_if_unconfirmed
pending_any_confirmation {yield}
end
def password_required?
# Password is required if it is being set, but not for new records
if !persisted?
false
else
!password.nil? || !password_confirmation.nil?
end
end
end
My Qualification Model:
class Qualification < ActiveRecord::Base
has_many :users
end
My Role Model:
class Role < ActiveRecord::Base
has_and_belongs_to_many :users, :join_table => :users_roles
belongs_to :resource, :polymorphic => true
scopify
end
Here is my index view to change the role:
<h2 class='subheader'>People</h2>
<% #users.each do |user| %>
<div class='row collapse'>
<div class='large-2 columns'><%= link_to user.name, user_path(user) %></div>
<div class='large-2 columns'><%= user.email %></div>
<div class='large-2 columns'><%= user.phone %></div>
<div class='large-2 columns'><%= user.qualification.name unless user.qualification.nil?%></div>
<div class='large-2 columns'><%= user.roles.first.name.titleize unless user.roles.first.nil? %></div>
<div class='large-2 columns'>
<% if user != current_user %>
<a data-reveal-id="role-options-<%= user.id %>" href="#" class="button tiny radius" type="button">Change role</a>
<% end %>
</div>
<div class='large-2 columns'>
<%= link_to("Delete Person", user_path(user), :data => { :confirm => "Are you sure?" }, method: :delete, class: 'button radius tiny') unless user == current_user %></div>
<hr>
</div>
<div id="role-options-<%= user.id %>" class="reveal-modal tiny" style="display: none;">
<%= simple_form_for user, url: user_path(user), html: {:method => :put, class: 'custom' } do |f| %>
<div>
<a class="close-reveal-modal">×</a>
<h3>Change Role</h3>
</div>
<div>
<%= f.input :role_ids, collection: Role.all, as: :radio_buttons, label_method: lambda {|t| t.name.titleize}, label: false, item_wrapper_class: 'inline', checked: user.role_ids.first %>
</div>
<div>
<%= f.submit "Change Role", :class => "button tiny radius" %>
</div>
<% end %>
</div>
<% end %>
Here's the relevant excerpt from my log:
Started PUT "/users/47" for 127.0.0.1 at 2013-11-20 22:27:59 -0700
Processing by UsersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"3kfL/K47ynC4LGF9kh/cAfAadGu8OXhH9kXhxeGPsvo=", "user"=> {"role_ids"=>"2"}, "commit"=>"Change Role", "id"=>"47"}
[1m[35mUser Load (0.2ms)[0m SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
[1m[36mUser Load (0.1ms)[0m [1mSELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1[0m [["id", "47"]]
[1m[35m (0.1ms)[0m SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = ? AND (((roles.name = 'admin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL))) [["user_id", 1]]
[1m[36m (0.1ms)[0m [1mbegin transaction[0m
[1m[35m (0.1ms)[0m commit transaction
Redirected to http://localhost:3000/users
Completed 302 Found in 8ms (ActiveRecord: 0.7ms)
I'm pulling my hair out on this one so any help will be greatly appreciated.
I'm not super familiar with rolify but it doesn't look like your User model does anything with the role_ids attribute in the params...
Assuming it doesn't, you'd want to iterate over each role_id (if they exist) within your controller:
def update
authorize! :update, #user, :message => 'Not authorized as an administrator.'
#user.update_roles(user_params[:role_ids]) if user_params[:role_ids].present?
if #user.update_attributes(user_params[:user])
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
Then on your User model, you'd have:
def update_roles(role_ids)
role_names = Role.find_all_by_id(role_ids).map(&:name)
role_names.each {|role_name| self.add_role role_name.to_sym }
end
Again, I haven't tested this and I'm not overly familiar with rolify so someone else may have a better suggestion, but this should at least get you closer.