accepts_nested_attributes_for creating duplicates
Model
class Article < ActiveRecord::Base
has_many :article_collections
accepts_nested_attributes_for :article_collections, :allow_destroy => true, reject_if: :all_blank
end
class ArticleCollection < ActiveRecord::Base
belongs_to :article
end
Controller
def update
#article = Article.find_by_id(params[:id])
#article.update_attributes(params[:article])
redirect_to :index
end
Params
params = {"utf8"=>"✓"
"article"=>
{
"data_id"=>"dfe9e32c-3e7c-4b33-96b6-53b123d70e7a", "name"=>"Mass", "description"=>"mass",
"status"=>"active", "volume"=>"dfg", "issue"=>"srf", "iscode"=>"sdg",
"image"=>{"title"=>"", "caption"=>"", "root_image_id"=>""},
"article_collections_attributes"=>
[
{"name"=>"abcd", "type"=>"Special", "description"=>"content ","ordering_type"=>""}
]
},
"commit"=>"Save", "id"=>"b8c8ad67-9b98-4705-8b01-8c3f00e55919"}
Console
Article.find("b8c8ad67-9b98-4705-8b01-8c3f00e55919").article_collections.count
=> 2
Problem is whenever we are updating article it's creating multiple article_collections.
Suppose article_collections is 2 mean if we are updating article it's creating multiple article_collections = 4, it's not updating same article_collections, it's newly creating article_collections.
Why is it creating duplicates?
Your params:
params = {"utf8"=>"✓"
"article"=>
{
"data_id"=>"dfe9e32c-3e7c-4b33-96b6-53b123d70e7a", "name"=>"Mass", "description"=>"mass",
"status"=>"active", "volume"=>"dfg", "issue"=>"srf", "iscode"=>"sdg",
"image"=>{"title"=>"", "caption"=>"", "root_image_id"=>""},
"article_collections_attributes"=>
[
{"name"=>"abcd", "type"=>"Special", "description"=>"content ","ordering_type"=>""}
]
},
"commit"=>"Save", "id"=>"b8c8ad67-9b98-4705-8b01-8c3f00e55919"}
You should send and permit the "id" attribute inside the "article_collections_attributes" params. For Example,
"article_collections_attributes"=>
[
{"id"=>"2", "name"=>"abcd", "type"=>"Special", "description"=>"content ","ordering_type"=>""}
]
I think this code will help you.
Read about nested attribute and build.
In your edit action build object when it has no article_collection.
For ex.#article.article_collection.build if #article.article_collection.blank?. it will not build a new object if it has already a article collection.
Related
I'm new to Rails and have started building my first api; I'm attempting to send an array of strings down as one of the parameters in my api request, like this:
{
"name": "doot doot",
"plans": "",
"sketches": "",
"images": ["foo.png", "bar.png"]
}
Originally, images was a string but I ran a migration to alter it to allow for an array of strings instead, like this:
change_column :projects, :images, "varchar[] USING (string_to_array(images, ','))"
In the controller I've defined the create function as:
def create
project = Project.create(project_params)
render json: project
end
def project_params
params.require(:project).permit(:name, :plans, :sketches, :images)
end
but I still get the following error:
Unpermitted parameter: :images. Context: { controller: ProjectsController, action: create, request: #<ActionDispatch::Request:0x00007fb6f4e50e90>, params: {"name"=>"Simple Box", "plans"=>"", "sketches"=>"", "images"=>["foo.png", "bar.png"], "controller"=>"projects", "action"=>"create", "project"=>{"name"=>"Simple Box", "plans"=>"", "sketches"=>"", "images"=>["foo.png", "bar.png"]}} }
I consulted this question here but the solutions didn't work; any suggestions?
You need to specify that images is an array.
params.require(:project).permit(:name, :plans, :sketches, images: [])
See Permitted Scalar Values in the Rails Guides.
I am sending a JSON from app1 to app2 whenever a model is created in app1. I need to create a similar model in app2 along with the nested attributes. I am able to create the model but not able to figure out how to create the nested attributes model for the same in app2. How can I do that in the same controller?
models in app1
class Author
has_many :books, dependent: :destroy
accepts_nested_attributes_for :books
end
class Book
belongs_to :author
end
books_controller.rb in app1
def new
#author = Author.new
#books = #author.books.build
end
def create
#author = Author.new(author_params)
if #author.save
redirect_to author_path(#author), notice: 'Author was successfully created.'
else
render :new
end
end
def author_params
params.require(:author).permit(:name, books_attributes: [:id, :book_name, :publisher]) if params[:author]
end
api in app1
def self.create_or_update_author_in_app2(auth)
app2_author = {}
app2_author[:author_id_in_app1] = auth.id
app2_author[:name] = auth.name
app2_author[:app2_books_attributes] = auth.books.as_json(except: 'app1_author_id')
response = API.post( 'create_or_update_author', body: { request: { author_data: author_data, authenticate: {auth_key: key} } } )
end
models in app2
class App2Author
has_many :app2_books
end
class App2Book
belongs_to :app2_author
end
controller in app2
def create_or_update_author
response = params['request']['app2_author']
app2_author = App2Author.find_or_create_by(author_id_in_app1: response['author_id_in_app1'])
author.update!(name: response['name'])
app2_author.update_attributes(response['app2_books_attributes']) unless app2_author
end
At present, App2Author instances are being created in app2 but how can I create the associated books from the same json?
response received by controller in app2
Parameters: {"request"=>{"app2_author"=>{"author_id_in_app1"=>"16", "name"=>"Author 1", "app2_books_attributes"=>[{"id"=>"43", "book_name"=>"Book 1", "publisher"=>"Publisher 1", "created_at"=>"2019-07-25 15:26:57 +0530", "updated_at"=>"2019-07-25 15:26:57 +0530"},
{"id"=>"43", "book_name"=>"Book 1", "publisher"=>"Publisher 1", "created_at"=>"2019-07-25 15:26:57 +0530", "updated_at"=>"2019-07-25 15:26:57 +0530"},
{"id"=>"43", "book_name"=>"Book 1", "publisher"=>"Publisher 1", "created_at"=>"2019-07-25 15:26:57 +0530", "updated_at"=>"2019-07-25 15:26:57 +0530"},
{"id"=>"43", "book_name"=>"Book 1", "publisher"=>"Publisher 1", "created_at"=>"2019-07-25 15:26:57 +0530", "updated_at"=>"2019-07-25 15:26:57 +0530"}]}, "authenticate"=>{"auth_key"=>"my_key"}}}
The code bellow is just an idea how you can handle this.
models in app2
class App2Author
has_many :app2_books
# it's better to keep business logic in models.
def update_info(data)
name = data['name']
data['author_books'].each do |book|
books << Book.new(book)
end
save!
end
end
class App2Book
belongs_to :app2_author
end
controller in app2
def create_or_update_author
request = params['request']['author_data']
author = App2Author.find_or_create_by(author_id_in_app1: request['author_id_in_app1'])
author.update_info(request)
end
Anyway to be hones with you it's not a good approach. Rails has default mechanism to create associated objects. TO make it work in the right way you need:
1) add accepts_nested_attributes_for :app2_books to App2Author model.
class App2Author
has_many :app2_books
end
2) in the first app build valid hash with parameters and send to the second app . Something like:
app2_author: { id: 1, name: 'Author name', app2_books_attributes: [:app2_author_id, :book_name, :publisher]}
3) In the second app in controller do something like this:
author = App2Author.find_or_create_by(id: params[:id])
author.update(params) #in case the params hash is valid
that will create associations automatically.
I'm facing a case when a need to display information contained in my join table. For example:
# == Schema Information
#
# Table name: quality_inspections
#
# id, content
#
# =============================================================================
class QualityInspection
has_many :inspection_inspectors
has_many :inspector, through: :inspection_inspectors
end
# == Schema Information
#
# Table name: inspection_inspectors
#
# quality_inspection_id, inspector_id, inspection_date
#
# =============================================================================
class InspectionInspector
belongs_to :quality_inspection
belongs_to :user, foreign_key: :inspector_id
end
Then, I'd like to have the following json:
{
"quality_inspectors": [{
"id": 1,
"content": "foo",
"inspectors": [{
"id": 1, // which is the user's id
"first_name": "Bar",
"last_name": "FooFoo",
"inspection_date": "random date"
}]
}]
}
For now, I'm doing the following in my serializer:
module Api::V1::QualityInspections
class InspectorSerializer < ActiveModel::Serializer
type :inspector
attributes :id, :first_name, :last_name, :inspection_date
def id
inspector.try(:public_send, __callee__)
end
def first_name
inspector.try(:public_send, __callee__)
end
def last_name
inspector.try(:public_send, __callee__)
end
private
def inspector
#inspector ||= object.inspector
end
end
end
Do you have any better solution ? Or maybe I'm not using the right methods on my Serializer ?
Anyway, I'm really stuck when it came to display information on a join table. Oddly, I'd the same issue when using cerebris/jsonapi-resources.
EDIT: Linked issue on GH: https://github.com/rails-api/active_model_serializers/issues/1704
I don't think you need to put additional methods such as id, first_name, last_name. Instead, use association within your serializer to get the appropriate JSON data as mentioned afore.
I have the following dynamic params depending on the line items i am trying to add to an order
{"line_item" => {"items"=>{"0"=>{"price"=>"5.75", "name"=>"Item name", "quantity"=>"5"}, "1"=>{"price"=>"3.35", "name"=>"Item name", "quantity"=>"1"}}}
In my controller:
def lineitems_params
params.require(:line_item).permit(:key1, :key2, :key3, :key4, :payment_type, :payment_provider).tap do |whitelisted|
whitelisted[:items] = params[:line_item][:items]
end
end
I still get the
Unpermitted parameters: items
in my logs, and it does not update the items.
How can i solve this?
NOTE: the items hash can have many elements inside.
EDIT:
In my model:
serialize :items, Hash
This should work
def lineitems_params
params.require(:line_item).permit(:key1, :key2, :key3, :key4, :payment_type, :payment_provider, {:items => {:price, :name, :quantity}})
end
Update
may be you should just give like this
def lineitems_params
params.require(:line_item).tap do |whitelisted|
whitelisted[:items] = params[:line_item][:items]
end
end
Source
Note: Don't give params.require(:line_items).permit! it permits all attributes.
Hey, I am writing a website similar to 4chan. I have two models: Board and Picture. A Board has many Pictures:
class Board < ActiveRecord::Base
# Relationships
has_many :pictures, :dependent => :destroy
end
class Picture < ActiveRecord::Base
# Relationships
belongs_to :board
end
I do this in my view boards/show.html.haml:
- if #board.pictures.any?
- #board.pictures.each do |picture|
%article.picture-article{ :id => "picture-#{picture.id}" }
- if picture.title?
%header
%h2= picture.title
%img{ :src => picture.pic.url, :alt => (picture.title? ? picture.title : 'untitled pic') }/
%pre~ picture.description
- else
meh this board is still empty
And in my nice BoardsController:
def show
#board = Board.find_by_short_name params[:id]
end
The problem is that always one Picture is shown which is empty. For example, I have a Board with no Pictures in the db (checked it with Picture.all), but this is the output:
<article class='picture-article' id='picture-'>
<img alt='untitled pic' src='/pics/original/missing.png'>
<pre></pre>
</article>
However, when I render it as JSON, the output is with no Pictures, like it should be:
{
"board": {
"name": "n00bs",
"created_at": "2011-03-16T17:14:32Z",
"updated_at": "2011-03-16T17:14:32Z",
"id": 14,
"short_name": "0",
"pictures": [
]
}
}
I can remove this by simply removing the last item of the array before doing an each but the bug still remains and this is super duper clumsy. How can this happen? Why does #board.pictures always append an empty Picture object?
Try pictures.present? instead of .any?
Ha!
In my view, adding .all did the job (and order also works):
- #board.pictures.all.each do |picture|