Reputation: 1792
I am playing around with Rails and Angular and trying to get a simple association to update via a JSON PUT request from the frontend.
Association: Article has_many :categories, through: :article_categories
Article model:
class Article < ActiveRecord::Base
validates :title, presence: true, uniqueness: true
validates :body, presence: true
has_many :article_categories
has_many :categories, through: :article_categories
accepts_nested_attributes_for :categories
end
I've got no issues updating the title
and body
, but I cannot update the article's categories.
Here are the relevant controller parts
def update
@article = Article.find(params[:id])
if @article.update(article_params)
render json: @article, status: 200
end
end
private
def article_params
params.require(:article).permit(:title, :body,
categories_attributes: [:name, :id])
end
My incoming JSON looks like this, spaced out to make it more readable:
Started PUT "/api/v1/articles/6" for 127.0.0.1 at 2014-06-01 17:53:04 +0900
Processing by Api::V1::ArticlesController#update as HTML
Parameters: {"title"=>"new title", "body"=>"blablabla", "id"=>"6", "categories"=>
[{"name"=>"Ruby", "id"=>1}, {"name"=>"Javascript", "id"=>2}, {"name"=>"HTML", "id"=>3},
{"name"=>"CSS", "id"=>4}],
"categories_attributes"=>[{"name"=>"Ruby", "id"=>1},
{"name"=>"Javascript", "id"=>2}, {"name"=>"HTML", "id"=>3}, {"name"=>"CSS", "id"=>4}],
"article"=>{"id"=>"6", "title"=>"new title", "body"=>"blablabla"}}
The only feedback I get is that article id
isn't a whitelisted param. Isn't the categories_attributes
what Rails looks for when it takes nested attributes? Why isn't it complaining about the categories
params not being whitelisted?
Upvotes: 1
Views: 221
Reputation: 1792
There were a couple issues here:
My json format was incorrect. The categories were not nested in article
and that's why rails wasn't throwing validation errors. I changed the angular frontend to post this:
{"article"=>{"title"=>"sdfsd", "body"=>"sdf", "category_ids"=>[1, 2, 3]}}
My angular $scope contained both the category ID and name, so I had to write a function to parse out the IDs and dump them in an array. Annoying.
Next, creating an article with this JSON format was failing because of the validations on ArticleCategory
. I added the inverse_of
to my models as described here https://github.com/rails/rails/issues/5178 and then the validations would pass when creating a new article with categories and bypassing the join model. If I understand things correctly, this is an alternative solution to Richard Peck's answer.
The final models looked like this:
class Article < ActiveRecord::Base
validates :title, presence: true, uniqueness: true
validates :body, presence: true
has_many :article_categories, inverse_of: :article
has_many :categories, through: :article_categories
end
class Category < ActiveRecord::Base
validates :name, presence: true, uniqueness: true
has_many :article_categories, inverse_of: :category
has_many :articles, through: :article_categories
end
class ArticleCategory < ActiveRecord::Base
belongs_to :article, inverse_of: :article_categories
belongs_to :category, inverse_of: :article_categories
validates :category, :article, presence: true
end
Upvotes: 0
Reputation: 76784
We've had this problem before - you're basically bypassing the join model
, which is preventing your application from working correctly.
Nested Association
Basically, you need to pass your associated data to your article_categories
model before passing the categories
data:
#app/models/article.rb
Class Article < ActiveRecord::Base
...
accepts_nested_attributes_for :article_categories
end
#app/models/article_category.rb
Class ArticleCategory < ActiveRecord::Base
belongs_to :category
belongs_to :article
accepts_nested_attributes_for :category
end
#app/controllers/articles_controller.rb
def new
@article = Article.new
@article.article_categories.build.build_category
end
def create
@article = Article.new(article_params)
@article.save
end
private
def article_params
params.require(:article).permit(:article, :attributes, article_categories_attributes: [categories_attributes: [:category, :options]] )
end
#app/view/articles/new.html.erb
<%= form_for @article do |f| %>
<%= f.fiels_for :article_categories do |ac| %>
<%= ac.fields_For :categories do |c| %>
<%= c.text_field :your_field &>
<% end %>
<% end %>
<% end %>
Upvotes: 1