Rails 4 - 无法更新模型属性

Rails 4 - Can't update model attribute

如何根据同一模型的属性之间的条件保存属性?除非属性的状态保持为真,否则我无法更新记录。

我之前检查过这个线程:Rails. Update model attributes on save

这是我的代码 运行:

class Ip < ActiveRecord::Base
  has_paper_trail
  has_and_belongs_to_many :users
  validates_uniqueness_of :ip_address, :hostname
  before_save :set_availability

  extend Enumerize
  enumerize :status, in: [:available, :allocated, :pending, :blocked], default: :available

  def ip_address_name
    self.ip_address
  end

  def set_availability
    self.is_available = false unless self.status.available?      
  end
end

基本上我有一个 "Ips" 的列表,我想跟踪它,还有一个 "is_available" 布尔值告诉我 ip 的状态是否可用(应该自行设置自动,基于 "status" 字段)。

所以问题是,例如,

我访问模型,编辑记录,将 :status 从 :available 更改为 :allocated,然后点击 "Save",它回滚,提升我 "Ip failed to be updated",并在服务器上事务是“已完成 406 不可接受

这是日志:

=============== Phusion Passenger Standalone web server started ===============
PID file: /home/user/IP_Manager/tmp/pids/passenger.3000.pid
Log file: /home/user/IP_Manager/log/passenger.3000.log
Environment: development
Accessible via: http://0.0.0.0:3000/

You can stop Phusion Passenger Standalone by pressing Ctrl-C.
Problems? Check https://www.phusionpassenger.com/library/admin/standalone/troubleshooting/
===============================================================================
App 31353 stdout: 
App 31368 stdout: 


Started HEAD "/" for 127.0.0.1 at 2015-08-04 09:41:07 -0300
  ActiveRecord::SchemaMigration Load (0.2ms)  SELECT "schema_migrations".* FROM "schema_migrations"
Processing by HomeController#index as HTML
Redirected to http://0.0.0.0/users/sign_in
Completed 302 Found in 10ms (ActiveRecord: 0.0ms)


