Order results in sub table - ruby-on-rails

#posts = Category.find(params[:id]).posts
How can I order the results with column from the posts table? For example on the posts.created_at column?

You can do this:
#posts = Category.find(params[:id]).posts.all(:order => "created_at")
not sure if there are better ways of doing it... hope that helps =)

#posts = Category.find(params[:id]).posts.all(:order => "created_at")
You can also add to this other things such as
#posts = Category.find(params[:id]).posts.all(:order => "created_at", :limit => 10)
or
#posts = Category.find(params[:id]).posts.all(:order => "created_at DESC")

Another very simple solution is to simply specify the order on the association itself.
class Post < ActiveRecord::Base
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :posts, :order => "created_at"
end
Any posts fetched via the association will already be sorted. This will let you keep the ordering details in the model itself and the SQL-ish syntax out of the controller.
#posts = Category.find(params[:id]).posts
Will give you back your records in "created_at" order.

Related

Rails Undefined method for ni:NilClass Error?

I have a rails app where I have has_many and belongs_to association. But I am having problems while trying to retrieve the elements. My Models are Events, Comments and Votes.
Events has multiple Comments and Votes. Of Course Comments and Voted belong_to one event.
My schema is
create_table "events",
t.string "etime"
t.integer "eid"
end
create_table "votes",
t.integer "eventid"
t.string "userid"
t.integer "event_id"
end
Associations:
class Event < ActiveRecord::Base
has_many :comments
has_many :votes
end
class Votes < ActiveRecord::Base
belongs_to :event
end
I am trying to view all the votes for the events. The controller action is:
#events = Event.all
#events.each do |event|
event.votes.each do |vote|
respond_to do |format|
format.html
end
end
end
View and error line:
<%= #vote.userid %>
I get an error "Undefined Method "userid" for vote". In my controller, I render my eventid and it worked. So it seems to be a problem when I do it in the view..
Any idea What I could be doing wrong? I am completely lost on this one
Might be as simple as using event_id instead of eventid.
It's always worth it to run rails console and play around in there. You can usually autocomplete methods.
v = Vote.new
v.event<tab to get options>
Somewhere you have got it all wrong, your table migrations, as in your foreign_key names are wrong, so are your primary keys.
In standard cases, all models have primary keys as id, just id.
For associating with other model, for example, a belongs_to :event association your vote model needs a , event_id column. These are rails standards
Lets assume, your associations /columns are correct, The error that you get is because your #vote object is nil , in your controller you need to assign/initialize the #vote variable to use it the view which you don't seem to be doing.

how to use array variables inside action in controller

