Mongoid creating embedded document from a view - ruby-on-rails

I am trying to add a embed a Profile into a User and I keep getting this error.
Access to the collection for Profile is not allowed since it is an embedded document, please access a collection from the root document.
I am sure it's a simple problem to fix but I have no clue how to do it. I am very new with RoR so things are still a little confusing. Here is my code.
Models/Profile
class Profile
include Mongoid::Document
attr_accessible :handle, :description
field :handle
field :description
embedded_in :user
end
Controllers/Profile
class ProfileController < ApplicationController
def create
#user = current_user
#profile = #user.profile.create!(params[:profile])
redirect_to dashboard_path
end
end
Views/profile/new
<h1>Create Profile</h1>
<%= form_for [:current_user, Profile.create] do |f| %>
<div class="field">
<%= f.label :handle %>
<%= f.text_field :handle %>
</div>
<div class="field">
<%= f.label :description %>
<%= f.text_area:description %>
</div>
<p class="button"><%= f.submit %></p>
<% end %>

You cannot use Profile.create in your views.html.erb because Profile is embedded in a user. So you need to do something like current_user.build_profile
<%= form_for [:current_user, current_user.build_profile] do |f| %>
should work

try
#user = current_user
#profile = Profile.new(params[:profile])
#user.profile = #profile
#user.save
# or #profile.save

Related

has_many join form with collection checkboxes not saving more than one checkbox value

