How to specify conditions on joined tables in rails - sql

I am trying to do a query in in Rails with ActiveRecord that specifies some condition on a joined table. And i can't seem to get it to work, even though i follow the examples from here:
http://guides.rubyonrails.org/active_record_querying.html#specifying-conditions-on-the-joined-tables
From the guides:
Client.joins(:orders).where(:orders => {:created_at => time_range})
My database schema looks like this, with tables scores, submissions and tasks:
create_table "scores", :force => true do |t|
t.integer "value"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "scores", ["user_id"], :name => "index_scores_on_user_id"
create_table "submissions", :force => true do |t|
t.integer "user_id"
t.integer "task_id"
t.integer "score_id"
t.datetime "completed_at"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "submissions", ["score_id"], :name => "index_submissions_on_score_id"
add_index "submissions", ["task_id"], :name => "index_submissions_on_task_id"
add_index "submissions", ["user_id"], :name => "index_submissions_on_user_id"
create_table "tasks", :force => true do |t|
t.integer "episode_id"
t.integer "score"
t.string "key"
t.datetime "created_at"
t.datetime "updated_at"
end
So i want to do a query where I can find all "scores" that have a relation to a spesific task. Submission belongs to tasks and scores.
My query now looks like this:
Score.joins(:submission).where(:submission => {:task_id => 1})
This generates the following syntax:
SELECT "scores".* FROM "scores" INNER JOIN "submissions" ON "submissions"."score_id" = "scores"."id" WHERE "submission"."task_id" = 1
Which generates the following error:
SQLite3::SQLException: no such column: submission.task_id
But there is a column submission.task_id, which you can see in the db schema. And i can do this successfully:
SELECT "submissions".* FROM "submissions" WHERE "submissions"."task_id" = 1

The name in the clause should be plural to reference the table name:
Score.joins(:submission).where(:submissions => {:task_id => 1})

I find this to be easier.
Score.joins(:submission).merge(Submission.where(task_id: 1))

The clause name should be plural to reference the table name.
Score.joins(:submission).where(submissions: { task_id: 1 })
If score has many submissions, the joins symbol should also be plural to reference the relation between Score and Submission.
Score.joins(:submissions).where(submissions: { task_id: 1 })

A caveat: if you're using non-standard table names the above will fail like so:
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "submissions"
To fix this, put (joined-model-class).table_name as a key in the where hash:
Score.joins(:submission).where(
Submission.table_name => {task_id: 1}
)

Related

Converting a SQL query to Active Record in Rails 4

