使用嵌套表单创建用户时的Rails回滚 [英] Rails rollback when creating user with nested forms
问题描述
尝试将用户添加到数据库时,我得到了回滚,但我不确定为什么.
I'm getting a rollback when trying to add a user to my DB and I'm not sure why.
我有三种型号: 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 **
** 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 **
** 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
因此,当用户想要注册时,他们需要输入公司,位置和用户信息,这由我的company_controller.rb
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 **
** 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
该表单使用具有嵌套属性的标准form_for
,这样,当用户单击提交"按钮时,我希望可以一次完成所有操作
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 **
** 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)
**公司表格**
class CreateCompanies < ActiveRecord::Migration
def change
create_table :companies do |t|
t.string :name
t.timestamps
end
end
end
**位置表**
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
**用户表**
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)
它引用位置标识符已经存在,但是不存在.这是在尝试创建新的Location
时动态进行的操作(请注意location.rb
模型中的方法generate_identifier
).最重要的是,该用户也不存在.
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?
推荐答案
编写validates_presence_of :company
时,这意味着在创建位置时公司记录必须存在,但尚未完全保存.但是,您的位置仍与公司对象相关联,并且无需进行此验证即可正确保存.我认为您可以通过company_id的存在来验证,因为在保存过程中可以使用公司ID.
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.
这篇关于使用嵌套表单创建用户时的Rails回滚的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!