Started GET "/admin/ip/2/edit" for 127.0.0.1 at 2015-08-04 09:41:14 -0300
Processing by RailsAdmin::MainController#edit as HTML
  Parameters: {"model_name"=>"ip", "id"=>"2"}
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ?  ORDER BY "users"."id" ASC LIMIT 1  [["id", 1]]
  Ip Load (0.1ms)  SELECT  "ips".* FROM "ips" WHERE "ips"."id" = ?  ORDER BY "ips"."id" ASC LIMIT 1  [["id", 2]]
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/rails_admin/main/_form_boolean.html.haml (2.4ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/rails_admin/main/_form_field.html.haml (1.2ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/rails_admin/main/_form_field.html.haml (0.2ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/rails_admin/main/_form_enumeration.html.haml (2.7ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/rails_admin/main/_form_text.html.haml (1.0ms)
  User Load (0.2ms)  SELECT "users".* FROM "users" INNER JOIN "ips_users" ON "users"."id" = "ips_users"."user_id" WHERE "ips_users"."ip_id" = ?  [["ip_id", 2]]
   (0.1ms)  SELECT COUNT(*) FROM "users"
  User Load (0.1ms)  SELECT "users".* FROM "users"  ORDER BY users.id desc
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/rails_admin/main/_form_filtering_multiselect.html.haml (27.7ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/rails_admin/main/_submit_buttons.html.haml (2.8ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/rails_admin/main/edit.html.haml within layouts/rails_admin/application (64.5ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/layouts/rails_admin/_secondary_navigation.html.haml (3.7ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/layouts/rails_admin/_navigation.html.haml (6.6ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/layouts/rails_admin/_sidebar_navigation.html.haml (4.1ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/layouts/rails_admin/pjax.html.haml (7.3ms)
Completed 200 OK in 611ms (Views: 484.5ms | ActiveRecord: 1.6ms)


Started PUT "/admin/ip/2/edit" for 127.0.0.1 at 2015-08-04 09:41:20 -0300
Processing by RailsAdmin::MainController#edit as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"OI5LqfNXeYLQm1wGglmnPvkrTL3sHTYsiE/uJGp7Uxuserk7ee8b8ozIXclBkUtYYcoIeKCjcNnyZ00siruQjEZQ==", "ip"=>{"is_available"=>"1", "ip_address"=>"192.168.0.2", "hostname"=>"localhost2", "status"=>"allocated", "details"=>"", "user_ids"=>["", "", "1"]}, "return_to"=>"", "_save"=>"", "model_name"=>"ip", "id"=>"2"}
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ?  ORDER BY "users"."id" ASC LIMIT 1  [["id", 1]]
  Ip Load (0.1ms)  SELECT  "ips".* FROM "ips" WHERE "ips"."id" = ?  ORDER BY "ips"."id" ASC LIMIT 1  [["id", 2]]
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
  User Load (0.1ms)  SELECT "users".* FROM "users" INNER JOIN "ips_users" ON "users"."id" = "ips_users"."user_id" WHERE "ips_users"."ip_id" = ?  [["ip_id", 2]]
   (1.1ms)  begin transaction
  Ip Exists (0.2ms)  SELECT  1 AS one FROM "ips" WHERE ("ips"."ip_address" = '192.168.0.2' AND "ips"."id" != 2) LIMIT 1
  Ip Exists (0.2ms)  SELECT  1 AS one FROM "ips" WHERE ("ips"."hostname" = 'localhost2' AND "ips"."id" != 2) LIMIT 1
   (0.1ms)  rollback transaction
  PaperTrail::Version Load (0.3ms)  SELECT "versions".* FROM "versions" WHERE "versions"."item_id" = ? AND "versions"."item_type" = ?  ORDER BY "versions"."created_at" ASC, "versions"."id" ASC  [["item_id", 2], ["item_type", "Ip"]]
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/rails_admin/main/_form_boolean.html.haml (0.4ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/rails_admin/main/_form_field.html.haml (0.4ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/rails_admin/main/_form_field.html.haml (0.3ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/rails_admin/main/_form_enumeration.html.haml (1.1ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/rails_admin/main/_form_text.html.haml (0.5ms)
  User Load (0.2ms)  SELECT "users".* FROM "users"  ORDER BY users.id desc
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/rails_admin/main/_form_filtering_multiselect.html.haml (5.0ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/rails_admin/main/_submit_buttons.html.haml (1.5ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/rails_admin/main/edit.html.haml within layouts/rails_admin/application (25.7ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/layouts/rails_admin/_secondary_navigation.html.haml (1.9ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/layouts/rails_admin/_navigation.html.haml (3.6ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/layouts/rails_admin/_sidebar_navigation.html.haml (2.6ms)
  Rendered /home/user/.rvm/gems/ruby-2.2.1/gems/rails_admin-0.6.8/app/views/layouts/rails_admin/pjax.html.haml (6.1ms)
Completed 406 Not Acceptable in 267ms (Views: 223.5ms | ActiveRecord: 2.9ms)

这是我的 Gemfile(除了默认的之外只有额外的 gem):

# Administration Panel Gems
gem 'rails_admin'                    # Rails Administration Panel Gem
gem 'rails_admin_history_rollback'   # Enables users to visualise and revert history
gem 'rails_admin_import', "~> 1.0.0" # Enables importation
gem 'devise'                         # Authentication Gem
gem 'cancancan'                      # Authorization Gem
gem 'paper_trail', '~> 4.0.0.rc'     # Auditing Gem (History)
gem 'enumerize'                      # Gem for enumerizing attributes

# Server gem
gem 'passenger'

感谢您的宝贵时间!

不确定这是否是导致问题的原因,但可能是。如果任何方法执行为 before_save returns false,整个事务将中止。

你的方法:

def set_availability
  self.is_available = false unless self.status.available?      
end

将 return 为假或为零。第一种情况将取消交易。将此方法更改为:

def set_availability
  self.is_available = false unless self.status.available?
  true    
end

由于您在 set_availability 中设置了一个假值,回调会返回一个假值并回滚。 returns false 的 before* 回调将回滚模型上的所有更新。

来自 Rails 文档 http://guides.rubyonrails.org/active_record_callbacks.html

6 Halting Execution

As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model's validations, the registered callbacks, and the database operation to be executed.

The whole callback chain is wrapped in a transaction. If any before callback method returns exactly false or raises an exception, the execution chain gets halted and a ROLLBACK is issued; after callbacks can only accomplish that by raising an exception.

类似这样的方法可能有效。

def set_availability
  self.is_available = false unless self.status.available?
  true      
end