Accepts nested attributes convention for mongoid - ruby-on-rails

I'm trying to create a form with nested attributes using mongoid. My models have the following code:
def Company
field :name
has_many :users, autosave: true, dependent: :destroy
accepts_nested_attributes_for :users
end
def User
belongs_to :company
has_one :profile
end
def Profile
belongs_to :user
end
The params that are returned from the form are in the following order:
"company"=>
{"users_attributes"=>
{"0"=>
{"profile_attributes"=>
{"first_name"=>"123123abcd123", "last_name"=>"abcd123123123"},
"email"=>"abcd#abcd.com123123123",
"password"=>"123123123123",
"password_confirmation"=>"123123123123"}},
"name"=>"abcd123123123",
"subdomain"=>"abcd123123123"}
Calling Company.create(params[:company]) seems to work, however it is not properly creating the user object. When I do company.users I can see that object, BUT when I do User.find, that document is not available. Reading the documentations I realized that the params should be passed in the following way:
"company"=>
{"users_attributes"=>
[{"profile_attributes"=>
{"first_name"=>"123123123", "last_name"=>"123123123"},
"email"=>"testin321#gmail.com",
"password"=>"123123",
"password_confirmation"=>"123123"}],
"name"=>"abcd123123123",
"subdomain"=>"abcd123123123"}
Note the subtle difference of using an array for users_attributes instead of a hash. This works right, but then it doesn't seem quite out of the box like it is with Active Record (and how it should be in something like in rails). I don't want to take the params hash and modify the data to make it follow certain conventions. Is there a better way, am I missing something?

if you can name the inputs as user_attributes[] that would make the array.
So instead of having user_attributes[0][profile_attributes] (I think you have something like this)
Make it to have user_attributes[][profile_attributes]

Could you post the code for the form? Then we can work on getting down to the reason it's formatted a certain way. (This should be a comment, but I am will to provide an answer once there are more details regarding the question.)
On a side note from your issue with the form in the view. I notice you are trying to create a company and it's nested users and the user and it's nested profile attributes as well. If you expect user to accept nested attributes for profile than you need to put that in the User model.
def User
belongs_to :company
has_one :profile, dependent: destroy, autosave: true
accepts_nested_attributes_for :profile
end
That may solve your problem, the error might arise from the User trying to mass-assign profile attributes without explicit instructions to do so.

Related

Rails has_many associations are not saved when only attributes are changed

I have two models, Profile and Skill, where a profile has_many skills.
I am using Rails as an API and passing an array of skill objects.
I'm doing #profile.skills = #skills in the controller, where #skills is my data from the frontend.
Whenever I delete or add a new skill, the above works as expected - also, #profile.skills.replace(#skills) works just the same.
The associated object gets deleted or created in the database as well. All as expected.
However, if I only change one or more attributes on an already existing skill, the changes are not saved to the database.
If I log #profile.skills after the above line of code, it seems like the changes expected are present.
But it does not get saved to the database and on the next request the changes are obviously not present.
What am I doing wrong?
Have you tried to pass like this:
class Profile < ApplicationRecord
has_many :skills
accepts_nested_attributes_for :skills
end
or
class Profile < ApplicationRecord
has_many :skills, autosave: true
end
after that, edit the same attribute of the same record and save
#profile.skills.same.same_attr = new_val
#profile.save
accepts_nested_attributes_for - it also adds autosave to asociations, and a lot of other things, you can see more in the documentation and source code
https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
a little bit about how autosave works for associations
https://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html

Nested attributes active record generating a "0" as a Hash key?