I want to fetch all posts posted by those users who have gone to same college as the current users...So inside my welcome controller i have written following code..
class WelcomesController < ApplicationController
def index
#col = Education.select(:college_id).where(:user_id => #current_user)
#user = Education.select(:user_id).where(:college_id => #col)
#welcome = Welcome.where(:user_id => #user)
end
end
Following is my shema code for welcome and education model:
create_table "welcomes", :force => true do |t|
t.text "message"
t.integer "user_id"
end
create_table "educations", :force => true do |t|
t.integer "college_id"
t.integer "user_id"
end
#col = Education.select(:college_id).where(:user_id => #current_user)....this line returns college ids associated with current logged in user.This is working perfectly on my console which is returning following output..
[#<Education college_id: 1>, #<Education college_id: 2>]
but i dont know how to use this output in my next line,so i have written this statement which should return all the users whose college id is the output of prevous statement
#user = Education.select(:user_id).where(:college_id => #col)
and my last line should return all the posts posted by those users whose ids are inside the #user array:
#welcome = Welcome.where(:user_id => #user)
but this is not working.When i run my project i cant see any output on my page and on console i am getting following output :
SELECT welcomes.* FROM welcomes WHERE (welcomes.user_id IN (NULL))
which means its not getting any user ids..
How can i solve this ...
You can try this:
#col = Education.select(:college_id).where(:user_id => #current_user.id).all
#users = Education.select(:user_id).where(:college_id => #col.collect(&:college_id)).all
#welcome = Welcome.where(:user_id => #users.collect(&:user_id)).all
The best way I see to accomplish this is to set up a has_many_and_belongs_to_many relationship between your User and Education models. (Each Education will have many Users and each User may have multiple Eductions.) You will need to create a joining table in your database to support this type of relationship - see the Rails Guide for more information on this.
I would set up your models in this manner:
class User < ActiveRecord::Base
has_one :welcome
has_and_belongs_to_many :educations
end
class Education < ActiveRecord::Base
has_and_belongs_to_many :users
end
class Welcome < ActiveRecord::Base
belongs_to :user
end
The join table for the has_many_and_belongs_to_many join table migration (be sure to double check this code, not sure I got this exactly right):
def self.up
create_table 'education_user', :id => false do |t|
t.column :education_id, :integer
t.column :user_id, :integer
end
end
Your controller code is now much simpler and looks like this:
#welcomes = #current_user.eductions.users.welcome.all
In your view:
<% #welcomes.each do |welcome| %>
<p><%= welcome.message %></p>
<% end %>
One of the more powerful features of Ruby on Rails is the model relationships. They are a little more work up front, but if you take the time to set them up correctly they can make your life much easier, as is evidenced by the simplified #welcomes query above.
I'd recommend you to make relation between User and Collage
class User < ActiveRecord::Base
has_many :educations
has_many :colleges, :through => :educations
has_many :posts
scope :by_college_id, lambda {|cid| where("exists (select educations.id from educations where educations.user_id = users.id AND educations.college_id in (?) limit 1)", Array.wrap(cid)) }
def college_mates
self.class.by_college_id(self.college_ids).where("users.id != ?", id)
end :through => :educations
end
class Education < ActiveRecord::Base
belongs_to :user
belongs_to :college
end
So now in your controller you can write
class WelcomesController < ApplicationController
def index
#posts = #current_user.college_mates.includes(:posts).map(&:posts).flatten
# or
#posts = Post.where(user_id: #current_user.college_mates.map(&:id))
end
end
Second variant generates 3 sql-requests, first variant - only two. But this is same work with data, I think time will be also same. Usually controllers contain only few lines of code, all logic written in models. Ideally controller should contain only Post.by_college_mates_for(#curren_user.id)

Rails, Sphinx (thinking_sphinx) and sorting from associations

I have 3 models:
Stats (belongs_to :item)
t.integer :skin_id
t.integer :item_id
t.integer :rating
Item (has_many :stats)
and
Skin (has_many :stats)
Using thinking_sphinx i want to create a separate index, for items, sorted by :rating for particular :skin_id
So, i'm trying to:
define_index 'sort_by_rate' do
indexes stats(:rating), :as => :ratings, :sortable => true
end
But this, will generate an index for all :skin_id (in the Stat model), not for particular one.
In other words, i need to gather all items, sorted by Stat.rating, with Stat.skin_id == 1 (for example).
Here is the example of SQL:
"SELECT `stats`.* FROM `stats` INNER JOIN `items` ON `items`.`id` = `stats`.`item_id` WHERE `stats`.`skin_id` = 1 ORDER BY rating DESC"
ANy solutions is very appreciated!
Perhaps you should have in your define_index block:
has :skin_id
and then, when searching, filter by that. Something like this:
Item.search(:with => {:skin_id => skin.id}, :order_by => 'ratings ASC')

using :counter_cache with some conditions [duplicate]

I am new to the concept of counter caching and with some astronomical load times on one of my app's main pages, I believe I need to get going on it.
Most of the counter caches I need to implement have certain (simple) conditions attached. For example, here is a common query:
#projects = employee.projects.where("complete = ?", true).count
I am stumbling into the N+1 query problem with the above when I display a form that lists the project counts for every employee the company has.
Approach
I don't really know what I'm doing so please correct me!
# new migration
add_column :employees, :projects_count, :integer, :default => 0, :null => false
# employee.rb
has_many :projects
# project.rb
belongs_to :employee, :counter_cache => true
After migrating... is that all I need to do?
How can I work in the conditions I mentioned so as to minimize load times?
With regards to the conditions with counter_cache, I would read this blog post.
The one thing you should do is add the following to the migration file:
add_column :employees, :projects_count, :integer, :default => 0, :null => false
Employee.reset_column_information
Employee.all.each do |e|
Employee.update_counters e.id, :projects_count => e.projects.length
end
So you current projects count can get migrated to the new projects_count that are associated with each Employee object. After that, you should be good to go.
Check counter_culture gem:
counter_culture :category, column_name: Proc.new {|project| project.complete? ? 'complete_count' : nil }
You should not use "counter_cache" but rather a custom column :
rails g migration AddCompletedProjectsCountToEmployees completed_projects_count:integer
(add , :default => 0 to the add_column line if you want)
rake db:migrate
then use callbacks
class Project < ActiveRecord::Base
belongs_to :employee
after_save :refresh_employee_completed_projects_count
after_destroy :refresh_employee_completed_projects_count
def refresh_employee_completed_projects_count
employee.refresh_completed_projects_count
end
end
class Employee
has_many :projects
def refresh_completed_projects_count
update(completed_projects_count:projects.where(completed:true).size)
end
end
After adding the column, you should initialize in the console or in the migration file (in def up) :
Employee.all.each &:refresh_completed_projects_count
Then in your code, you should call employee.completed_projects_count in order to access it
Instead of update_counters i use update_all
You don't need the Employee.reset_column_information line AND it's faster because you are doing a single database call
Employee.update_all("projects_count = (
SELECT COUNT(projects.id) FROM projects
WHERE projects.employee_id = employees.id AND projects.complete = 't')")

Counter Cache for a column with conditions?

I am new to the concept of counter caching and with some astronomical load times on one of my app's main pages, I believe I need to get going on it.
Most of the counter caches I need to implement have certain (simple) conditions attached. For example, here is a common query:
#projects = employee.projects.where("complete = ?", true).count
I am stumbling into the N+1 query problem with the above when I display a form that lists the project counts for every employee the company has.
Approach
I don't really know what I'm doing so please correct me!
# new migration
add_column :employees, :projects_count, :integer, :default => 0, :null => false
# employee.rb
has_many :projects
# project.rb
belongs_to :employee, :counter_cache => true
After migrating... is that all I need to do?
How can I work in the conditions I mentioned so as to minimize load times?
With regards to the conditions with counter_cache, I would read this blog post.
The one thing you should do is add the following to the migration file:
add_column :employees, :projects_count, :integer, :default => 0, :null => false
Employee.reset_column_information
Employee.all.each do |e|
Employee.update_counters e.id, :projects_count => e.projects.length
end
So you current projects count can get migrated to the new projects_count that are associated with each Employee object. After that, you should be good to go.
Check counter_culture gem:
counter_culture :category, column_name: Proc.new {|project| project.complete? ? 'complete_count' : nil }
You should not use "counter_cache" but rather a custom column :
rails g migration AddCompletedProjectsCountToEmployees completed_projects_count:integer
(add , :default => 0 to the add_column line if you want)
rake db:migrate
then use callbacks
class Project < ActiveRecord::Base
belongs_to :employee
after_save :refresh_employee_completed_projects_count
after_destroy :refresh_employee_completed_projects_count
def refresh_employee_completed_projects_count
employee.refresh_completed_projects_count
end
end
class Employee
has_many :projects
def refresh_completed_projects_count
update(completed_projects_count:projects.where(completed:true).size)
end
end
After adding the column, you should initialize in the console or in the migration file (in def up) :
Employee.all.each &:refresh_completed_projects_count
Then in your code, you should call employee.completed_projects_count in order to access it
Instead of update_counters i use update_all
You don't need the Employee.reset_column_information line AND it's faster because you are doing a single database call
Employee.update_all("projects_count = (
SELECT COUNT(projects.id) FROM projects
WHERE projects.employee_id = employees.id AND projects.complete = 't')")

Resources