1

I'm new to Ruby on Rails, and I'm developing a backend API.

Currently, I got 2 Active Record Models called Book and Genre.

Active Record Model

class Book < ActiveRecord::Base
    has_and_belongs_to_many :genres
end

class Genre < ActiveRecord::Base
    hast_and_belongs_to_many :books
end

DB Schema Model

create_table :books do |t|
  t.string "title"
end

create_table :genres do |t|
  t.string "genre"
end

create_join_table :books, :genres do |t|
  t.index [:book_id, :genre_id]
  t.index [:genre_id, :book_id]
end

REST POST Request

 # POST /book
    def create
        book= Book.new(books_params)

        if book.save
            render json: {status: 'SUCCESS', message:'Book Saved', data: book},status: :ok
        else
            render json: {status: 'ERROR', message: 'Booknot saved', data: book.errors}, status: :unprocessable_entity
        end 

    end

private
    def books_params
        params.require(:book).permit(:title)
    end

QUESTION

I'd like to make an HTTP Post request to create a new book with it's genres. I've already tested the book insertion (without genres, just passing the book name), it works perfectly. However I'd also like to add some genre categories.

Jiahui Chen
  • 75
  • 3
  • 10
  • Welcome to Stack Overflow! It doesn't look like there's a specific, answerable question in your question segment, and it can be hard for the community to give a specific answer in this case. What have you tried so far? Are you running into a specific error or issue? You should [edit] your question to include anything you've tried so far to implement this yourself, and why/how it hasn't worked in your case. – Hoppeduppeanut Jul 23 '20 at 05:58
  • @Hoppeduppeanut Thank you! What I'd like to achieve, is to store the data sent from a front-end form. In the "QUESTION" section I said that I've tried to send a book data (without the genres array), and it works perfectly so far. However, now I'd like to create a new book with a genres array, for instace genres_id[1, 2]. What should I do to store the genres in the "def create" section? book = Book.new(books_params) only creates a new book, but the newly created book is not assigned with its genres in the join table. Also, I think that I need to create the genres first in the genres table. – Jiahui Chen Jul 24 '20 at 11:44

1 Answers1

1

Both the has_many and has_and_belongs_to_many class methods create a set of _ids setters and getters:

book = Book.new
book.genre_ids = [1, 2, 3]
book.genre_ids == [1, 2, 3] # true

These setters will automatically create and delete rows in the join table. Since ActiveModel::AttributeAssignment maps the hash argument to .new, .update and .create to setters in the model all you need to do is whitelist the genre_ids parameter:

def books_params
  params.require(:book).permit(:title, genre_ids: [])
end

Passing an empty array permits an array of permitted scalar values like for example numericals or strings.

max
  • 96,212
  • 14
  • 104
  • 165
  • so far so good, now that I expect to receive an array of genre_ids, how do I assign them to book.genre_ids ? It seems that the example you provided,you're directly assigning genres (1,2,3) I've tried book.genre_ids = books_params[:genre_ids] – Jiahui Chen Jul 25 '20 at 06:59
  • You would pass a JSON object in the request body that looks like `{ "book": { "title": "AN Example Book", "genre_ids": [1, 2, 3] }}`. When you pass the whitelisted params hash with `book = Book.new(books_params)` `assign_attributes` will call the setter. You don't actually have to do anything manually. That's the whole point of mass assignment. – max Jul 25 '20 at 09:16