nested resources and build method for making association - ruby-on-rails

I have three models associated between: User, Post, Comment. Comment is nested resource with Post.
routes.rb
resources :posts do
resources :comments
end
User model:
has_many :comments
Post model:
has_many :comments
Comment model:
belonsg_to :user
belonsg_to :post
The goal is when User makes new Comment it creates association with that user. So you can see it like User knows all comments he has made.
comments_controller.rb
def create
#post = Post.find(params[post_id]
#comment = #post.comments.build[:comment]
current_user.comments >> #comment
....
end
new.html.erb
<% form_for [#post, #post.comment.build] do |f| %>
.....
<% end %>
This gives me an error no method comments. What should I make to avoid this?

Most likely you are missing "S" letter in new.html.erb. Should be comments:
<% form_for [#post, #post.comments.build] do |f| %>
.....
<% end %>
If there is some more logic behind you didn't post let us know. Your create action looks fine. Try to look in console student_id attribute, if its populated with ID than you are fine.cheers.

Use
#post.comments.build
Instead of
#post.comment.build (x)
this should work, if possible move this line of code from view to controller
for more info
http://guides.rubyonrails.org/association_basics.html#detailed-association-reference

In new.html.erb file, you are using "s" for build method.
It should be,
<% form_for [#post, #post.comments.build] do |f| %>
.....
<% end %>

Related

Where to handle request that deal with multiple models

Example: a Survey has many Questions. A request is made to create a survey, and the request contains the survey title and metadata, plus all the questions.
This all happens on one page -- when the user clicks submit, the survey and its questions are created.
So far I have all the logic in the SurveysController but I'm not sure if this is MVC, especially because I have methods like add_question and remove_question.
Is there a preferred way of doing this?
If you're creating the questions through accepts_nested_attributes_for, then it would be okay.
--
When you mention add_question / remove question - this would be best handled in a separate questions controller (with nested resource routing):
#config/routes.rb
resources :surveys do
resources :questions, only: [:create, :destroy]
end
This allows you to use the following:
#app/controllers/surverys_controller.rb
class SurveysController < ApplicationController
def show
#survey = Survey.find params[:id]
#new_question = #survey.questions.new
end
end
#app/views/surveys/show.html.erb
<%= #survey.title %>
<% #survey.questions.each do |question| %>
<%= link_to "Remove", surveys_question_path(#survey, question), method: :delete %>
<% end %>
<%= form_for #question do |f| %>
<%= f.text_field :text %>
<%= f.submit %>
<% end %>
This would keep your controllers conventional.
Nested resources:
resources :surveys do
resources :questions # check http://localhost:3000/rails/info/routes for generated routes
end
Now you can create QuestionsController with normal CRUD actions and survey_id in params. I.e:
class QuestionsController
def create
#question = Question.new(survey_id: params[:survey_id], ... )
end
end
And to create survey with all questions at once use accepts_nested_attributes_for on Survey model http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

How to use simple form for comments from a nested resource?

So I'm creating a blog rails app and I'm trying to create a comment session on the blog. I'm trying to render a form using simple form, but I'm having a hard time getting the simple form to work. For now I have:
<%= simple_form_for ([#user, #post.comments.build]) do |f| %>
<%= f.input :comment %>
<%= f.button :submit %>
<% end %>
but it says that post.comments isn't a defined path.
My comment model:
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
end
post belongs to user and has_many comments
user has many post and has many comments
Here are my current routes:
resources :posts do
resources :comments
end
Any advice?
Thanks!
Why are you sending #user in simple_form_for?
Use #post in place of #user.
I found a fix to this by generating a migration for the comment. I just had to make sure that everything with an association actually had the columns in the database. After that I just made sure I was rendering the #post.comment instead of comment/comment. Hope this helps anyone that came across the same problem.
remove user from the form.
<%= simple_form_for [#post, #post.comments.build] do |f| %>
<%= f.input :comment %>
<%= f.button :submit %>
<% end %>
and then you will assign the user value in the controller using something like current_user if you are using devise.
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(comment_params)
#comment.user = current_user
#comment.save
redirect_to #post
end
def comment_params
params.require(:comment).permit(:comment)
end
it's a bad idea to have a model named comment and a field into it named comment. I would prefer to call it content

Couldn't find Post without an ID using rails 4

guys i am suffering from a problem from last two days and i dont know whats going wrong with my code. I have two model with the name of 'post' and 'descrip'. Descrip belongs_to post as post has_one descrip. So when i submit form for post then next form appear which is for descrip.I descrip form i have one hidden field to pass post_id. But when i submit descrip form an error appear that "POST can't be find without an id". Here below my code as
in view
<%= form_for :descrip, url:{action: "create", :controller => "descriptions"} do |f| %>
<li>
<%= f.label 'Detail' %><br>
<%= f.text_field :detail %>
</li>
<li>
<%= f.hidden_field :post_id , :value => #post.id %>
</li>
<li>
<%= f.submit %>
</li>
<% end %>
In Descriptions_controller
def new
#descrip = Descrip.new
end
def create
#post = Post.find(params[:descrip][:post_id])
#descrip = #post.descrips.build(descrip_params)
if #descrip.save
render #post
else
render 'new'
end
end
model of post and descrip
class Post < ActiveRecord::Base
has_one :descrip, :dependent => :destroy
end
class Descrip < ActiveRecord::Base
belongs_to :post
end
resources.rb
resources :descriptions
get "descriptions/new", :to => "descriptions#new", as: :descriptions_new
post "descriptions/create/:id", :to => "descriptions#create", as::descriptions_create
resources :posts do
resources :descriptions
end
Kindly suggest me what i should do.How i can solve my error and how i can get post_id in my decrips table.Please.
It seems like there are two possibilities.
1) Firstly, it could be that your params are not what you think they are and plugging in an invalid number for Post.find(). Could you share what your params looks like?
2) If that's not the case you could be calling id on a post that isn't yet saved and therefore can't be found by ActiveRecord within your database. If you're submitting this form along with a form to create a post, you're likely saving the description first. Make sure your post either exists in the database or is saved before the description to fix this.
Out of curiosity is there any reason you've made description a separate table from post? It seems like you could save yourself a lot of trouble just having it be a text column within the posts table.
in your action add logger.info:
def create
logger.info "---params ==> #{params.inspect}---"
#post = Post.find(params[:descrip][:post_id])
#descrip = #post.descrips.build(descrip_params)
if #descrip.save
render #post
else
render 'new'
end
end
run the application then in log/development.rb file you can view the all params value.
if params or not passing try with
<%= hidden_field_tag 'descrip[post_id]', #post.id %>

How do I use nested attributes with the devise model

I have the same issue as Creating an additional related model with Devise (which has no answer).
I have overridden the devise view for creating a new user and added a company name, I have changed the model to use accepts_nested_attributes_for
There are no errors, but it is not adding the nested record and I don't have a controller where I can modify the request.
I have the following (shortened to make it readable):
routes.rb
map.devise_for :users
map.resources :users, :has_many => :companies
user.rb
has_many :companies
accepts_nested_attributes_for :companies
devise :registerable ... etc
company.rb
belongs_to :user
new.html.erb
...
<% form_for resource_name, resource, :url => registration_path(resource_name) do |f| %>
...
<% f.fields_for :company do |company_form| %>
<p><%= company_form.label :name %></p>
<p><%= company_form.text_field :name %></p>
<% end %>
...
UPDATE:
I didn't add :company to the attr_accessible list in the User model.
You can add the following method to the User model:
user.rb
def with_company
self.companies.build
self
end
And modify the view:
new.html.erb
...
<% form_for [resource_name, resource.with_company], :url => registration_path(resource_name) do |f| %>
...
<% f.fields_for :company do |company_form| %>
...
<% end %>
This way, you'll have the nested form to add one company to the user.
To dynamically add multiple companies to the user, check the Railcast #197 by Ryan Bates.
Make sure you are passing the pair of resources as an array, otherwide you will get an error like this: "wrong number of arguments (3 for 2)".
You may be trying to mass assign some protected variable, OR you might not be saving a valid record. Check to make sure that the record is actually saving to the db.
I realised this is a very old thread, but since I found a better solution, hence the reply.
Just change the new.html.erb as follows,
<% form_for(resource, :as => resource_name,:url => registration_path(resource_name) do |f| %>
...
<% prefix = "user[company_attributes]"%>
<% fields_for prefix, #user.company do |company_form| %>
...
<% end %>
<% end %>
This way when #user.save gets invoked, it would run company.save too with all the validations you may have in company model.
I don't have whole lot of RoR experience, but I think this is a better solution. What do you think?

Rails map.resources & Helper Urls

I have two models like this:
class Topic < ActiveRecord::Base
has_many :articles
end
class Article < ActiveRecord::Base
belongs_to :user
belongs_to :topic
has_many :comments
end
I have setup the resource mapping like so:
map.resources :topics do |topic|
topic.resources :articles
end
And I can view the articles just fine when I call the appropriate URL (e.g. /:topic_slug/articles/2). In my article's views, I use a partial to handle the creation and editing of the articles, like this:
<% form_for(#article) do |f| %>
...
<% end %>
The problem arrises wen I try to either create a new article or edit an existing one I get the following error:
NoMethodError in Articles#new
Showing app/views/articles/_form.html.erb where line #1 raised:
undefined method `articles_path' for #<ActionView::Base:0x103d11dd0>
Extracted source (around line #1):
1: <% form_for(#article) do |f| %>
2: <%= f.error_messages %>
3:
4: <p>
Trace of template inclusion: app/views/articles/new.html.erb
Does anyone know where I am going wrong and what I am missing?
You need to pass topic also:
<% form_for([#topic, #article]) do |f| %>
When you pass only #article to form_for than it tries to generate correct path based on #article - that's why your error says that you don't have article_path method. When you pass [#topic, #article] to form_for than it will quess that you want to call topic_article_path.
If you don't have any #topic on creating new article, then you probably need to specify new route that accept article without topic, so add:
map.resources :articles
And then:
<% form_for(#article) do |f| %>
will work, but it will generate url like: /articles/3 - without topic part.
If you want a non-nested route for articles, map the articles separately as well:
map.resources :articles

Resources