问题
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?
回答1:
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.
来源:https://stackoverflow.com/questions/31884008/rails-rollback-when-creating-user-with-nested-forms