Why doesn't mongoid add my nested attributes on new()? - ruby-on-rails

I can't seem to figure out why Mongoid won't set the nested attributes for a child object when I create a new parent. I want to create a new Folio, add one child Feature, then push it on the Folios array on Profile.
I have a Profile, which embed many Folios, which embed many Features:
class Profile
include Mongoid::Document
include Mongoid::Timestamps::Updated
#regular fields here; removed for brevity
embeds_many :folios, class_name: "Folio"
end
class Folio
include Mongoid::Document
include Mongoid::Timestamps::Updated
accepts_nested_attributes_for :features
embedded_in :profile
field :name
field :desc
field :order, type: Integer, default:0
embeds_many :features
attr_accessible :name, :desc, :order
end
class Feature
include Mongoid::Document
include Mongoid::Timestamps::Updated
embedded_in :folio
belongs_to :project
field :content_type, type: Integer #ContentType
field :content_id
field :txt, type: String
field :order, type: Integer, default:0
attr_accessible :project_id, :content_type, :content_id, :txt, :order
end
Controller:
def new
#folio = Folio.new
#folio.features.build
end
def create
#folio = Folio.new(params[:folio])
##folio.features is still empty here.
#profile.folios << #folio
#profile.save
render "create_or_update.js"
end
In create, the param hash looks good:
{"folio"=>{"id"=>"new", "name"=>"new name", "desc"=>"new description", "features_attributes"=>{"0"=>{"project_id"=>"4ea0b68e291ebb44a100000a", "content_type"=>"1", "content_id"=>"4ea0b68e291ebb44a100000d", "txt"=>"note here"}}}, "commit"=>"Save", "action"=>"create", "controller"=>"folios"}
But #folio.features is still empty.
This worked fine with AR, if I remember. Strangely, there is no features_attributes=() method on Folio. I thought that was required for the nested attributes to work? What am I missing?
This is on Rails 3.1 with Mongoid 2.2.3.

have you tried enabling AutoSave true for features in Folio document
class Folio
include Mongoid::Document
include Mongoid::Timestamps::Updated
accepts_nested_attributes_for :features , :autosave => true
embedded_in :profile
end

Related

Mongoid Polymorphic Association Rails

Work env: Rails 4.2 mongoid 5.1
Below are my models:
class Tag
include Mongoid::Document
include Mongoid::Timestamps
field :name, type: String
belongs_to :entity_tags, :polymorphic => true
end
class EntityTag
include Mongoid::Document
include Mongoid::Timestamps
field :tag_id, type: String
field :entity_id, type: String // Entity could be Look or Article
field :entity_type, type: String // Entity could be Look or Article
field :score, type: Float
end
class Look
include Mongoid::Document
include Mongoid::Timestamps
has_many :tags, :as => :entity_tags
end
class Article
include Mongoid::Document
include Mongoid::Timestamps
has_many :tags, :as => :entity_tags
end
We are trying to implement polymorphic functionality between Looks and Articles to Tags.
i.e. Let's say we have a Tag named "politics", and we would like to add the tag to an Article with the score '0.9' and to a Look with the score '0.6'. The Score should be saved at the EntityTags Model.
The problem:
The first assign of the tag works, but then when I try to assign the same tag to another entity, it removes it and reassigns it from the first one to the latter.
The assignment looks like the following:
entity.tags << tag
Does anybody know the proper way to save associations and create the EntityTag Object with the correct polymorphism and assignment properly?
Thanks!
I've managed to implement a non-elegant working solution based on the following answer in this link

Mongoid: embeds_one and _destroy param at embedded documents

I have such model.
class Article
include Mongoid::Document
embeds_many :blocks, class_name: 'Article::Block', cascade_callbacks: true
accepts_nested_attributes_for :blocks, allow_destroy: true
...
class Block
include Mongoid::Document
embedded_in :article
embeds_one :squib, class_name:'Article::Block::Squib', cascade_callbacks: true
accepts_nested_attributes_for :squib, allow_destroy: true
...
class Squib
include Mongoid::Document
...
embedded_in :block, class_name: 'Article::Block'
end
end
end
problem is about firing callbacks. When I pass to controller following params:
{"article"=>{"_id"=>"55d4c8a43a98c118b100001a", ... , "blocks_attributes"=>[{... "squib_attributes"=>{... "_destroy"=>1, "_id"=>"55d4ccb63a98c118b1000044"}, "_id"=>"55d4c8d73a98c118b100001c"}]}}
Embedded Article::Block::Squib doesn't destroys. There is no problem when I am using embeds_many relation. Problem only in embeds_one.
How to fix it?
Mongoid version 4.0.2
I found solution at mongoid sources. There is code with check for embeds_one relation:
def delete?
destroyable? && !attributes[:id].nil?
end
mongoid-4.0.2/lib/mongoid/relations/builders/nested_attributes/one.rb:82
It means that embeds_one understands only :id doc identifier,instead of embeds_many, that allow you to pass embedded documents with :_id doc identifier.
Instead of:
{"article"=>{"_id"=>"55d4c8a43a98c118b100001a", ... ,
"blocks_attributes"=>[{... "squib_attributes"=>{... "_destroy"=>1,
"_id"=>"55d4ccb63a98c118b1000044"},
"_id"=>"55d4c8d73a98c118b100001c"}]}}
you should pass
{"article"=>{"_id"=>"55d4c8a43a98c118b100001a", ... ,
"blocks_attributes"=>[{... "squib_attributes"=>{... "_destroy"=>1,
"id"=>"55d4ccb63a98c118b1000044"},
"_id"=>"55d4c8d73a98c118b100001c"}]}}
to update_attributes method.