Where is this "0" coming from in owners_attributes? Everything is working but this "0" key seems oddly out of place.. Is it normal/bug/or some setting? Leave it alone or remove it? Thanks!
Parameters: {"utf8"=>"✓", "shorturl"=>{"redirect"=>"http://www.test.com",
"owners_attributes"=>{"0"=>{"email"=>"adm#test.com"}}}...
Strong_params:
def shorturl_params
params.require(:shorturl).permit(:redirect, owners_attributes: [:email])
end
model:
class Shorturl < ActiveRecord::Base
has_many :campaigns
has_many :owners, through: :campaigns
accepts_nested_attributes_for :owners
...
That's the internal format for how these are ported over via HTTP parameters. It allows multiple sets of nested attributes to be included.
It does look a little odd, but that shouldn't be a concern. The nested attribute handler will know what to do with it. It's only ever an issue if you need to manipulate these before they're intercepted by the default handler, but that's best avoided.

How can I design DB system while I want to change the ownership of records to the others when record is deleted?

Assuming I have these 4 models.
Then I'm using the gem called acts_as_paranoid for each model to implement logical deletion.
User
Community
Topic
Comment
User can resign anytime he wants. It means User's record will be deleted.
In general situation, communities, topics, and comments that are created by the user, should be also deleted together. (w/ dependant => destroy )
However, I don't want that. Because the other User might have added the community to his bookmark list. So for this reason they shouldn't be deleted.
When supposing that the user record was deleted but all those communities, topics and comments were remained, it starts returning nil error at the community page or wherever which was made by the user.
I'm coding like just this now.
It's gonna be nil everywhere since the user record is gone but all the records remain.
How can I handle this kind of problem?
views/communities/show.html.erb
<%= #community.user.username %>
What I want to do is, replacing the username displayed with this fixed word "Not Found User". Then possibly I'd just change the ownership(user_id) of community to the other User so that he can manage this community instead.
My association is just like this.
models/user.rb
has_many :communities
has_many :topics
has_many :comments
models/community.rb
belongs_to :user
has_many :topics
has_many :comments
models/topic.rb
belongs_to :user
belongs_to :community
has_many :comments
models/comment.rb
belongs_to :user
belongs_to :community
belongs_to :topic
I think the best way to handle this would be to refrain from deleting but put code in your display logic to handle a user that has been deleted. If you stick with acts_as_paranoid this would work fine, what I would do is use a helper method for username such as:
def community_username(community)
user = User.with_deleted.find(community.user_id)
if user.deleted_at.blank?
return user.username
end
"[deleted]"
end
You can put this in your appropriate helper or application helper and call it in your view like
<%= community_username(#community) %>
and it will display their username, or [deleted] if it has been deleted.
Note the above code is off the top of my head, you may need to adjust slightly if I'm forgetting acts_as_paranoids methods...

How to specify fields to set for accepts_nested_attributes_for in ActiveRecord

I have something like:
class Profile < ActiveRecord::Base
belongs_to :user
delegate :full_name, :to => :user
accepts_nested_attributes_for :user
.......
This work fine as I want profile to be able to set first_name and last_name in user. But this poses security threat if user injects other parameters in the form.
How to make accepts_nested_attributes_for only takes first_name and last_name and drop other paramters?
There are two options that I can think of and they are actually the same way you would handle the mass assignment without going through anaf.
accepts_nested_attributes_for will respect attrs_accessible on the user model. If you specify that an attribute is not exposed to mass assignment on the user model, it will not be able to be assigned through accepts_nested_attributes_for either.
You can handle the sanitation in your profiles controller. Attributes that will be passed to the user model will come in under params[:profile][:user_attributes]. You can then either use slice to only get the attributes you want to allow, or use except to delete the attributes you don't want to allow. Though I prefer to whitelist allowed attributes vs blacklisting.
Option 1 would affect anywhere you are using mass assignment for the user model, while option 2 would only affect parameters coming in through the profiles controller

Delete Rails model and its associations in one action? NEWBIE question

I have a really simple association with the Devise user object where each user has one Profile (with more application specific stuff...) I am having no issues creating the User object and accessing the user and its profile object. i.e.,
#user.profile
However, I'm having an issues when I try to delete the profile object - I'd assume that when I delete the User object, it would also delete each associated object. The association in my User object is like so
accepts_nested_attributes_for :profile, :allow_destroy => true
The has_one and belongs_to associations are set on both the User and Profile objects. Maybe the issues is in Devise code - I'm stumped. An idea what I'm missing here.
You need to specify :dependent on the association:
has_one :profile, :dependent => :destroy
Look Association for more information.

Resources