5

我在尝试更新具有has_and_belongs_to_many关联的模型时遇到问题。

假设Post has_and_belongs_to_many Tag, 并Post验证 title 和 的存在Tags。如果我更新Post,删除它的标题和标签,我会在title和中得到验证错误tags,好的。但是已经删除了在andActiveAdmin之间建立关联的记录,因此,如果我离开编辑页面,数据库上的 将无效,而没有.PostTagPostposttags

这是我的模型:

class Tag < ActiveRecord::Base
  attr_accessible :label
  has_and_belongs_to_many :posts
end

class Post < ActiveRecord::Base
  attr_accessible :content, :title, :tag_ids
  has_and_belongs_to_many :tags
  validates_presence_of :content, :title, :tags
end

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

我使用 selected -rails gem,它允许用户取消选择帖子的所有标签。

总而言之,我的问题是:ActiveAdmin 在执行模型验证之前更新数据库上的关系。

这种行为有解决方案还是我做错了什么?

编辑:

这是我尝试更新没有标题和标签的帖子时的请求日志:

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.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 

在进行任何数据库更新之前,有什么方法可以更新帖子属性(包括标签)并验证模型?

4

5 回答 5

2

你可以使用这个 gem:https ://github.com/MartinKoerner/deferred_associations

延迟关联将修复此错误。

于 2013-09-02T18:38:40.270 回答
1

@Rodrigo 我能够在没有活动管理员的情况下在本地重现您的问题,如果您看到 [此处][1],问题实际上是使用 HABTM 关系时的默认操作之一

[1]:http ://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_and_belongs_to_many 它说:

集合=对象

Replaces the collection’s content by deleting and adding objects as appropriate.

所以显然你需要覆盖这个操作

这是一个例子:

在 has_many :through 关系上覆盖 ActiveRecord << 运算符,以接受连接模型的数据

让我知道如何帮助你

于 2013-04-10T14:19:16.177 回答
0

因此,对于遇到相同问题的任何人,我找到了一种解决方法:您可以在 has_and_belongs_to_many 关联中定义 before_remove 并引发异常,然后您可以在 ActiveAdmin 中捕获该异常。

为此,它将是

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

于 2020-03-16T21:12:23.633 回答
0

我建议添加一个 tmp 变量并在其中存储值。然后,如果验证通过,您应该将它们移至数据库。

这是一个例子:

你的models/article.rb

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

你的admin/article.rb, 块form

f.input :tmp_author_ids, as: :select, multiple: true, collection: Author.all, label: 'Authors'

而已

于 2017-04-27T12:52:53.010 回答
-1

如果需要,您可以在 active_admin 中重新定义更新操作,以防止保存空标签,类似于这种风格

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

我认为模型中的这些东西可以删除

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
于 2013-04-03T18:44:46.117 回答