Edit object with HABTM association modify database before validation with ActiveAdmin
我在尝试更新具有
假设
如果我更新
但是
这里是我的模特:
1 2 3 4 | class Tag < ActiveRecord::Base attr_accessible :label has_and_belongs_to_many :posts end |
1 2 3 4 5 | class Post < ActiveRecord::Base attr_accessible :content, :title, :tag_ids has_and_belongs_to_many :tags validates_presence_of :content, :title, :tags end |
1 2 3 4 5 6 7 8 9 10 11 12 13 | ActiveAdmin.register Post do form do |f| f.inputs do f.input :title f.input :content f.input :image f.input :tags end f.buttons end end |
我使用选择的Railsruby,它允许用户取消选择所有帖子标签。
总而言之,我的问题是:ActiveAdmin在执行模型验证之前更新数据库上的关系。
有针对此行为的解决方案,或者我做错了什么?
编辑:
当我尝试更新不带标题和标签的帖子时,出现以下请求日志:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Started PUT"/admin/posts/8" for 127.0.0.1 at 2013-04-01 10:32:07 -0300 Processing by Admin::PostsController#update as HTML Parameters: {"utf8"=>"?","authenticity_token"=>"amSbLlP/rgDrNn/N8lgq/KEaRXK1fMPShZDwpZ0QIJ4=","post"=>{"title"=>"","content"=>"content","tag_ids"=>["",""]},"commit"=>"Update Post","id"=>"8"} AdminUser Load (0.2ms) SELECT `admin_users`.* FROM `admin_users` WHERE `admin_users`.`id` = 1 LIMIT 1 Post Load (0.2ms) SELECT `posts`.* FROM `posts` WHERE `posts`.`id` = 8 LIMIT 1 Tag Load (0.2ms) SELECT `tags`.* FROM `tags` INNER JOIN `posts_tags` ON `tags`.`id` = `posts_tags`.`tag_id` WHERE `posts_tags`.`post_id` = 8 (0.1ms) BEGIN SQL (12.3ms) DELETE FROM `posts_tags` WHERE `posts_tags`.`post_id` = 8 AND `posts_tags`.`tag_id` IN (1, 2) (49.6ms) COMMIT (0.1ms) BEGIN (0.2ms) ROLLBACK Post Load (0.3ms) SELECT `posts`.* FROM `posts` WHERE `posts`.`id` = 8 LIMIT 1 Tag Load (0.2ms) SELECT `tags`.* FROM `tags` Rendered /home/rodrigo/.rvm/gems/ruby-1.9.3-p125@blog/gems/activeadmin-0.5.1/app/views/active_admin/resource/edit.html.arb (192.3ms) Completed 200 OK in 276ms (Views: 194.8ms | ActiveRecord: 63.3ms) |
编辑2:
好的,我确定ActiveAdmin出现此错误。
从ActiveRecord的行为来看,我认为仅使用模型类会破坏验证流程。请参见以下示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 1.9.3p125 :064 > post = Post.find(8) Post Load (0.3ms) SELECT `posts`.* FROM `posts` WHERE `posts`.`id` = 8 LIMIT 1 => #<Post id: 8, title:"title", content:"content", created_at:"2013-03-27 13:13:20", updated_at:"2013-03-27 13:13:20", image:"extrato.bmp"> 1.9.3p125 :065 > post.tags Tag Load (0.2ms) SELECT `tags`.* FROM `tags` INNER JOIN `posts_tags` ON `tags`.`id` = `posts_tags`.`tag_id` WHERE `posts_tags`.`post_id` = 8 => [#<Tag id: 1, label:"tag", created_at:"2013-02-25 18:32:45", updated_at:"2013-02-25 18:32:45">, #<Tag id: 2, label:"new", created_at:"2013-02-25 18:32:50", updated_at:"2013-02-25 18:32:50">] 1.9.3p125 :066 > post.title ="" =>"" 1.9.3p125 :067 > post.save #<<<<<<< It's invalid on title => false 1.9.3p125 :068 > post.tags = [] #<<<<<<< This shouldnt trigger database update (0.3ms) BEGIN SQL (0.5ms) DELETE FROM `posts_tags` WHERE `posts_tags`.`post_id` = 8 AND `posts_tags`.`tag_id` IN (1, 2) (55.5ms) COMMIT => [] 1.9.3p125 :069 > post.save #<<<<<<< It's invalid on title AND TAGS (0.2ms) BEGIN (0.2ms) ROLLBACK => false 1.9.3p125 :070 > post.reload Post Load (0.2ms) SELECT `posts`.* FROM `posts` WHERE `posts`.`id` = 8 LIMIT 1 => #<Post id: 8, title:"title", content:"content", created_at:"2013-03-27 13:13:20", updated_at:"2013-03-27 13:13:20", image:"extrato.bmp"> 1.9.3p125 :071 > post.valid? #<<<<<<< Now, I have this model in invalid state Tag Load (0.6ms) SELECT `tags`.* FROM `tags` INNER JOIN `posts_tags` ON `tags`.`id` = `posts_tags`.`tag_id` WHERE `posts_tags`.`post_id` = 8 => false |
是否可以在进行任何数据库更新之前更新帖子属性(包括标签)并验证模型?
您可以使用以下gem:https://github.com/MartinKoerner/deferred_associations
延迟的关联将修复此错误。
@Rodrigo我可以在没有活动管理员的情况下在本地重现您的问题,实际上,这是使用HABTM关系时的默认操作之一,如果您看到[here] [1]
[1]:http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_and_belongs_to_many,它说:
collection =对象
1 | Replaces the collection’s content by deleting and adding objects as appropriate. |
因此,显然您需要覆盖此操作
这里是一个例子:
在has_many:through关系上重写ActiveRecord <<运算符,以接受联接模型的数据
让我知道如何为您提供帮助
因此,对于遇到相同问题的任何人,我都找到了一种解决方法:
您可以在has_and_belongs_to_many关联中定义一个before_remove,并引发一个异常,然后您可以在ActiveAdmin中捕获该异常。
为此它将是
1 2 3 4 5 6 7 8 9 10 11 | class Post < ActiveRecord::Base attr_accessible :content, :title, :tag_ids has_and_belongs_to_many :tags, before_remove: :check_something validates_presence_of :content, :title, :tags end def check_something(agent) if self.tags.size == 1 raise ActiveModel::MissingAttributeError.new 'something' end end |
更多信息:https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
我建议添加一个tmp变量并将值存储在其中。然后,如果通过验证,则应将它们移至数据库。
这里是一个例子:
您的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class Article < ActiveRecord::Base validate :author_presence has_and_belongs_to_many :authors attr_writer :tmp_author_ids def tmp_author_ids @tmp_author_ids || author_ids end def author_presence if tmp_author_ids.reject(&:blank?).empty? errors.add(:tmp_author_ids, 'Author is missing') else self.author_ids = tmp_author_ids end end end |
您的
1 | f.input :tmp_author_ids, as: :select, multiple: true, collection: Author.all, label: 'Authors' |
就这样
如果需要,您可以在active_admin中重新定义更新操作,以防止以这种样式保存空标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | ActiveAdmin.register Post do controller do def update if params[:post][:tag_ids] == ["",""] flash.now[:alert] ="You can't remove all tags" render :edit else super end end end ... end |
我认为可以删除模型中的这些内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | attr_accessor :new_tag_ids validate :validate_new_tags_ids after_save :update_tags def update_tags self.tag_ids = @new_tag_ids if defined?(@new_tag_ids) @new_tag_ids = nil end private def validate_new_tags_ids errors[:tags] <<"can't be blank (2)" if @new_tag_ids.blank? end |