rails: using associated model's columns in :where - ruby-on-rails

I'm not sure what's going on here. I have a scope I'm trying to create that works with my association:
class Subscription < ActiveRecord::Base
belongs_to :subscriber, :class_name => "User"
belongs_to :subscribable, :polymorphic => true
end
create_table :products do |t|
t.string :name
t.decimal :price
t.decimal :cost_per_unit
t.integer :user_id
end
create_table :subscriptions do |t|
t.string :name
t.decimal :price
t.decimal :cost_per_unit
t.integer :subscriber_id
t.integer :subscribable_id
t.string :subscribable_type
end
class Product < ActiveRecord::Base
has_many :subscriptions, :as => :subscribable, :dependent => :destroy
def self.lower_prices
Product.includes(:subscriptions).
where("products.price < subscriptions.price OR products.cost_per_unit < subscriptions.cost_per_unit" )
end
end
I'm trying to compare the lower price of the Product to the Subscription but this gives me the error:
ActiveRecord::StatementInvalid in Pages#subscribed_products
PGError: ERROR: missing FROM-clause entry for table "subscriptions"
LINE 1: ... WHERE (user_id != 2) AND (products.price < subscripti...
^
: SELECT COUNT(*) FROM "products" WHERE (user_id != 2) AND (products.price < subscriptions.price OR products.cost_per_unit < subscriptions.cost_per_unit)
What's wrong here?

The includes method doesn't do exactly what you think. Substitute joins for includes and it should Do What You Mean:
Product.joins(:subscriptions).
where("products.price < subscriptions.price OR products.cost_per_unit < subscriptions.cost_per_unit" )
or perhaps:
Product.includes(:subscriptions).joins(:subscriptions).
where("products.price < subscriptions.price OR products.cost_per_unit < subscriptions.cost_per_unit" )
joins translates to a JOIN in the resulting SQL query, so you can perform WHERE clauses on the joined table. include just asks Active Record to perform another query to select all the related records in the given table. If you do both together, Active Record creates a (rather long) all-in-one that both joins the two tables and uses the results to create both sets of objects.

Related

How can I directly update the join model in a has_many_through association?

I have a Rails app with two models, joined by a third:
class Food < ApplicationRecord
has_many :shopping_list_items
has_many :users, through: :shopping_list_items
end
class ShoppingListItem < ApplicationRecord
belongs_to :user
belongs_to :food
end
class User < ApplicationRecord
has_many :shopping_list_items
has_many :foods, through: :shopping_list_items
end
The middle model, ShoppingListItem, has a few extra attributes, including priority which I'd like to update directly.
So for instance, I'd like to do something like:
r = current_user.shopping_list_items.where(food_id: 1).first
r.priority = "urgent"
r.save
The object looks fine until I try to save it, when I get a SQL error:
ActiveRecord::StatementInvalid (PG::SyntaxError: ERROR: zero-length delimited identifier at or near """"
LINE 1: ...$1, "updated_at" = $2 WHERE "shopping_list_items"."" IS NULL
^
: UPDATE "shopping_list_items" SET "priority" = $1, "updated_at" = $2 WHERE "shopping_list_items"."" IS NULL):
I guess it's complaining about the absence of a primary key? Not sure how to fix this, since the rails docs say that join tables shouldn't have a primary key column...
I created the middle table with a migration like this, :
create_table :shopping_list_items, id: false do |t|
t.belongs_to :user
t.belongs_to :food
t.string :priority
t.integer :position
t.timestamps
end

Querying many-to-many relationship filtering by many to many table but ordering by an associated table's field

I am trying to come up with a query for a many-to-many relationship with filtering for a certain field in the many-to-many table while ordering by a field in an associated table.
How do I get all the active firm_emps of a specific firm and order the firm_emps by user's name?
user.rb
Class User < ApplicationRecord
has_many :firm_emps, :dependent => :destroy
has_many :firms, through: :firm_emps
end
user.rb migration file
...
t.string :name
t.boolean :active
...
firm_emp.rb
Class FirmEmp < ApplicationRecord
belongs_to :firm
belongs_to :user
end
firm_emp.rb's migration file
...
t.belongs_to :user, index: true
t.belongs_to :firm, index: true
t.boolean :admin, default: false
t.boolean :active, default: true
...
firm.rb
Class Firm < ApplicationRecord
has_many :firm_emps, dependent: :destroy
has_many :users, through: :firm_emps
end
Firm.rb's migration file
...
t.string :full_name
t.boolean :active
...
I've tried the following queries in rails console:
f = Firm.first
f.users.where(active: true).order('users.name asc')
# But this filters on User's table field active: true and not the FirmEmps table field active: true
f.users.joins(:firmemps).where(active: true).order('users.name asc')
# Just doesn't work
f.firm_emps.active.order('firm_emps.active')
# But i can't order by user's field 'name'
EDIT:
#PragyaSriharsh's and #ArunEapachen's answers worked.
Try it:
f.users.joins(:firm_emps).where('firm_emps.active=?', true).order('users.name asc')
If it doesn't work use sort_by method.
Try the following.
f = Firm.first
f.users.where('firm_epms.active = ?' , true).order('users.name asc')

how to sort ruby array by model action?

I'm trying to make a project like cafe search service based on social network, and I want to sort Cafe array by points which other users gave.
class Cafe < ActiveRecord::Base
belongs_to :user
has_many :posts, dependent: :destroy
has_many :tags, dependent: :destroy
has_many :payments, dependent: :destroy
has_many :payinfos, dependent: :destroy
mount_uploader :image,CafeimageuploaderUploader
mount_uploader :thumnail,CafeimageuploaderUploader
geocoded_by :address
after_validation :geocode
def avg
total = 0
posts.each do |c|
total += c.score
end
if posts.count == 0
0
else
total.to_f / posts.count
end
end
end
this is Cafe model, 'avg' is point average that users gave.
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :content , null: false, default: ""
t.string :image
t.string :address , null: false, default: "위치정보 없음"
t.string :hashstr
t.datetime :writtentime
t.integer :user_id
t.integer :cafe_id , null: false, default: 0
t.integer :score, default:0
t.timestamps null: false
end
end
end
Here's post columns.
What I want to do is sort new Cafe array by this avg action.(is it called model action, right??)
give me some advice, thank you.
I would add an attribute average_score to cafe model and a after_save callback method to post model. If a post will be safed you take all posts of a cafe (by the foreign key), calculate the average score and save it to the cafe or you trigger a method of the cafe model to do that. So you can sort your cafes easily.
Let use db query, it is more efficient. (assume Cafe's table name is cafes)
Cafe.joins("LEFT OUTER JOIN (SELECT cafe_id, AVG(score) AS avg_score
FROM posts
GROUP BY cafe_id
) AS temp
ON cafes.id = temp.cafe_id
").order('temp.avg_score DESC NULLS LAST')
Ideally, we calculate the average score in a temporary table first then join with Cafe via cafe_id. After that, we can easily use order with avg_score.
Because we are using LEFT OUTER JOIN, the avg_score of cafe which doesn't have any post will be NULL, so we use NULLS LAST to ensure that cafe will be in the end.

Rails Join in Model

I'm trying to use a belongs_to relationship inside a has_many as below.
In words: I want unique Reports that belong to a DownloadSchedule while being constrained by the client_id.
class DownloadSchedule < ActiveRecord::Base
serialize :custom_data
belongs_to :client
has_many :report_column_schedule_links
has_many :reports, -> { uniq where("report_column_schedule_links.client_id = ?", self.client.id) }, :through => :report_column_schedule_links
end
The error thrown is
Mysql2::Error: Unknown column 'report_column_schedule_links.client_id' in 'where clause': SELECT `reports`.* FROM `reports` WHERE (report_column_schedule_links.client_id = 1)
ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'report_column_schedule_links.client_id' in 'where clause': SELECT `reports`.* FROM `reports` WHERE (report_column_schedule_links.client_id = 1)
Is this possible with a has_many or do I have to write a custom join? I'm using Rails 4.
[Update]
The structure for report_column_schedule_links is below.
create_table :report_column_schedule_links do |t|
t.integer :report_id
t.integer :report_column_id
t.integer :client_id
t.integer :schedule_id
t.integer :download_schedule_id
t.timestamps
end
You'll notice the MySQL error is on the statement
SELECT `reports`.* FROM `reports` WHERE (report_column_schedule_links.client_id = 1)
This statement isn't performing the join on the has_many.
Thanks,
Justin

Trouble with self referential model in Rails

I have a model named User and I want to be able to self reference other users as a Contact. In more detail, I want a uni-directional relationship from users to other users, and I want to be able to reference an owned user of one user as a 'contact'. ALSO, i want to have information associated with the relationship, so I will be adding fields to the usercontact relation (I just edited this sentence in).
I attempted to do this while using the answer to this question as a guide.
Here is the User model:
user.rb
class User < ActiveRecord::Base
attr_accessible(:company, :email, :first_name, :last_name,
:phone_number, :position)
has_many(:user_contacts, :foreign_key => :user_id,
:dependent => :destroy)
has_many(:reverse_user_contacts, :class_name => :UserContact,
:foreign_key => :contact_id, :dependent => :destroy)
has_many :contacts, :through => :user_contacts, :source => :contact
end
I also created the model UserContact as a part of connecting contacts to users:
usercontact.rb
class UserContact < ActiveRecord::Base
belongs_to :user, :class_name => :User
belongs_to :contact, :class_name => :User
end
Here is the create_users.rb migration file i used:
create_users.rb
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :phone_number
t.string :email
t.string :company
t.string :position
t.timestamps
end
end
end
And here is the create_users_contacts.rb migration:
create_users_contacts.rb
class CreateUsersContacts < ActiveRecord::Migration
def up
create_table :users_contacts, :force => true do |t|
t.integer :user_id, :null => false
t.integer :contact_id, :null => false
t.boolean :update, :null => false, :default => false
end
# Ensure that each user can only have a unique contact once
add_index :users_contacts, [:user_id, :contact_id], :unique => true
end
def down
remove_index :users_contacts, :column => [:user_id, :contact_id]
drop_table :users_contacts
end
end
However, for reasons unknown to me, I believe something has gone awry in the linking since on my users index page, I have a column using <td><%= user.contacts.count %></td>, but I get this error from the line when I attempt to load the page:
uninitialized constant User::UserContact
I think the issue may be something to do with the fact that I want to name users associated with another user as contacts, because I cannot find other examples where that is done, and as far as I can tell I am doing everything properly otherwise (similarly to other examples).
The closest similar problem that I found was outlined and solved in this question. The issue was incorrect naming of his connecting model, however I double checked my naming and it does not have that asker's problem.
Any help is appreciated, let me know if any other files or information is necessary to diagnose why this is occurring.
EDIT
After changing usercontact.rb to user_contact.rb, I am now getting this error:
PG::Error: ERROR: relation "user_contacts" does not exist
LINE 1: SELECT COUNT(*) FROM "users" INNER JOIN "user_contacts" ON "...
^
: SELECT COUNT(*) FROM "users" INNER JOIN "user_contacts" ON "users"."id" = "user_contacts"."contact_id" WHERE "user_contacts"."user_id" = 1
EDIT TWO
The issue was that my linking table, users_contacts, was misnamed, and should have been user_contacts! so I fixed it, and now it appears to work!!
You need to rename your usercontact.rb to user_contact.rb
This is naming convention rails autoload works with.

Resources