how to Deleting and destroying embedded documents in Mongoid with Rails 4 - ruby-on-rails

I m using relation ( has_many , belongs_to) tha's working with source code above here, but I get error document Not Found for delete Photo(picture) when change relations (embeds_many, embedded_in). Anybody Helpme please, how to use Embed_many relations using mongoid & what's wrong my source code here :
class Room
include Mongoid::Document
field :home_type, type: String
field :room_type, type: String
embeds_many :photos
end
class Photo
include Mongoid::Document
include Mongoid::Paperclip
embedded_in :room
end
class PhotosController < ApplicationController
def destroy
#photo = Photo.find(params[:id])
room = #photo.room
#photo.destroy
#photos = Photo.where(room_id: room.id)
respond_to :js
end
end

The simple answer here is that when you embed a document you are adding that document within another one. In order for mongodb to find the embedded document it first needs to find the parent. In your previous iteration you with has_many you are associating two documents from different collections, with enables you to look up by the associated document.
Therefore although embedded documents have an _id, you are only able to look them up from within the document. If you were to output #photo you would see that it was nil. I am surprised that your second line room = #photo.room is not returning an error no method for nil:NilClass.
To do what you want, you first need to find the document, which you could do without too much change:
class PhotosController < ApplicationController
def destroy
room = Room.find_by('photo._id': BSON::ObjectId(params[:id]))
#photo = room.photos.find(params[:id])
#photo.destroy
#photos = room.photos
respond_to :js
end
end

Related

Issue on association in RAILS

Here is my issue. I have two models (Construction and Customer)
class Construction < ApplicationRecord
has_many :works
belongs_to :customer
end
class Customer < ApplicationRecord
has_many :constructions
end
I would like to associate a Customer to a Construction during the creation of a new construction.
To do so I have de following controller's method (which is obviously false)
def create
# #construction = Construction.new(constructions_params) (commented)
#construction = Construction.new(customer: #customer)
#customer = Customer.find(params[:customer_id])
#construction.save!
end
from the params I am able to understand that the construction is not saved because it is not attached to a customer and so cannot be created.
I am new to rails and I have been struggling for hours now..
Hope someone will be able to help me.
thanks a lot
Try to revert the order:
#customer = Customer.find(params[:construction][:customer_id])
#construction = Construction.new(customer: #customer)
#construction.save!
you need to assign #customer instance variable before you use it. Otherwise it's nil and nothing is assigned to the new Construction record.
If you have the customer_id available at the point of form creation I reckon that you can do something like this.
Also given the belongs_to relations with the customer on the construction, you should be able to update the customer_id on the construction.
def create
#construction = Construction.new(construction_params)
if #construction.save
# whatever you want to do on success
else
# Whatever you want to do on failure
end
end
# Given you have construction params
private
def construction_params
params.require(:construction).permit(:all, :the, :construction, :attributes, :customer_id)
end

Rails query of parent model doesn't retrieve child models

I have parent and child models on rails 5 with mongoid. When I query the parent, with .includes command - I can see rails trying to query mongo db - but the result json does not return the child objects.
Parent Model:
class Activity
include Mongoid::Document
field :title, type: String
has_many :activity_pictures
end
Child Model:
class ActivityPicture
include Mongoid::Document
field :name, type: String
belongs_to :activity, :class_name => 'Activity'
end
The controller methods:
def index
#activities = Activity.includes(:activity_pictures).all
end
def show
Activity.includes(:activity_pictures)
end
off course, I have updated activity_params:
def activity_params
params.require(:activity).permit(:title, :activity_pictures)
end
How do i get the full json data from http://localhost:3000/activities.json and the single object links?
Whilst the associations are being loaded with the use of includes, you need to specifically call the loaded association in order for it to render. Try
def index
#activities = Activity.includes(:activity_pictures).all
render json: #activities, include :activity_pictures
end
The answer above by margo was the right lead. I am using jbuilder though, so the solution was to change the file
index.json.jbuilder
as follows:
json.array! #activities do |activity|
json.title activity.title
json.activity_pictures activity.activity_pictures do |activity_picture|
json.name activity_picture.name
end
end

