lundi 11 janvier 2021

How to update related attributes while saving objects in Rails Active Record

My current logic is, taking two inputs from form where one is primary book and one is secondary book and save it to database with is_primary check being true for primary book and false for secondary. We can have multiple secondary books.

I have a table books: Table name: books

  • book_id
  • book_name

And I have another table authors: Table name: authors

  • author_id
  • author_name

And I have relation table as below: Table name: author_book

  • author_book_id
  • book_id
  • author_id
  • is_primary (Bool)

Code for form:

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

MODEL Book class:

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

Authors class:

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:

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

Controller Author controller

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

ISSUE: What I want to achieve is whenever I save book object, I also somehow save the is_primary as true or false which is present in author_book table.

  1. When I try to add book to author, it gets saved successfully. But I am unable to update the is_primary attribute resulting in all books as secondary. I get undefined method error if I try to update the is_primary attribute. How do I access the is_primary attribute which is present in relation table and solve this cleanly?
  2. If I try to update the author_book table after adding Book, it returns empty array because everything is saved after Author is created.
  3. This is stub code with example I created and not the complete code.

We are using Rails 3.

Aucun commentaire:

Enregistrer un commentaire