I am working on a form for a editorial calendar app. I have two things going out that are pretty similar and not working.
Working with 3 models: Platforms, Posts and Calendars. They are join tables. Platform <=> Post, Post <=> Calendars
Post/new & Post/edit form:
<div class="container">
<div class="form-field">
<%= form_for #post do |f| %>
<%= f.label :title %>
<%= f.text_field :title, required: true %> <br>
Title is required.
</div>
<div class="form-field">
<%= f.label :content%>
<%= f.text_area :content%>
</div>
<div class="form-field">
<%= f.label :link %>
<%= f.text_field :link %>
</div>
<div class="file-field">
<%= f.label :picture%>
<%= f.file_field :picture, id: :post_picture%>
</div>
<div class="file-field">
<%= f.label :finalized %>
<%= f.radio_button :finalized , true%>
<%= f.label :finalized, "Yes" %>
<%= f.radio_button :finalized, false %>
<%= f.label :finalized, "No" %>
</div>
<%= f.hidden_field :user_id %> <br>
<div class="form-field">
<%= f.fields_for :platform_attributes do |platform| %>
<%= platform.label :platform, "Social Platforms"%>
<%= platform.collection_check_boxes :platform_ids, Platform.all, :id, :name %> <br> <br>
</div>
<div>
<h4> Or Create a new platform: </h4>
<%= platform.label :platform, 'New Platform'%>
<%= platform.text_field :name%> <br> <br>
</div>
<% end %>
<%= f.submit%>
<% end %>
</div>
My post controller is handling the checkboxes issue, and the "schedule post" issue. It will only allow me to schedule for one calendar, and it does not save the updates and add additional calendars.
Posts Controller:
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :schedule_post, :destroy]
def new
#posts = current_user.posts.select {|p| p.persisted?}
#post = current_user.posts.build
#platforms = Platform.all
end
def edit
#calendars = current_user.calendars
#platforms = Platform.all
end
def create
#post = current_user.posts.build(post_params)
if #post.save
redirect_to post_path(#post)
else
redirect_to new_post_path
end
end
def update
#post.update(post_params)
if #post.save
redirect_to post_path(#post), notice: 'Your post has been updated.'
else
redirect_to edit_post_path(#post)
end
end
def schedule_post
#calendar_post = CalendarPost.new(calendar_post_params)
if #calendar_post.save
binding.pry
redirect_to post_path(#post)
else
render 'show'
end
end
private
def set_post
#post = Post.find(params[:id])
end
def set_calendars
#calendars = current_user.calendars
end
def post_params
params.require(:post).permit(:title, :content, :link, :finalized, :picture, :user_id, :platform_attributes => [:platform_ids, :name])
end
def calendar_post_params
params.require(:calendar_post).permit(:post_id, :calendar_id, :date, :time)
end
end
I want the user to be able to add a post to multiple platforms and multiple calendars because of the versatility of what someone may need.
I also have my setter in my Post model.
class Post < ApplicationRecord
has_many :calendar_posts
has_many :calendars, through: :calendar_posts
has_many :platform_posts
has_many :platforms, through: :platform_posts
belongs_to :user
def platform_attributes=(platform_attributes)
if platform_attributes['platform_ids']
platform_attributes.platform_ids.each do |id|
platform = Platform.find(id: id)
self.platforms << platform
end
end
if platform_attributes['name'] != ""
platform = Platform.find_or_create_by(name: platform_attributes['name'])
self.platforms << platform
end
end
thoughts? why are they not saving to more than one calendar or more than one platform if they choose to have more than one?
Here is the updated code... and more of what I know about these changes and what is happening.
My submit button is not working for some odd reason on my form, so I'm trying to get the params submitted but it won't even route to give me params even if I raise them, nothing is happening.
On the form you can choose checkboxes or add in a platform. If you add in a platform it creates that one but it does not also save the other ones you selected. If you go to edit the post, and click submit with changes, no page loads at all and nothing is happening in log. It's just idle.
<%= f.fields_for :platform_attributes do |platform| %>
assumes you are creating one platform... it says "these are the fields for this platform"
but platform_ids is intended to be a selection of a set of platforms... and probably should be outside of the fields_for section (which should only surround the name field).
try something like the following:
<div class="form-field">
<%= f.label :platform_ids, "Social Platforms"%>
<%= f.collection_check_boxes :platform_ids, Platform.all, :id, :name %> <br> <br>
</div>
<div>
<%= f.fields_for :platform_attributes do |platform| %>
<h4> Or Create a new platform: </h4>
<%= platform.label :name, 'New Platform'%>
<%= platform.text_field :name%> <br> <br>
<% end %>
<%# end fields_for %>
</div>
Also you'll need to update permit/require appropriately eg
def post_params
params.require(:post).permit(:title, :content, :link, :finalized, :picture, :user_id, :platform_ids, :platform_attributes => [:name])
end
Note: not tested - bugs are left as an exercise for the reader ;)

Display fields before dynamically adding with cocoon gem

I have a form and i am using cocoon gem to add extra fields if the user requires. As it stands, its displays add educations link and then the fields appear, I would like to have the fields present in the form and then if required, users clicks add education link to render the fields. Other than that its all working fine, the models are all set up properly, but can't figure this out.
new.html.erb
<%= form_for #profile do |f| %>
<%= f.label :bio %>
<%= f.text_area :bio %>
<%= f.label :university %>
<%= f.text_field :university %> <br>
<%= f.label :course %>
<%= f.text_field :course %> <br>
<%= f.label :finishedDate %>
<%= f.text_field :finishedDate %> <br>
<div id="educations">
<%= f.fields_for :educations do |education| %>
<% render 'education_fields', f: education %>
<% end %>
<div class="links">
<%= link_to_add_association 'add education', f, :educations %>
</div>
</div>
<%= f.button :submit %>
<% end %>
_education_fields.html.erb
<div class="nested-fields">
<%= f.label :university %>
<%= f.text_field :university %> <br>
<%= f.label :course %>
<%= f.text_field :course %> <br>
<%= f.label :finishedDate %>
<%= f.text_field :finishedDate %> <br>
<%= link_to_remove_association "remove education", f %>
</div>
profiles_controller.rb
class ProfilesController < ApplicationController
def index
#profiles = Profile.all
end
def new
#profile = Profile.new
#profile = educations.build
end
def create
#profile = Profile.create(profile_params)
redirect_to profiles_path
end
def show
#profile = Profile.find(params[:id])
end
def edit
#profile = Profile.find(params[:id])
end
def update
#profile = Profile.find(params[:id])
#profile.update(profile_params)
redirect_to(profiles_path(#profile))
end
private
def profile_params
params.require(:profile).permit(:bio, educations_attributes:[:id, :university, :course, :finishedDate, :destroy])
end
end
The form shows a profile, with all it's existing educations. So if you want to show the fields for an education by default, add an education before rendering the form.
So, in your controller, where you do something like
#profile = Profile.new
you can build an initial education so it will show up in the form:
#profile = Profile.new
#profile.educations.build

undefined method `reviews' for nil:NilClass

I'm trying to create a reviews model for company pages. For this I have:
Models
user.rb
has_many :reviews
class Review < ActiveRecord::Base
belongs_to :user
belongs_to :company
end
class Company < ActiveRecord::Base
has_many :reviews
end
My reviews controller is:
def create
#company = Company.find_by_slug(params[:id])
#review = #company.reviews.create(params[:review])
#review.save
redirect_to company_path(#company)
end
and I have this code in the company show page:
<% #company.reviews.each do |review| %>
<p>
<strong>Title:</strong>
<%= review.title %>
</p>
<p>
<strong>Avantage:</strong>
<%= review.avantage %>
</p>
<p>
<strong>Inconvenient:</strong>
<%= review.inconvenient %>
</p>
<% end %>
</br>
<%= form_for([#company, #company.reviews.build]) do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :avantage %><br>
<%= f.text_area :avantage %>
</div>
<div class="field">
<%= f.label :inconvenient %><br>
<%= f.text_area :inconvenient %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
However, when I go to a specific company page and try to create a review for this company I'm getting this error message undefined method reviewsfor nil:NilClass
Instead of #company = Company.find_by_slug(params[:id]) use this code #company = Company.friendly.find(params[:company_id])
There are a couple of things you may find useful:
If you're using Rails 4, you may encounter a further problem. In the third line of your create method, you are using unsecure params directly in a .create call. Check out the Rails Guide page on "strong params".
If you implement strong parameters as mentioned above, you should probably deliberately omit the company_id field from the list of permitted params.
Assuming your users are allowed to write a review for any company in your system, it might be simpler for you to embed the company_id as a hidden field in your form. This would allow you to also simplify the controller method. For example:
# _form.html.erb
<%= form_for(#review) do |f| %>
<%= f.hidden_field :company_id, value: #company.id %>
...bla bla bla
<% end %>
Then, in your reviews_controller...
# reviews_controller.rb
def create
#review = Review.new(approved_params)
if #review.save
flash[:success] = 'Review created!'
else
flash[:error] = "Review wasn't saved"
end
#company = #review.company
redirect_to #company
end
def approved_params
params.require(:review).permit(:title, :avantage, :inconvenient, :company_id)
end
In your companies_controller, you should add this to your show method
# companies_controller.rb
def show
#company = Company.find(params[:id]
# add this line below...
#review = Review.new
end
I hope this helps.

params.require().permit does not work as expected

I have this controller
class PeopleController < ApplicationController
def new
#person = Person.new
#person.phones.new
end
# this is the action that gets called by the form
def create
render text: person_params.inspect
# #person = Person.new(person_params)
# #person.save
# redirect_to people_path
end
def index
#person = Person.all
end
private
def person_params
params.require(:person).permit(:name, phones_attributes: [ :id, :phone_number ])
end
end
and this view
<%= form_for :person, url: people_path do |f| %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :phones do |f_phone| %>
<div class="field">
<p>
<%= f_phone.label :phone_number %><br />
<%= f_phone.text_field :phone_number %>
</p>
</div>
<% end %>
<p>
<%= f.submit %>
</p>
<% end %>
When I fill out both form fields and hit "Save Person" I only get {"name"=>"foo"} - the phone number seems to vanish.
However, when I change phones_attributes to phones I get {"name"=>"foo", "phones"=>{"phone_number"=>"123"}} (this would however cause problems with the create function.
What's wrong here?
Please note that this question is strongly related to that one: accepts_nested_attributes_for: What am I doing wrong as well as to this posting: https://groups.google.com/forum/#!topic/rubyonrails-talk/4RF_CFChua0
You don't have #phones defined in the controller:
def new
#person = Person.new
#phones = #person.phones.new
end
Finally found the problem. In the view there should be
<%= form_for #person, url: people_path do |f| %>
Instead of
<%= form_for :person, url: people_path do |f| %>
#phron said that already here:
accepts_nested_attributes_for: What am I doing wrong

NoMethodError with deeply nested models

I am not sure if I'm structuring my application corretly (I've been learning Rails for 2 months now) but I am building a pretty deeply nested application that looks like this:
user has_many accounts > accounts has_many characters > characters has_many items
So it's 4 levels deep (that's the plan at least).
I'm currently at characters and I'm having trouble creating the form which is throwing up this error: undefined method 'characters' for nil:NilClass (screenshot).
Here's the project on github: https://github.com/imjp/d2shed
characters_controller.rb
class CharactersController < ApplicationController
def create
#user = User.find(params[:user_id])
#account = Account.find(params[:account_id])
#character = #account.characters.create(params[:character])
redirect_to root_url
end
end
character.rb
class Character < ActiveRecord::Base
attr_accessible :name, :type
belongs_to :account
end
_form.html.erb
<%= form_for([#account, #account.characters.build]) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.radio_button(:type, "SC") %>
<%= f.label(:type, "SC") %>
<%= f.radio_button(:type, "HC") %>
<%= f.label(:type, "HC") %>
<%= f.radio_button(:type, "SCL") %>
<%= f.label(:type, "SCL") %>
<%= f.radio_button(:type, "HCL") %>
<%= f.label(:type, "HCL") %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You get this error because you don't define #account in the users controller in the show action,
class UsersController < ApplicationController
...
def show
#user = User.find(params[:id])
#account = #user.accounts.first # Otherwise #account == nil
...
end
...
end
Also, the route in your form don't look right.
The create action for the Character resource is like this in the routes:
POST /:user_id/accounts/:account_id/characters
So you need to provide, :user_id, :account_id, and character
like this:
<%= form_for [#user, #account, #account.characters.build] %>

Resources