【问题标题】:How to update related attributes while saving objects in Rails Active Record如何在 Rails Active Record 中保存对象时更新相关属性
【发布时间】:2021-01-12 00:36:57
【问题描述】:

我目前的逻辑是,从表单中获取两个输入,其中一个是主要书籍,一个是辅助书籍,并将其保存到数据库中,is_primary 检查主要书籍为真,次要书籍为假。我们可以有多个辅助书籍。

我有桌书: 表名:书籍

  • book_id
  • 书名

我还有另一个表 authors: 表名:作者

  • author_id
  • 作者姓名

我有如下关系表: 表名:author_book

  • author_book_id
  • book_id
  • author_id
  • is_primary(布尔)

表格代码:

  <div class="elem-set">
    <div class="elem-label">
      <label>Author</label>
    </div>

    <%= f.select :associated_author,
    Author.sort_by_name.map{|t| ["#{t.id}: #{t.name}", t.name]},
    { include_blank: "No Author" },
    { class: 'chzn-select', multiple: false }
    %><br /><br />
  </div>


  <div class="elem-set">
    <div class="elem-label">
      <label>Primary Book</label>
    </div>

    <%= f.select :associated_primary_book,
    Book.sort_by_name.map{|n| ["#{n.book_name}: #{n.book_id}", n.book_name]},
    { include_blank: "No Book" },
    { class: 'chzn-select', multiple: false }
    %><br /><br />
  </div>


  <div class="elem-set">
    <div class="elem-label">
      <label>Secondary Book</label>
    </div>

    <%= f.select :associated_secondary_book,
    Book.sort_by_name.map{|n| ["#{n.book_name}: #{n.book_id}", n.book_name]},
    { include_blank: "No Book" },
    { class: 'chzn-select', multiple: true }
    %><br /><br />
  </div>

型号 书籍类:

class Book < ActiveRecord::Base
    has_many :author_book, dependent: :destroy
    has_many :authors, through: :author_book

    scope :sort_by_name, -> { select([:book_id, :book_name]).order('book_name ASC') }
end

作者类:

class Author < ActiveRecord::Base
  has_many :author_book, dependent: :destroy
  has_many :books, through: :author_book
  def associated_primary_books=(fetched_books)
    self.books.clear
    if !fetched_books.empty?
        self.books << Book.find_by_name(fetched_books)
        #is_primary=> true
    end
  end


  def associated_secondary_books=(fetched_books)
    fetched_books = fetched_books.delete_if { |x| x.empty? }
    if !fetched_books.empty?
      fetched_books.each do |book|
        self.books << Book.find_by_name(book)
      end
    end
  end
  
end

AuthorBook 类:

class AuthorBook < ActiveRecord::Base
  belongs_to :authors
  belongs_to :books
  
end

控制器 作者控制器

AuthorController < ApplicationController
  def create
    @Author = Author.new(params[:author])

    respond_to do |format|
      begin
        retries ||= 0
        if @author.save
          flash[:notice] = 'Author created.'
          Event.init(user: current_user.id, type: "Create Author", details: {
            :author => @author.id
          })
          format.html { redirect_to action: 'show',
                :id => @author.id
          }
        end
      rescue ActiveRecord::StatementInvalid => e
        flash[:error] = 'error'
        redirect_to action: 'new'
      end
    end
  end

问题:我想要实现的是,每当我保存书籍对象时,我也会以某种方式将 is_primary 保存为存在于 author_book 表中的 true 或 false。

  1. 当我尝试将书籍添加到作者时,它会成功保存。但我无法更新 is_primary 属性导致所有 书籍为次要。如果我尝试更新,我会收到未定义的方法错误 is_primary 属性。如何访问 is_primary 属性 哪个存在于关系表中并干净地解决了这个问题?
  2. 如果我在添加 Book 后尝试更新 author_book 表,它会返回空数组,因为在 Author 之后所有内容都已保存 已创建。
  3. 这是我创建的带有示例的存根代码,而不是完整代码。

我们正在使用 Rails 3。

【问题讨论】:

  • 您的表应命名为author_books,关联命名为has_many :author_books。我有点惊讶这还没有导致错误。

标签: mysql ruby-on-rails ruby-on-rails-3 design-patterns activerecord


【解决方案1】:

基本上,您在这里要做的是循环并插入记录。除了 Rails 3 不支持仅在 Rails 6 中添加的 upserts 之外。您可以做的是:

class Author < ActiveRecord::Base
  has_many :author_books, dependent: :destroy
  has_many :books, through: :author_books
  
  def associated_primary_books=(fetched_books)
    fetched_books.select(&:present?).each do |id|
      ab = author_books.find_or_initialize_by(book_id: id)
      ab.is_primary = true
      ab.save unless new_record?
    end  
  end

  def associated_secondary_books=(fetched_books)
    fetched_books.select(&:present?).each do |id|
      ab = author_books.find_or_initialize_by(book_id: id)
      ab.is_primary = false
      ab.save unless new_record?
    end 
  end
end

请注意,我使用的是书的 ID 而不是名称。不要传递名字 - 这很愚蠢,因为书名几乎不是唯一的,因此必然会出现歧义。

【讨论】:

  • 您好 max,我收到 find_or_initialize_by 的错误,因为在调用 associated_primary_books 后,作者的记录被插入到作者控制器中。根据我的观察, associated_primary_books 在 self.books 中分配书籍对象,并且一旦在作者控制器中调用 @Author = Author.new(params[:author]) 就会将其保存到数据库中。有什么可以帮助的想法吗?
  • 我想把这段代码移到 Author.new(params[:author]) 下面,但这看起来一点也不好看。我得到的错误是:[]:ActiveRecord::Relation 的未定义方法“find_or_initialize_by”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-03
  • 2010-12-07
  • 1970-01-01
相关资源
最近更新 更多