Rails nested form - refactor create action | cocoon gem

Everything is working fine but I want to change the code in create action to something like in update action. Right now, in the create action I am looping through all the values and saving them, but want to do it in a single line.
I have a College model.
class College< ActiveRecord::Base
has_many :staffs, dependent: :destroy
accepts_nested_attributes_for :staffs, reject_if: :all_blank, allow_destroy: true
end
And this is my Staff.rb
class Staff < ActiveRecord::Base
belongs_to :college
end
And these are my Staffs controller create and update actions
def create
#college= College.find(params[:college][:id_college_profile]
)
staff_params = params[:college][:staffs_attributes].values
staff_params.each do |staff_param|
#staff = #college.staffs.new
#staff.name = staff_param[:name]
#staff.designation = staff_param[:designation]
#staff.experience = staff_param[:experience]
#staff.specialization = staff_param[:specialization]
#staff.save
end
redirect_to dashboard_path(id: #college.id), notice: "Successfully added Staff."
end
def update
#college= College.find(params[:college][:id_college]
)
#college.update_attributes(staff_parameters)
redirect_to root_path
end
These are strong parameters
def staff_parameters
params.require(:college).permit(:id, staffs_attributes: [:id, :name, :specialization, :experience, :designation, :_destroy])
end
Is there a way to save all of staffs in create action, without looping through all the values, but save all of them with a single line of code as in update action?
I have tried this in the StaffsController create action
def create
#college= College.find(params[:college][:id_college]
)
#staff= #college.staffs.new(staff_parameters)
#staff.save
redirect_to dashboard_path(id: #college.id), notice: "Successfully added Staffs."
end
But it threw this error
unknown attribute 'staffs_attributes' for Staff.
Can someone kindly help me with this issue?
This is a CollegesController so I am assuming the create action also creates the new college?
So in that case your create action should simply be something like:
def create
#college = College.new(staff_parameters)
if #college.save
# succesfully created
else
# there was a validation error
end
end
Note that in general we would use college_parameters because the root element is college and that you not only edit the nested staff, but also possibly attributes from college.
If the college always already exists (because you are doing a find), it is a bit confusing to me what the difference is between create and update and why not always render the edit action in that case?
I have a demo-project show-casing cocoon and nested attributes.
You can do this many ways. The "staff_parameters" method threw an error because you are calling it on class Staff in the create action and on the college class for the update action. Simplest thing to do what you want is to copy the staff parameters method strong parameters and duplicate it. Name this second method create_staff and change the "params.require(:college)" part to "params.require(:staff)" and leave the rest the same. Then in your create action you can do "college.staff(create_staff)". Im on my phone so the formatting isnt good lol i put the code in quotes.

Rails 4 Not Updating Nested Attributes Via JSON

I've scoured related questions and still have a problem updating nested attributes in rails 4 through JSON returned from my AngularJS front-end.
Question: The code below outlines JSON passed from AngularJS to the Candidate model in my Rails4 app. The Candidate model has many Works, and I'm trying to update the Works model through the Candidate model. For some reason the Works model fails to update, and I'm hoping someone can point out what I'm missing. Thanks for your help.
Here's the json in the AngularJS front-end for the candidate:
{"id"=>"13", "nickname"=>"New Candidate", "works_attributes"=>[
{"title"=>"Financial Analyst", "description"=>"I did things"},
{"title"=>"Accountant", "description"=>"I did more things"}]}
Rails then translates this JSON into the following by adding the candidate header, but does not include the nested attributes under the candidate header and fails to update the works_attributes through the candidate model:
{"id"=>"13", "nickname"=>"New Candidate", "works_attributes"=>[
{"title"=>"Financial Analyst", "description"=>"I did things"},
{"title"=>"Accountant", "description"=>"I did more things"}],
"candidate"=>{"id"=>"13", "nickname"=>"New Candidate"}}
The candidate_controller.rb contains a simple update:
class CandidatesController < ApplicationController
before_filter :authenticate_user!
respond_to :json
def update
respond_with Candidate.update(params[:id], candidate_params)
end
private
def candidate_params
params.require(:candidate).permit(:nickname,
works_attributes: [:id, :title, :description])
end
end
The candidate.rb model includes the following code defining the has_many relationship with the works model:
class Candidate < ActiveRecord::Base
## Model Relationships
belongs_to :users
has_many :works, :dependent => :destroy
## Nested model attributes
accepts_nested_attributes_for :works, allow_destroy: true
## Validations
validates_presence_of :nickname
validates_uniqueness_of :user_id
end
And finally, the works.rb model defines the other side of the has_many relationship:
class Work < ActiveRecord::Base
belongs_to :candidate
end
I appreciate any help you may be able to provide as I'm sure that I'm missing something rather simple.
Thanks!
I've also been working with a JSON API between Rails and AngularJS. I used the same solution as RTPnomad, but found a way to not have to hardcode the include attributes:
class CandidatesController < ApplicationController
respond_to :json
nested_attributes_names = Candidate.nested_attributes_options.keys.map do |key|
key.to_s.concat('_attributes').to_sym
end
wrap_parameters include: Candidate.attribute_names + nested_attributes_names,
format: :json
# ...
end
Refer to this issue in Rails to see if/when they fix this problem.
Update 10/17
Pending a PR merge here: rails/rails#19254.
I figured out one way to resolve my issue based on the rails documentation at: http://edgeapi.rubyonrails.org/classes/ActionController/ParamsWrapper.html
Basically, Rails ParamsWrapper is enabled by default to wrap JSON from the front-end with a root element for consumption in Rails since AngularJS does not return data in a root wrapped element. The above documentation contains the following:
"On ActiveRecord models with no :include or :exclude option set, it will only wrap the parameters returned by the class method attribute_names."
Which means that I must explicitly include nested attributes with the following statement to ensure Rails includes all of the elements:
class CandidatesController < ApplicationController
before_filter :authenticate_user!
respond_to :json
wrap_parameters include: [:id, :nickname, :works_attributes]
...
Please add another answer to this question if there is a better way to pass JSON data between AngularJS and Rails
You can also monkey patch parameter wrapping to always include nested_attributes by putting this into eg wrap_parameters.rb initializer:
module ActionController
module ParamsWrapper
Options.class_eval do
def include
return super if #include_set
m = model
synchronize do
return super if #include_set
#include_set = true
unless super || exclude
if m.respond_to?(:attribute_names) && m.attribute_names.any?
self.include = m.attribute_names + nested_attributes_names_array_of(m)
end
end
end
end
private
# added method. by default code was equivalent to this equaling to []
def nested_attributes_names_array_of model
model.nested_attributes_options.keys.map { |nested_attribute_name|
nested_attribute_name.to_s + '_attributes'
}
end
end
end
end

Mongoid: cannot embed different classes in one array

I have Mongoid classes as follows:
class Order
include Mongoid::Document
embeds_many :animals
end
class Animal
include Mongoid::Document
embedded_in :order
def self.has_gender
field :gender, type: String
end
end
class Deer < Animal
has_gender
end
and when I call animals on any order, even empty one:
Order.new.animals
I get the following error:
undefined method `has_gender' for Deer:Class
Any ideas?
The problem is somewhere else. Your code works on my machine. (I'm using Mongoid 3.0-rc, though).
order = Order.new
order.animals << Animal.new
order.animals << Deer.new
order.save
puts Order.first.animals
# >> #<Animal:0x007fca04bae890>
# >> #<Deer:0x007fca04bb4b50>
I think that the problem is in the way I create sub-classes:
class Game
include Mongoid::Document
TYPES = {'deer' => Deer, 'pig' => Pig, 'duck' => Duck}
def self.new_of_type(type, attrs={})
TYPES[type].new attrs
end
end
because when I commented out line when I define TYPES, error disappeared, so the problem may be with calling subclasses when defining TYPES (Deer, Pig, Duck).
Any ideas for a better solution for creating sub-classes? i'm doing it this way in controller:
class GamesController < ApplicationController
def create
#game = Game.new_of_type params[:type], params[:game]
#game.save
end
end

Resources