There are many questions already on this topic but I cannot seem to find anything that works. Using this railscast, I'm trying to sort a list of questions using jquery-ui but like this question my nested resources are confusing things.
I have three models: posts, comments and questions.
Post.rb:
class Post < ActiveRecord::Base
has_many :comments
has_many :questions, :through :comments
end
Comment.rb
class Comment < ActiveRecord::Base
belongs_to :post
has_many :questions
end
Question.rb
class Question < ActiveRecord::Base
belongs_to :comment
end
The list of questions I'd like to sort is on the ordered_path view (posts/:id/ordered). Here is the posts controller:
Posts_controller.rb
def ordered
#post = Post.friendly.find(params[:id])
#ordered = #post.questions.where(:hide => true).where(:recommend => true).order("position")
end
and questions_controller.rb:
def sort
params[:question].each_with_index do |id, index|
Question.update_all({position: index+1}, {id: id})
end
render nothing: true
end
I believe I've followed the railscast correctly. I've added a 'position' column to questions. I added this to the routes:
routes.rb
resources :comments do
resources :questions do
collection { post :sort }
end
end
And in my view I have this
posts/ordered.html.erb
<ul id="questions" data-update-url="<%= sort_comment_questions_path %>">
<% #ordered.each do |question| %>
<%= content_tag_for :li, question do %>
<span class="handle">[drag]</span>
<%= question.body %>
<% end %>
<% end %>
</ul>
Lastly, posts.js.coffee:
jQuery ->
$('#questions').sortable
axis: 'y'
handle: '.handle'
update: ->
$.post($(this).data('update-url'), $(this).sortable('serialize'))
My problem(s) is that I am unsure what to pass into the data-update-url (to get rid of the 'no route matches' error) or if that's even the correct path in the first place.
First thing in your code, change the line
#ordered = #post.questions.where(:hide => true).where(:recommend => true).order("position")
to
#ordered = #post.questions.where(:hide => true, :recommend => true).order("position")
As you typically only want one where() call if you can help it. Sometimes you will need to add one conditionally which is fine. In an if block for example.
As far as your route error, run rake routes in terminal, and you will see output of all the route methods, the params they accept, the HTTP method, and what controller#action it hits.
The important thing to note about nested resources, is the nested resource is applied to a "member" of the parent. So in your case what your two resources blocks are generating are:
GET /comments/:comment_id/questions questions#index
GET /comments/:comment_id/questions/:id questions#show
POST /comments/:comment_id/questions/sort questions#sort
So in your erb tags in the data attribute, you need to add the comment to it:
<ul id="questions" data-update-url="<%= sort_comment_questions_path(#comment) %>">
The problem though is that you are using this at the post model level, which has many comments. So what you probably want is this:
resources :comments do
resources :questions
end
resources :posts do
member do
post "sort" => "questions#sort", :as => "sort_questions"
end
end
Then in your view:
<ul id="questions" data-update-url="<%= sort_questions_post_path(#post) %>">
Related
I'm new in the world of rails developers. Please, help me to understand.
I've 3 tables:
Calls, Questions, Results
Calls is:
id, name, date
Questions is:
id, question
Results is:
id, call_id, question_id, result
I've read the Rails manual, as i understand i've created 3 models.
In my model Call.rb
I've done next relationship:
has_many :results
has_many :question, through: :results
My Result.rb
belongs_to :call
belongs_to :question
My Question.rb
has_many :result
So, there are can be many records in the table "results" with one call_id, and it's can be one relation with question through results table
If if try to launch code like this:
#calls = Call.all
Than on my view:
<% #calls.each do |call| %>
<%= call.result.result %>
<% end %>
i've error that "result is undefined method". But it's must be a property.
What i do wrong?
Thanks!
According to your schema, your associations should look like this
class Call < ActiveRecord::Base
has_many :questions
has_many :results
end
class Question < ActiveRecord::Base
belongs_to :call
end
class Result < ActiveRecord::Base
belongs_to :call
end
So in the view,
<% #calls.each do |call| %>
<% call.results.each do |result| %>
<%= result.result%>
<% end %>
<% end %>
A few things.
First, you need to fix your associations so the plural and singular tenses match. has_many :result does not work as Marcelo points out.
Second, you need to ensure that your tables actually have the correct id's to make the associations work. Use the rails console to inspect Result. From your question info, it should have attributes for call_id and question_id. Once you've confirmed this, create a few objects in the console and test your associations.
#call = Call.create(name: "test", date: Time.now)
#result = Result.create(call_id: #call.id, result: "some result")
Then
#call.result # should yield the Result record you just created
Lastly, you need to rename the result attribute for Result. That's super confusing and will only cause problems.
The first thing I notice is that your call should have many questions, and many results through questions. That's because calls own questions, which in turn own results themselves.
class Call < ActiveRecord::Base
has_many :questions
has_many :results, through: :questions
end
You didn't need call_id in Result class. But, if you wish to keep it there, you dont need through: :questions in your call class (given there is a direct relation between them)
In your Question class, I assume it is a typo, but it should be plural
has_many :results
Having said that, your loop through calls will bring results (plural) and not result (singular) given that a call may have many results. Therefore:
<% #calls.each do |call| %>
<% call.results.each do |result| %>
<%= call.result %>
<% end %>
<% end %>
In my Rails 3.2 app, I have User, Comment, and Article models. In a view I'm trying to show all of a User's comments ordered by the article date (comments belong to articles). The problem is that in my view, when I try to retrieve the comment id, I get the article id.
***Models***
# user.rb
has_many :comments
# comment.rb
belongs_to :user
belongs_to :article
scope :by_article_date,
select("*, articles.date as article_date").
joins(:article).
order("article_date DESC")
# article.rb
has_many :comments
# users_controller
def comments
#user = current_user
#comments = #user.comments.by_article_date
end
# comments.html.erb
<% #comments.each do |comment| %>
<%= comment.id %> # shows the article id, but should be comment.id
<%= comment.article.id %> # shows article id correctly
<% end %>
Can someone please help me figure out what's going on?
Try this in your scope:
scope :by_article_date, -> { joins(:article).order("articles.date DESC") }
I have been working with rails for a while but I have yet to overcome the problem of using 3 level deep nested resources. When I am on the notes page I would like to link course name to the class and course but rails keeps giving me an error.
I have 3 models class, course, and notes. A class has many courses and courses belong to a class. Course has many notes and note belong to a course. I will explain below.
class.rb
has_many :courses
course.rb
belongs_to :class
has_many :schedules
has_many :notes, :through => :schedules
note.rb
has_many :schedules
has_many :courses, :through => :schedules
schedule.rb
belongs_to :course
belongs_to :note
routes.rb
resources :classes, :shallow => true do
resources :courses do
resources :notes
end
end
index.html.erb
<% #notes.each do |note| %>
<% note.courses.each do |course| %>
<%= note_class(course) %>
<% end %>
<% end %>
notes_helper.rb
def note_class(course)
link_to course.course_name, class_course_path(class, course)
end
Shallow routes works great except when rails give me an error 'undefined local variable or method `class' for'. I think my code above is right but I am not sure why it is not working correctly. Any suggestions on how I can get the course to link to a url like so mysite.com/classes/1/course/3?
I realize this question was asked a while back but I figured since it hasn't been 'answered', I'll give it a go.
Couple of things. First, the error you're getting inside the helper method is a result of not having a reference to the class instance. You're only passing the course object as a parameter. This would work given your associations above
notes_helper.rb
def note_class(course)
link_to course.course_name, class_course_path(course.class, course)
end
Second, as mentioned in one of the comments, class is a reserved keyword in Ruby so it would serve you best to avoid using it for your models and associations. Cheers.
Foreword (because it is my first rails post)
I love rails but currently it drives me crazy!
I played around with rails 2.3 two years ago. Now I started with rails 3.1. All just for fun. Rails is the best and most professional I've ever seen in web development.
But... Since one line of code has so much power it is pretty hard to learn it!!!
The rail magic is hard to get :-)
Problem
I've got a nested models and routs. One page has_many articles.
I render pages/show.html.haml and in the page I list all articles. Each article shall have buttom underneath to 'new','edit' and 'destroy'.
render #articles
Here is my views/articles/_article.html.haml
%li.article_list{:id=>"a#{article.id}"}
.article
.article_head
%h2
=article.title:class => "icon"), '#'
.clear
.article_content
= raw parse_content(article.content)
-if admin?
#article_menu
%ul
%li= link_to icon_new + 'New...', new_page_article_path(#page)
%li=# link_to icon_edit + 'Edit', edit_page_article_path(WHAT SHALL BE IN HERE???)
%li= link_to icon_destroy + 'Destroy', [article.page, article], :confirm => "#{article.title}\n\nAre you sure?", :method => :delete
My problem is this line
%li=# link_to icon_edit + 'Edit', edit_page_article_path(WHAT SHALL BE IN HERE???)
I want to edit the article but I don't get the clue how to! I've tried and googled for hours!
In general I've been following this nice guide: http://guides.rubyonrails.org/getting_started.html#associating-models
Additional information
models/article.rb
class Article < ActiveRecord::Base
attr_accessible :title, :content, :publish_at, :fan_only
belongs_to :page
validates :content, :presence => true
validates :page_id, :presence => true
default_scope :order => 'articles.created_at DESC'
end
models/page.rb
class Page < ActiveRecord::Base
attr_accessible :short_name, :title, :content, :fan_only
has_many :articles, :dependent => :destroy
end
routes.rb
resources :pages do
resources :articles
end
part of the articles_controller.rb
# GET /articles/1/edit
def edit
#page = Page.find(params[:page_id])
#article = #page.articles.find(params[:id])
end
rake routes should show you all the paths, and give you a hint as to what goes in there.
Based in the code you posted, the answer to what goes in there is that is should be a reference to a PageArticle, a Page or an Article - I'm not sure which it is, because I don't know what your models are.
With two models, Page and Article, and nested resources, then you should have a nested route in your routes.rb file that looks something like:
resources :pages do
resources :articles
end
...which sets up a nested route to /pages/:page_id/articles/:id/edit
...and which looks like edit_page_article_path(#page, #article) in your app
More detail on routing+nested resources: http://edgeguides.rubyonrails.org/routing.html#nested-resources
I am following Ryan Bate's tutorial: http://railscasts.com/episodes/163-self-referential-association
But my setup is slightly different.
I am making comments that are self-referential so that comments can be commented on.
The form displays in the view, but when I submit, I get this :
Routing Error
No route matches "/conversations"
And my url says this : http://localhost:3000/conversations?convo_id=1
models
#conversation.rb
belongs_to :comment
belongs_to :convo, :class_name => "Comment"
#comment.rb
belongs_to :post
has_many :conversations
has_many :convos, :through => :conversations
My form :
- for comment in #comments
.grid_7.post.alpha.omega
= comment.text
%br/
- form_for comment, :url => conversations_path(:convo_id => comment), :method => :post do |f|
= f.label 'Comment'
%br/
= f.text_area :text
%br/
= f.submit 'Submit'
My conversations_controller:
def create
#conversation = comment.conversations.build(:convo_id => params[:convo_id])
The app fails here in its creation as it never makes it to the redirect portion of the create method.
There are several pieces to work on here, but the good news is I think the answer you're looking for is simpler than what you already have. If I understand you correctly, you want comments to have many child comments of their own. This is how YouTube works, letting members reply to existing comments. For this, you don't need the has_many :through solution you've implemented. You don't need the conversations object at all. A comment may have many replies (child comments), but a reply isn't going to have more than one parent.
The answer for this is using polymorphism, which is easier to implement than it is to pronounce :) You want your comments to either belong to a post, or to another comment. Polymorphism lets an object belong to one of possibly many things. In fact, comments are the most common use for this.
I cover polymorphism with the example of addresses in this blog post:
http://kconrails.com/2010/10/19/common-addresses-using-polymorphism-and-nested-attributes-in-rails/
But I can show you how it applies to your case more specifically. First, drop the conversation model/controller/routes entirely. Then, change your comments table:
change_table :comments do |t|
t.integer :commentable_id
t.string :commentable_type
t.remove :post_id
end
We don't need post_id anymore, because we're going to change how we associate with other tables. Now let's change the models:
# app/models/post.rb
has_many :comments, :as => :commentable
# app/models/comment.rb
belongs_to :commentable, :polymorphic => true
has_many :comments, :as => :commentable
Notice we dropped the comment belonging to a post directly. Instead it connects to the polymorphic "commentable" association. Now you have an unlimited depth to comments having comments.
Now in your Post#show action, you'll want to create a blank comment like so:
get show
#post = Post.find(params[:id])
#comment = #post.comments.build
end
#comment will now have commentable_id and commentable_type set for you, automatically. Now in your show page, using erb:
<% form_for #comment do |f| %>
<%= f.hidden_field :commentable_type %>
<%= f.hidden_field :commentable_id %>
/* other fields go here */
<% end %>
Now when Comments#create is called, it works like you'd expect, and attaches to the right parent. The example above was showing a comment being added directly to a post, but the process is essentially the same for a comment. In the controller you'd call #comment.comments.build, and the form itself would stay the same.
I hope this helps!
The app is failing sooner than you think - it's not finding a route to that action, so it's not reaching it at all. In your routes.rb file, you need to add:
# rails 3
resources :conversations
# rails 2
map.resources :conversations
This should fix it.