Skip default scope for relation in mongoid

How can I skip the default scope for relations in mongoid?
The trashable concern implements a soft-delete on the model, also it adds the following
field :d_at, type: DateTime
default_scope -> { where(d_at: nil) }
If a brand gets trashed I still want to have it available when I load a product that has a reference to that brand
These are the model definitions
class Product
include Mongoid::Document
field :title, type: String
belongs_to :brand, class_name: 'Brand'
end
class Brand
include Mongoid::Document
include Concerns::Trashable
field :title, type: String
end
Example:
product = Product.find([id])
puts product.brand.inspect #This brand is soft-deleted and not fetched because of the default scope
This works, but it breaks more then it fixes
class Product
include Mongoid::Document
field :title, type: String
belongs_to :brand, class_name: 'Brand'
#unscope relation to brand
def brand
Brand.unscoped.find(self.brand_id)
end
end
According to the the fix Support unscoping default_scope in eager_loaded associations, you can skip the default scope manually by specifying the columns to be ignored in your association.
-> { unscope(where: :column_name) }
Or you may use unscoped_associations.

Mongoid embeds_many documents are not removed after reload

I have the following two models:
class Customer
include Mongoid::Document
include Mongoid::Timestamps
embeds_many :locks, class_name: "Lock"
accepts_nested_attributes_for :locks, allow_destroy: true
field :name, type: String
validates :name,
presence: true
belongs_to :list
end
and
class Lock
include Mongoid::Document
include Mongoid::Timestamps
field :locked_by, type: Moped::BSON::ObjectId
embedded_in :customer, inverse_of: :locks, class_name: "Customer"
def unlock!
self.destroy
end
end
So when I try to delete an lock the lock is removed from the child collection but after reload of customer it is still there:
locks = customer.locks.where({ some conditions})
locks.each do |l|
l.unlock!
end
customer.save
The where conditions definitely returns the correct objects.
Can somebody help me and tell me what I did wrong?
Update:
This does not work also
customer.locks = []
customer.save
customer.reload
Well, lets try.
First, delete this block
def unlock!
self.destroy
end
Then, replace
locks = customer.locks.where({ some conditions})
locks.each do |l|
l.unlock!
end
with
customer.locks.where({ some conditions}).delete_all
If still it does not work, please add this one more line after the line above
customer.locks.save

How to save embedded classes in mongoid?

I am using Rails 3 with mongoid 2. I have a mongoid class forum, which embeds_many topics.
Topics embeds_many forumposts
When I try to save a forumpost doing the following in my controller...
#forum = Forum.find(params[:forum_id])
#forum.topics.find(params[:topic_id]).forumposts.build(:topic_id => params[:forumpost][:topic_id], :content => params[:forumpost][:content], :user_id => current_user.id,:posted_at => Time.now, :created_at => Time.now, :updated_at => Time.now)
if #forum.save
On save I get...
undefined method `each' for 2012-11-14 23:15:39 UTC:Time
Why am I getting that error?
My forumpost class is as follows...
class Forumpost
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paranoia
field :content, type: String
field :topic_id, type: String
field :user_id, type: String
field :posted_at, type: DateTime
attr_accessible :content, :topic_id, :user_id, :posted_at, :created_at, :updated_at
validates :content, presence: true
validates :topic_id, presence: true
validates :user_id, presence: true
belongs_to :topic
belongs_to :user
end
There is alot wrong/wierd with your example code, so lets see if we can start at the start:
You say forum embeds many topics, which embeds many posts. But your model is using a belongs_to association. Belongs_to is used for references which are different than embedded documents. If your Topic model has this:
class Topic
...
embeds_many :forumposts
...
end
Then your Forumpost model should have this:
class Forumpost
...
embedded_in :topic
...
end
Read up on references vs embedded documents here: http://mongoid.org/en/mongoid/docs/relations.html
Next point, You don't need to put :topic_id into the forumpost since you are building it off the topic.
Next point, don't save the forum, save the forumpost. And instead of doing a build followed by a save, try just doing it as a create in one go.
Next point, instead of setting user_id => current_user.id, try setting user => current_user. This is the magic that the belongs_to association provides... its cleaner and avoids messing around with IDs.
Next point, why do you need both created_at (supplied by Mongoid::Timestamps) and posted_at ?
Last point, you shouldn't need to set the timestamps, they should be set automatically when created/updated (unless for some reason you actually need posted_at).
Try something more like this:
#forum = Forum.find(params[:forum_id])
#topic = #forum.topics.find(params[:topic_id])
if #topic.forumposts.create(:content => params[:forumpost][:content], :user => current_user)
#handle the success case
else
#handle the error case
end

Resources