Here are my two models
class Comment < ActiveRecord::Base
belongs_to :phone
end
class Phone < ActiveRecord::Base
has_many :comments, :dependent => :destroy
end
Here is the schema for the tables
ActiveRecord::Schema.define(version: 20131119231249) do
create_table "comments", force: true do |t|
t.string "username"
t.string "ipaddy"
t.text "pcomments"
t.string "company"
t.string "calltype"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "pnumber"
t.string "source"
end
create_table "phones", force: true do |t|
t.integer "pnumber"
t.text "mrcomment"
t.integer "ccount"
t.datetime "created_at"
t.datetime "updated_at"
end
end
Here is the raw SQL that works
SELECT phones.ccount ,
comments.*
FROM phones
INNER JOIN comments
ON phones.pnumber = comments.pnumber;
When I run the following in my controller
#phones = Phone.select("phones.ccount, comments.*").joins(:comments).where(:comments => {comments.pnumber => phones.pnumber})
I get the following error
undefined local variable or method `comments' for #<FrontPageController:0x00000003c56c70>
Any help on what the active record statement should like would be greatly appreciated
It seems like you're using the select() erroneously. Have you read the docs: http://apidock.com/rails/ActiveRecord/QueryMethods/select ?
from docs: "Second: Modifies the SELECT statement for the query so that only certain fields are retrieved:"
The query's syntax should more look like (using a standard example):
l = Location.where(["id = ?", id]).select("name, website, city").first.

rails no such column error

I have the following app. A Movie has many reviews, a moviegoer has many reviews.
When I try to associate a review with a movie I get the following error
Review Load (0.1ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."movie_id" = 5
SQLite3::SQLException: no such column: reviews.movie_id: SELECT "reviews".* FROM "reviews" WHERE "reviews"."movie_id" = 5
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: reviews.movie_id: SELECT "reviews".* FROM "reviews" WHERE "reviews"."movie_id" = 5
after using a sql gui editor I found that the correct query should be
SELECT "reviews".* FROM "reviews" WHERE "movie_id" = 5
review.rb
class Review < ActiveRecord::Base
belongs_to :movie
belongs_to :moviegoer
attr_protected :moviegoer_id
end
movie.rb and moviegoer.rb have
has_many :reviews
in them.
schema.rb
ActiveRecord::Schema.define(:version => 20130222225620) do
create_table "moviegoers", :force => true do |t|
t.string "name"
t.string "provider"
t.string "uid"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "movies", :force => true do |t|
t.string "title"
t.string "rating"
t.text "description"
t.datetime "release_date"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "reviews", :force => true do |t|
t.integer "potatoes"
t.text "comments"
t.integer "moviegoers_id"
t.integer "movies_id"
end
end
What am I doing wrong? why is rails querying "reviews"."movie_id" instead of just "movie_id"?
You have the wrong column name in your migration. The rails convention is that foreign keys are to be singular. If they are not then you need to tell rails what the foreign key is with an options hash on the association.
Either rollback your migration, fix the column name (moviegoers_id is wrong as well) then migrate again, or tell rails the foreign key.
Class Review < ActiveRecord::Base
belongs_to :movie, :foreign_key => 'movies_id'
belongs_to :moviegoer, :foreign_key => 'moviegoers_id'
end
And the same has to happen on the has many side of both models.

ActsAsTaggableOn after own tag implementation

I had my own implementation of tags in my rails application and wanted to replace it by the ActsAsTaggableOn gem. I generated the migration and deleted all the migrations with tags in it, but forgot to rollback first, so I just did rake db:reset. Now the schema looks fine with tag and taggable in it, but if I try Tags out in the Console, by just typing Tag i get
NameError: uninitialized constant Tag
What causes this error, I thought I just did like in the Railscast.
My schema.rb looks like this:
# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20121009203921) do
create_table "comments", :force => true do |t|
t.text "content"
t.integer "user_id"
t.integer "message_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "events", :force => true do |t|
t.string "name"
t.datetime "start_at"
t.datetime "end_at"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "messages", :force => true do |t|
t.text "content"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "taggings", :force => true do |t|
t.integer "tag_id"
t.integer "taggable_id"
t.string "taggable_type"
t.integer "tagger_id"
t.string "tagger_type"
t.string "context", :limit => 128
t.datetime "created_at"
end
add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id"
add_index "taggings", ["taggable_id", "taggable_type", "context"], :name => "index_taggings_on_taggable_id_and_taggable_type_and_context"
create_table "tags", :force => true do |t|
t.string "name"
end
create_table "users", :force => true do |t|
t.string "name"
t.string "email"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "encrypted_password"
t.string "salt"
t.boolean "admin", :default => false
t.string "phone"
t.string "street"
t.string "zip"
t.string "location"
end
add_index "users", ["email"], :name => "index_users_on_email", :unique => true
end
Thanks for your help!
I had to have a model, to get it work in the console.

Rails: category and subcategory tree

Hi I am new to rails ... I am trying to make a category and subcategory tree in rails...can you please guide me..
my schema is as follows:
create_table "categories", :force => true do |t|
t.string "name", :null => false
t.string "aka", :null => false
t.integer "parent"
end
you can find information here :
http://railscasts.com/episodes/162-tree-based-navigation

Rails find with three tables and a SUM operation

I'm a little stumped as to get the order of records I want with a find operation.
Let's say you had three models:
1. Websites
2. Links
3. Votes
A website has many links and a link has many votes. Each vote has a certain amount of points that a user can attribute to that vote. I'm trying to get a website index page where websites are listed in order of the sum of the points they've received for all the links for that website.
Here's a simplified version of the schema
create_table "votes", :force => true do |t|
t.integer "link_id"
t.integer "points"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
end
create_table "links", :force => true do |t|
t.string "name"
t.string "link"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
t.integer "votes_count", :default => 0
t.integer "website_id"
end
create_table "websites", :force => true do |t|
t.string "domain"
t.boolean "verified", :default => false
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
I'm trying to think about the right active record query to use here. Any help would be appreciated.
To think that I spent a day pulling my hair out when I came up with a solution just an hour after I posted this question.
In the website model I put that the website has_many :points, through => :links
Then the query:
array = Website.find(:all)
array.sort_by {|w| w.donations.sum('amount')}.reverse
This seems to work.