Rails multi page form with multiple models [duplicate] - ruby-on-rails

I'm getting a rollback when trying to add a user to my DB and I'm not sure why.
I have three models:
company.rb
class Company < ActiveRecord::Base
acts_as_paranoid
has_many :locations, dependent: :destroy
has_many :users, dependent: :destroy
has_many :tests, through: :locations
has_many :reports, dependent: :destroy
accepts_nested_attributes_for :locations, :users
validates_presence_of :name
end
** user.rb **
class User < ActiveRecord::Base
acts_as_paranoid
devise :database_authenticatable,
:recoverable,
:rememberable,
:trackable,
:validatable,
:registerable
belongs_to :company
has_and_belongs_to_many :roles
end
** location.rb **
class Location < ActiveRecord::Base
acts_as_paranoid
belongs_to :company
has_many :network_hosts, dependent: :destroy
has_many :tests, dependent: :destroy
has_many :commands, dependent: :destroy
validates_presence_of :company, :identifier, :name
validates_uniqueness_of :identifier
delegate :security_percentage, to: :last_test, allow_nil: true
after_initialize :generate_identifier, if: -> { self.identifier.blank? }
def generate_identifier
self.identifier = SecureRandom.uuid.delete("-")
end
So, when a user wants to signup they need to enter company, location, and user information, which is controlled by my company_controller.rb
** company_controller.rb **
class CompanyController < ApplicationController
def new
#company = Company.new
1.times { #company.locations.build }
1.times { #company.users.build }
end
def create
#company = Company.new(company_params)
if #company.save
redirect_to root_url
else
render :new
end
end
private
def company_params
params.require(:company).permit(:name, locations_attributes: [:name], users_attributes: [:first_name, :last_name, :full_name, :email, :password, :password_confirmation])
end
end
The form is using the standard form_for with nested attributes so that I can hopefully make everything in one shot when the user clicks the submit button
** company/new.html.erb **
<%= form_for #company, :url => url_for( :controller => 'company', :action => 'new' ) do |f| %>
<div class="form-group">
<%= f.label "Company Name" %>
<%= f.text_field :name, class: "form-control", placeholder: "ACME Inc." %>
<%= f.fields_for :locations do | location_builder | %>
<%= location_builder.label "Location Name" %>
<%= location_builder.text_field :name, class: "form-control", placeholder: "Main Building" %>
<% end %>
</div>
<div class="form-group">
<%= f.fields_for :users do | user_builder | %>
<%= user_builder.label "First Name" %>
<%= user_builder.text_field :first_name, class: "form-control", placeholder: "John" %>
<%= user_builder.label "Last Name" %>
<%= user_builder.text_field :last_name, class: "form-control", placeholder: "Smith" %>
<%= user_builder.label "Full Name" %>
<%= user_builder.text_field :full_name, class: "form-control", placeholder: "John Smith" %>
<%= user_builder.label "Email" %>
<%= user_builder.email_field :email, class: "form-control", placeholder: "john#acme.com" %>
<%= user_builder.label "Password" %>
<%= user_builder.password_field :password, class: "form-control" %>
<%= user_builder.label "Confirm Password" %>
<%= user_builder.password_field :password_confirmation, class: "form-control" %>
<% end %>
</div>
<%= f.submit "Submit", class: "btn btn-large btn-success" %>
<% end %>
However, I'm getting a rollback in the logs that this isn't happening and can't figure out why.
Started POST "/signup" for 127.0.0.1 at 2015-08-07 13:49:22 -0400
Processing by CompanyController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"2OHwJ9UfEbfkZHjLdm9BfOd7jlRdvoEz0L4NRJCKl64=", "company"=>{"name"=>"ACME Brick", "locations_attributes"=>{"0"=>{"name"=>"Main House"}}, "users_attributes"=>{"0"=>{"first_name"=>"Testin", "last_name"=>"User", "full_name"=>"Test User", "email"=>"test#test.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}}, "commit"=>"Submit"}
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."deleted_at" IS NULL AND "users"."id" = 7 ORDER BY "users"."id" ASC LIMIT 1
Role Load (0.3ms) SELECT "roles".* FROM "roles" INNER JOIN "roles_users" ON "roles"."id" = "roles_users"."role_id" WHERE "roles"."deleted_at" IS NULL AND "roles_users"."user_id" = $1 [["user_id", 7]]
(0.1ms) BEGIN
Location Exists (0.3ms) SELECT 1 AS one FROM "locations" WHERE "locations"."identifier" = '3b7febb35ea740488788d43fcc5e989c' LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = 'test#test.com' LIMIT 1
(0.1ms) ROLLBACK
Rendered company/new.html.erb within layouts/application (5.0ms)
Completed 200 OK in 108ms (Views: 32.2ms | ActiveRecord: 1.9ms | Solr: 0.0ms)
** Company Table **
class CreateCompanies < ActiveRecord::Migration
def change
create_table :companies do |t|
t.string :name
t.timestamps
end
end
end
** Location Table **
class CreateLocations < ActiveRecord::Migration
def change
create_table :locations do |t|
t.belongs_to :company, index: true
t.string :identifier
t.string :name
t.timestamps
end
end
end
** User Table **
class CreateUsers < ActiveRecord::Migration
def self.up
create_table(:users) do |t|
t.belongs_to :company, index: true
t.string :username
t.string :first_name
t.string :last_name
t.string :full_name
t.string :time_zone, :default => "Central Time (US & Canada)"
t.string :avatar_file_name
t.string :avatar_content_type
t.integer :avatar_file_size
t.datetime :avatar_updated_at
## Database authenticatable
t.string :email, :null => false, :default => ""
t.string :phone_number
t.string :encrypted_password, :null => false, :default => ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, :default => 0
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
## Token authenticatable
t.string :authentication_token
t.timestamps
end
add_index :users, :email, :unique => true
add_index :users, :reset_password_token, :unique => true
create_table :roles_users, :id => false do |t|
t.references :role, :user
end
end
def self.down
drop_table :users
drop_table :roles_users
end
end
** The error from logs **
Started POST "/signup" for 127.0.0.1 at 2015-08-07 18:12:54 -0400
Processing by CompanyController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"aAc83zKKlV4w1i2GhTqTo3ehtXP+tPvYbBBRq1ccYzA=", "company"=>{"name"=>"Test Co.", "locations_attributes"=>{"0"=>{"name"=>"Main"}}, "users_attributes"=>{"0"=>{"first_name"=>"John", "last_name"=>"Smith", "full_name"=>"John Smith", "email"=>"john#acme.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}}, "commit"=>"Submit"}
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."deleted_at" IS NULL AND "users"."id" = 7 ORDER BY "users"."id" ASC LIMIT 1
Role Load (0.3ms) SELECT "roles".* FROM "roles" INNER JOIN "roles_users" ON "roles"."id" = "roles_users"."role_id" WHERE "roles"."deleted_at" IS NULL AND "roles_users"."user_id" = $1 [["user_id", 7]]
(0.1ms) BEGIN
Location Exists (0.3ms) SELECT 1 AS one FROM "locations" WHERE "locations"."identifier" = '0d759e5405084663a1c110d37f04573a' LIMIT 1
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = 'john#acme.com' LIMIT 1
(0.1ms) ROLLBACK
Completed 422 Unprocessable Entity in 75ms
** [Airbrake] Notice was not sent due to configuration:
Environment Monitored? false
API key set? true
ActiveRecord::RecordInvalid (Validation failed: Locations company can't be blank):
app/controllers/company_controller.rb:10:in `create'
app/controllers/application_controller.rb:95:in `set_time_zone'
Rendered /Users/godzilla/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/actionpack-4.1.7/lib/action_dispatch/middleware/templates/rescues/_source.erb (0.9ms)
Rendered /Users/godzilla/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/actionpack-4.1.7/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (1.7ms)
Rendered /Users/godzilla/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/actionpack-4.1.7/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (1.2ms)
Rendered /Users/godzilla/.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/actionpack-4.1.7/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (15.6ms)
It references that the location identifier already exists, but it doesn't. This is something that is made on the fly when trying to create a new Location (notice the method generate_identifier in the location.rb model). On top of that, the user doesn't exist either.
Any ideas how to get around this problem?

When you write validates_presence_of :company it means that your company record has to exist at the time of location creation, but it is not fully saved yet. However, your location is still associated with the company object, and it will be saved correctly without this validation. I think you can validate on presence of company_id instead because company id becomes available during saving.

Related

Ruby on Rails Self Referential Create Action Returning Unknown Attribute Error

I am creating a self referential relationship for services. The idea is to allow you to add a potentially infinite level of sub services to a service, so services can have children, and those children can have children, etc.
To do this I have crated two classes, Services, and SubServices. SubServices is a simple join table with a parent_service_id and a child_service_id. I am able to create everything in the rails console and it works just fine. Both the child and parent associations work. It's just in the controller that it's breaking down
schema:
create_table "services", force: :cascade do |t|
t.string "name"
t.integer "business_category_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["business_category_id"], name: "index_services_on_business_category_id", using: :btree
end
create_table "sub_services", force: :cascade do |t|
t.integer "child_service_id"
t.integer "parent_service_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["child_service_id"], name: "index_sub_services_on_child_service_id", using: :btree
t.index ["parent_service_id"], name: "index_sub_services_on_parent_service_id", using: :btree
end
models:
class SubService < ApplicationRecord
belongs_to :parent_service, class_name: 'Service'
belongs_to :child_service, class_name: 'Service'
end
class Service < ApplicationRecord
belongs_to :business_category
has_many :client_services # TODO REMOVE
has_many :clients, :through => :client_services # TODO REMOVE
has_many :business_services
has_many :businesses, :through => :business_services
has_many :parental_services, foreign_key: :parent_service_id, class_name: "SubService"
has_many :child_services, through: :parental_services
has_many :children_services, foreign_key: :child_service_id, class_name: "SubService"
has_many :parent_services, through: :children_services
validates :name, presence: true
validates :name, uniqueness: { scope: :business_category, message: "service already added" }
end
SubService new action:
def new
#sub_service = #service.child_services.new
respond_to do |format|
format.html
format.js { render :new }
end
end
SubService create action:
def create
#sub_service = #service.child_services.new(service_params);
# binding.pry
if #sub_service.save
redirect_to(admin_business_categories_path)
end
end
service_params:
def service_params
params.require(:sub_service).permit(:name)
end
Sub Service new view:
<%= render 'form', f: f %>
<% end %>
_form:
<div class="col-12 col-md-10">
<div class="col-12 col-md-12">
<%= f.input :name, label: 'Service Name' %>
</div>
</div>
<div class="col-12 col-md-2">
<div class="btn-group-vertical" role="group" aria-label="...">
<button id="serviceFormCancelButton" class="btn btn-danger">CANCEL</button>
<%= f.submit 'SAVE', class: 'btn btn-success' %>
<br>
</div>
</div>
This is the error my console is returning
ActiveModel::UnknownAttributeError (unknown attribute 'parent_service_id' for Service.):
app/controllers/admin/sub_services_controller.rb:14:in `create'
Rendering C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/actionpack-5.0.7/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout
Rendering C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/actionpack-5.0.7/lib/action_dispatch/middleware/templates/rescues/_source.html.erb
Rendered C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/actionpack-5.0.7/lib/action_dispatch/middleware/templates/rescues/_source.html.erb (3.0ms)
Rendering C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/actionpack-5.0.7/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb
Rendered C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/actionpack-5.0.7/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (2.0ms)
Rendering C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/actionpack-5.0.7/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb
Rendered C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/actionpack-5.0.7/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (1.0ms)
Rendered C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/actionpack-5.0.7/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout (846.1ms)
You want a Service > SubService and then potentially Service > SubService > SubService > SubService.... right? You're getting the unkown attribute error because parent_id is on SubService. You can do what you want with just the Service model. Just put the parent_id on that.
You could then get rid of the SubService model.
class Service
belongs_to :parent_service, foreign_key: :parent_id, class_name: 'Service'
has_many :child_services, class_name: 'Service'
end

Rails rolling back twice every time I try to post Data

I am trying to create an app that allows users to make lists of items and view only the lists they themselves have created. Every time I press submit on the form this happens
Started POST "/lists" for 127.0.0.1 at 2017-08-18 15:56:40 -0400
Processing by ListsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"VnsMdQq3mw5XabkYCZFTgvgwFc3H89paHA0VE5gunFbiMfa0xGr0p1GEZDHc3yemwBx07K1h4CXuS0l5XL1VbA==", "list"=>{"income"=>"12", "put_into_savings"=>"12", "month"=>"12", "year"=>"21"}, "commit"=>"Create List"}
(0.1ms) begin transaction
(0.1ms) rollback transaction
(0.0ms) begin transaction
(0.0ms) rollback transaction
Rendering lists/new.html.erb within layouts/application
Rendered lists/new.html.erb within layouts/application (9.3ms)
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 5], ["LIMIT", 1]]
Completed 200 OK in 269ms (Views: 222.2ms | ActiveRecord: 2.7ms)
Here is all my code:
lists_controller.rb
class ListsController < ApplicationController
def show
#user = User.find(params[:id])
#lists = #user.lists
end
def new
end
def edit
end
def create
#list = List.create(list_params)
if #list.save
redirect_to home_url
else
render :new
end
end
private
def list_params
params.require(:list).permit(:income, :put_into_savings, :month, :year)
end
end
lists/new.html.erb
<%= form_for List.new do |f| %>
<div class="field">
<%= f.label :income %><br />
<%= f.text_field :income %>
</div>
<div class="field">
<%= f.label :put_into_savings %><br />
<%= f.text_area :put_into_savings %>
</div>
<div class="field">
<%= f.label :month %><br />
<%= f.number_field :month %>
</div>
<div class="field">
<%= f.label :year %><br />
<%= f.number_field :year %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
schema.rb
ActiveRecord::Schema.define(version: 20170818185700) do
create_table "items", force: :cascade do |t|
t.string "item_name"
t.integer "item_cost"
t.string "item_waste"
t.string "item_group"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "lists", force: :cascade do |t|
t.integer "income"
t.integer "put_into_savings"
t.string "month"
t.string "year"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "Item_id"
t.integer "User_id"
end
create_table "users", force: :cascade do |t|
t.string "email"
t.string "password_digest"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name"
end
end
Routes.rb
Rails.application.routes.draw do
root 'home#index'
get 'home' => 'home#index'
resources :lists
resources :sessions, only: [:new, :create, :destroy]
resources :users, only: [:new, :create]
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
Here is my list model:
class List < ApplicationRecord
has_many :items
belongs_to :user
end
How can I solve this?
You are having logic problem with your model association.
Assuming that a List could have more than one Item, you shouldn't have declared your table List with the attribute item_id. (Doing that it means a List could ONLY have one item). I recommend you read ruby-on-rais-guide-for-associations.
For the problem with the user_id, you need to explicit declared the user_id in your list object (considering that you want to associate a List with a User in the moment the List is created). One way to do it could be:
def create
#list = List.new(list_params)
#list[:user_id] = current_user.id # Considering you add this method
if #list.save
redirect_to home_url
else
render :new
end
And add some validation in model:
class List < ApplicationRecord
has_many :items
belongs_to :user
validates :user_id, presence: true
end
It seems you need to read more about validation too ruby-on-rais-guide-for-validation. About your twice rollback, it is unclear the reason, but fixing you association and validations problems, I think you can fix it.
Try read more about rails, the problem you are having are really basic. Good luck!
UPDATE:
As suggested by at0misk answer, to solve the problem with twice rollback:
In List controller:
#list = List.new(list_params)
# instead of #list = List.create(list_params)
The create method create a new object and save immediately. So, rails was trying to save twice, in the method create first, then in the method save in sequence.
In your create method, you're calling create and then calling save. Create creates an object and saves it to the database, so calling save is redundent.
Have you checked to see if your record is saving? If it is then this is definitely what's wrong. I prefer to use this pattern, using new instead of create, and then attempting to save in an if block:
def create
#list = List.new(list_params)
if #list.save
redirect_to home_url
else
render :new
end
end

Rails: Create action not saving has_many through associations - "rollback transaction"

Be easy on me, I'm just starting to learn Rails and this is my first question on here!
The project I'm using to learn is a volleyball scoreboard, so right now I'm trying to build a form that will submit the score of a 2v2 game. I have users and games which are associated by a has_many through relationship to a join table of participants which also includes a 'result' attribute ('W' or 'L').
My problem is that when I submit it fails, and no participants are created. If I removed the associations from the form, submission will work with just game parameters.
Hopefully, I've included all the relevant information below. Also, if there is a better way to do all this, I'd love to hear it!
MODELS
class Game < ApplicationRecord
has_one :venue
has_many :participants
has_many :users, through: :participants
accepts_nested_attributes_for :participants,
reject_if: :all_blank, allow_destroy: true
end
class User < ApplicationRecord
has_many :participants
has_many :games, through: :participants
end
class Participant < ApplicationRecord
belongs_to :game
belongs_to :user
end
SCHEMA
create_table "games", force: :cascade do |t|
t.date "game_date"
t.integer "winning_score"
t.integer "losing_score"
t.text "notes"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "venue_id"
t.index ["venue_id"], name: "index_games_on_venue_id"
end
create_table "participants", force: :cascade do |t|
t.integer "user_id"
t.integer "game_id"
t.string "result"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["game_id"], name: "index_participants_on_game_id"
t.index ["user_id"], name: "index_participants_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
t.index ["email"], name: "index_users_on_email", unique: true
end
create_table "venues", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
CONTROLLER
class GamesController < ApplicationController
def show
#game = Game.find(params[:id])
end
def new
#users = User.all
#game = Game.new
#game.participants.build
end
def create
#game = Game.new(game_params)
if #game.save
redirect_to 'show'
else
render 'new'
end
end
private
def game_params
params.require(:game).permit(:game_date, :winning_score,
:losing_score, :notes, :venue_id,
participants_attributes: [:user_id, :result,
:_destroy])
end
end
FORM
<%= simple_form_for #game do |f| %>
<div id="winners">
<b>Winners</b>
<% for i in 0..1 %>
<%= f.simple_fields_for :participants do |p| %>
<%= p.association :user, :collection => #users, label: false %>
<%= p.input :result, :as => :hidden, :input_html => { :value => 'W' }%>
<% end %>
<% end %>
</div>
<%= f.input :winning_score, :collection => 15..30 %>
<div id="losers">
<b>Losers</b>
<% for i in 2..3 %>
<%= f.simple_fields_for :participants do |p| %>
<%= p.association :user, :collection => #users, label: false %>
<%= p.input :result, :as => :hidden, :input_html => { :value => 'L' }%>
<% end %>
<% end %>
</div>
<%= f.input :losing_score, :collection => 0..30 %>
<%= f.input :notes %>
<%= f.submit "Submit!", class: "btn btn-primary" %>
<% end %>
RESPONSE
Processing by GamesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"p8081+wU7EqYV7PIIAOGP3N+Md4CJusFpL9qTm3CeC54fP7pTPEwtfYS5v5x+ErBWxGiB0oj1pklYGXwl/cRBw==", "game"=>{"participants_attributes"=>{"0"=>{"user_id"=>"3", "result"=>"W"}, "1"=>{"user_id"=>"2", "result"=>"W"}, "2"=>{"user_id"=>"1", "result"=>"W"}, "3"=>{"user_id"=>"6", "result"=>"W"}}, "winning_score"=>"18", "losing_score"=>"4", "notes"=>"13241234"}, "commit"=>"Submit!"}
(0.1ms) begin transaction
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 6], ["LIMIT", 1]]
(0.1ms) rollback transaction
Rendering games/new.html.erb within layouts/application
Rendered games/new.html.erb within layouts/application (69.4ms)
Rendered layouts/_shim.html.erb (0.4ms)
Rendered layouts/_header.html.erb (0.7ms)
Rendered layouts/_footer.html.erb (0.3ms)
Completed 200 OK in 199ms (Views: 144.9ms | ActiveRecord: 0.5ms)
#kkulikovskis comment worked for me. I changed:
has_many :participants
to
has_many :participants, inverse_of: :game
in the game model

undefined method `email' for #<UserInfo:0xc0ac77c>

I have this problem in rails..
It says undefined method email for #<UserInfo:0xc0ac77c>
I debug this several times i could not trace the error.
here is the sample code i have.
user_infos_controller.erb
class UserInfosController < ApplicationController
before_action :check_user_profile, only: :index
def index
#user = User.find(current_user.id)
puts #user
end
def new
#user_info = current_user.build_user_info
end
def show
#user = User.find(current_user)
end
def create
#user_info = UserInfo.create(
user_id: current_user.id,
fname: params[:user_info][:fname],
lname: params[:user_info][:lname],
bday: params[:user_info][:bday],
address: params[:user_info][:address],
picture: params[:user_info][:picture])
#if #user_info.save
#redirect_to user_infos
#else
#render new_user_info_path
#end
end
private
def profile_params
params.require(:user_info).permit(:fname, :lname, :bday, :address, :picture)
end
private
def check_user_profile
user = User.find(current_user)
if !user.user_info
redirect_to new_user_info_path
end
end
end
new.html.erb
<%= simple_form_for #user_info, html: { multipart: true } do |f| %>
<% if #user_info.errors.any? %>
<h2><%= pluralize(#user_info.errors.count, "error") %> Prevented this User from saving </h2>
<ul>
<% #user_info.errors.full_messages.each do |mg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<div class="form-group">
<%= f.input :picture, input_html: { class: "form-control"} %>
</div>
<div class="form-group">
<%= f.input :fname, input_html: { class: "form-control"} %>
</div>
<div class="form-group">
<%= f.input :lname, input_html: { class: "form-control"} %>
</div>
<div class="form-group">
<%= f.input :address, input_html: { class: "form-control"} %>
</div>
<div class="form-group">
<%= f.date_field :bday, input_html: { class: "form-control"} %>
</div>
<div class="form-group">
<button class="btn btn-primary pull-right" type="submit">Save</button>
</div>
<% end %>
This is for the user database
class DeviseCreateUsers < ActiveRecord::Migration
def change
create_table(:users) do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, default: 0, null: false
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
end
And this is for user_infos database
class CreateUserInfos < ActiveRecord::Migration
def change
create_table :user_infos do |t|
t.string :fname
t.string :lname
t.date :bday
t.string :address
t.timestamps null: false
end
end
end
class AddAttachmentPictureToUserInfos < ActiveRecord::Migration
def self.up
change_table :user_infos do |t|
t.attachment :picture
end
end
def self.down
remove_attachment :user_infos, :picture
end
end
rails console
Started POST "/user_infos" for 127.0.0.1 at 2015-06-16 13:44:14 +0800
Processing by UserInfosController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"ig6pSrP9EV7ivQ3DRG/XPcwSQmr8oRhX+4YUtuxxqn/71ViwodxX06IMaQrzEQOWvOEjohAB1suFhubz0+cAJw==", "user_info"=>{"fname"=>"das", "lname"=>"dasa", "address"=>"dsasd", "bday"=>"2015-06-16"}}
User Load (1.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 3]]
(0.2ms) BEGIN
(2.8ms) ROLLBACK
Completed 500 Internal Server Error in 148ms (ActiveRecord: 14.0ms)
NoMethodError (undefined method `email' for #<UserInfo:0xbcaa624>):
app/controllers/user_infos_controller.rb:19:in `create'
Rendered /home/allanprog/.rvm/gems/ruby-2.2.1/gems/actionpack-4.2.1/lib/action_dispatch/middleware/templates/rescues/_source.erb (20.2ms)
Rendered /home/allanprog/.rvm/gems/ruby-2.2.1/gems/actionpack-4.2.1/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (11.9ms)
Rendered /home/allanprog/.rvm/gems/ruby-2.2.1/gems/actionpack-4.2.1/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (3.8ms)
Rendered /home/allanprog/.rvm/gems/ruby-2.2.1/gems/actionpack-4.2.1/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout (108.5ms)
Cannot render console with content type multipart/form-dataAllowed content types: [#<Mime::Type:0xa39d5f0 #synonyms=["application/xhtml+xml"], #symbol=:html, #string="text/html">, #<Mime::Type:0xa39d474 #synonyms=[], #symbol=:text, #string="text/plain">, #<Mime::Type:0xa38b65c #synonyms=[], #symbol=:url_encoded_form, #string="application/x-www-form-urlencoded">]
user model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :pins
has_one :user_info
end
user_info model
class UserInfo < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :user
has_attached_file :picture, styles: { medium: "300x300>" }
validates_attachment_content_type :picture, :content_type => /\Aimage\/.*\Z/
end
Devise method in your models accepts some options to configure its modules.
So if you use devise on User model then you have to remove Devise methods from UserInfo model which is below
Remove this from UserInfo
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
For configuring Devise for multiple models have look at following documentation link
https://github.com/plataformatec/devise
Hopefully this will help.
I don't see any reason for getting NoMethodError (undefined method 'email' for #<UserInfo:0xbcaa624>) error, but 1 issue I can see is :
In create method of UserInfosController you have written :
#user_info = UserInfo.create(
user_id: current_user.id,
fname: params[:user_info][:fname],
lname: params[:user_info][:lname],
bday: params[:user_info][:bday],
address: params[:user_info][:address],
picture: params[:user_info][:picture])
but in user_infos table you haven't added the column for user_id. You should add the column user_id to user_infos table through migration like this :
class AddUserIdToUserInfos < ActiveRecord::Migration
def self.up
change_table :user_infos do |t|
t.references :user, foreign_key: true, index:true
end
end
def self.down
t.remove_references(:user)
end
end

Rails 4: Update_attributes does not work but update_column does

Currently I have an edit form as follows:
<li>
<%= form_for #ingredient do |f| %>
<span class="span2"><%= f.text_field :name, placeholder: "#{#ingredient.name}" %></span>
<span class="span1"><%= f.text_field :quantity, placeholder: "#{#ingredient.quantity}" %></span>
<span class="span1"><%= f.text_field :unit, placeholder: "#{#ingredient.unit}" %></span>
<span class="span3">Added: <%= #ingredient.updated_at.strftime("%d %b. at %H:%M") %></span>
<span class="span2"><%= f.text_field :expiration, placeholder: "#{#ingredient.expiration}" %></span>
<span class="span2"><%= f.submit "Submit", class: "btn btn-small" %></span>
<% end %>
</li>
When I click submit my log file shows the follow:
Started PATCH "/pantries/112" for 127.0.0.1 at 2014-04-29 18:03:35 -0400
Processing by PantriesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"NUSmadjWCVVLHOZmncKD5D48L+7ZMa3DEbZ9Y+Y+Pnc=", "pantry"=>{"name"=>"test1", "quantity"=>"1", "unit"=>"cup", "expiration"=>"2015-05-05"}, "commit"=>"Submit", "id"=>"112"}
[1m[35mUser Load (0.5ms)[0m SELECT "users".* FROM "users" WHERE "users"."remember_token" = '27ecdc04fc67375fd3567c89fbe831e4d4919d09' LIMIT 1
[1m[36mPantry Load (0.3ms)[0m [1mSELECT "pantries".* FROM "pantries" WHERE "pantries"."id" = $1 LIMIT 1[0m [["id", "112"]]
[1m[35m (0.2ms)[0m BEGIN
[1m[36m (0.1ms)[0m [1mCOMMIT[0m
Redirected to http://localhost:3000/users/1/pantry
Completed 302 Found in 6ms (ActiveRecord: 1.1ms)
It doesn't raise an error it just does not update at all, but says that the update completed successfully.
pantry.rb
class Pantry < ActiveRecord::Base
before_save { self.name = name.downcase }
belongs_to :user
validates :name, presence: true
validates :user_id, presence: true
end
pantries_controller
def update
#ingredient = Pantry.find(params[:id])
if #ingredient.update_attributes(params[ingredient_params])
redirect_to pantry_user_path(current_user), :flash => {info: "Ingredient Updated"}
else
redirect_to pantry_user_path(current_user), :flash => {info: "Failed"}
end
end
private
def ingredient_params
params.require(:pantry).permit(:name, :quantity, :unit, :expiration, :created_at, :updated_at)
end
schema:
create_table "pantries", force: true do |t|
t.string "name"
t.string "quantity"
t.string "unit"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
t.date "expiration"
end
add_index "pantries", ["expiration"], name: "index_pantries_on_expiration", using: :btree
add_index "pantries", ["quantity"], name: "index_pantries_on_quantity", using: :btree
add_index "pantries", ["unit"], name: "index_pantries_on_unit", using: :btree
If I replace #ingredients.update_attributes with #ingredient.update_column(:expiration, params[:pantry][:expiration]) the update takes place on that column. Falling back to update_column is not ideal. I understand update_attributes and update_attributes! call callbacks while update_column does not. I don't see any issue with the callbacks and no error messages are given. Anyone have an idea on what the issue might be?
Change your update action as below:
def update
#ingredient = Pantry.find(params[:id])
if #ingredient.update_attributes(ingredient_params) ## Call ingredient_params
redirect_to pantry_user_path(current_user), :flash => {info: "Ingredient Updated"}
else
redirect_to pantry_user_path(current_user), :flash => {info: "Failed"}
end
end
With Rails 4 strong parameters concept, you need to whitelist the attributes that you would like to be saved in database.
Currently, you are using params[ingredient_params] instead of calling ingredient_params which is causing this issue

Resources