Rails 虚拟属性不会从 form_for 提交中读取

发布于 2024-12-25 11:29:02 字数 524 浏览 4 评论 0原文

我正在尝试实现 Ryan Bate 的 Railscast #167 中概述的 Rails 标记模型。 http://railscasts.com/episodes/167-more-on-virtual-attributes

这是一个很好用的系统。但是,我无法获取将 tag_names 提交到控制器的表单。 tag_names 的定义是:

 def tag_names
   @tag_names || tags.map(&:name).join(' ')
 end

不幸的是,在我的例子中,@tag_names 永远不会在表单提交时被分配。我不明白为什么。所以它总是默认为tags.map(&:name).join(' ')。这意味着我无法创建文章,因为它们的 tag_names 不存在,而且我也无法在现有标签上编辑这些标签。有人可以帮忙吗?

I am trying to implement a rails tagging model as outlined in Ryan Bate's railscast #167. http://railscasts.com/episodes/167-more-on-virtual-attributes

This is a great system to use. However, I cannot get the form to submit the tag_names to the controller. The definition for tag_names is :

 def tag_names
   @tag_names || tags.map(&:name).join(' ')
 end

Unfortunately, @tag_names never gets assigned on form submission in my case. I cannot figure out why. SO it always defaults to tags.map(&:name).join(' '). This means that I can't create Articles because their tag_names are not there, and I also can't edit these tags on existing ones. Anyone can help?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

我乃一代侩神 2025-01-01 11:29:02

简而言之,您的类缺少一个 setter(或者用 Ruby 术语来说,就是属性编写器)。有两种方法可以定义 setter 并处理将空格分隔的标记名称字符串转换为 Tag 对象并将它们保存在数据库中。

解决方案 1(Ryan 的解决方案)

在您的类中,使用 Ruby 的 attr_writer 方法定义 setter,并将标签名称字符串(例如 "tag1 tag2 tag3")转换为 Tag 对象并在保存后回调中将它们保存在数据库中。您还需要一个 getter,用于将文章的 Tag 对象数组转换为字符串表示形式,其中标签之间用空格分隔:

class Article << ActiveRecord::Base
  # here we are delcaring the setter
  attr_writer :tag_names

  # here we are asking rails to run the assign_tags method after
  # we save the Article
  after_save :assign_tags

  def tag_names
    @tag_names || tags.map(&:name).join(' ')
  end

  private

  def assign_tags
    if @tag_names
      self.tags = @tag_names.split(/\s+/).map do |name|
        Tag.find_or_create_by_name(name)
      end
    end
  end
end

解决方案 2:将标签名称字符串转换为 Tag< /code> setter 中的对象

class Article << ActiveRecord::Base
  # notice that we are no longer using the after save callback
  # instead, using :autosave => true, we are asking Rails to save
  # the tags for this article when we save the article
  has_many :tags, :through => :taggings, :autosave => true

  # notice that we are no longer using attr_writer
  # and instead we are providing our own setter
  def tag_names=(names)
     self.tags.clear
     names.split(/\s+/).each do |name|
       self.tags.build(:name => name)
     end
  end

  def tag_names
    tags.map(&:name).join(' ')
  end
end

In short, your class is missing a setter (or in Ruby lingo, an attribute writer). There are two ways in which you can define a setter and handle converting the string of space-separated tag names into Tag objects and persist them in the database.

Solution 1 (Ryan's solution)

In your class, define your setter using Ruby's attr_writer method and convert the string of tag names (e.g. "tag1 tag2 tag3") to Tag objects and save them in the database in an after save callback. You will also need a getter that converts the array of Tag object for the article into a string representation in which tags are separated by spaces:

class Article << ActiveRecord::Base
  # here we are delcaring the setter
  attr_writer :tag_names

  # here we are asking rails to run the assign_tags method after
  # we save the Article
  after_save :assign_tags

  def tag_names
    @tag_names || tags.map(&:name).join(' ')
  end

  private

  def assign_tags
    if @tag_names
      self.tags = @tag_names.split(/\s+/).map do |name|
        Tag.find_or_create_by_name(name)
      end
    end
  end
end

Solution 2: Converting the string of tag names to Tag objects in the setter

class Article << ActiveRecord::Base
  # notice that we are no longer using the after save callback
  # instead, using :autosave => true, we are asking Rails to save
  # the tags for this article when we save the article
  has_many :tags, :through => :taggings, :autosave => true

  # notice that we are no longer using attr_writer
  # and instead we are providing our own setter
  def tag_names=(names)
     self.tags.clear
     names.split(/\s+/).each do |name|
       self.tags.build(:name => name)
     end
  end

  def tag_names
    tags.map(&:name).join(' ')
  end
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文