I'm making personal money management app. There I have 2 models: Account and Transaction.
Transaction has belongs_to :account and Account has has_many :transactions.
When I had account_id column in db (postgres) everything worked fine. But I have to rename account_id to from_account_id and add another column to_account_id. So that I can add a spending from Account and income to Account. Also it's necessary for transfers between accounts (where from_account_id and to_account_id both will have some values).
So, I've renamed fields :account_id in my views to :from_account_id and add :to_account_id field. But, when I rename account_id to from_account_id in db, I've got this error.
ActiveRecord::StatementInvalid in PagesController#index
PG::Error: ERROR: column transactions.account_id does not exist
LINE 1: ...s".* FROM "transactions" INNER JOIN "accounts" ON "transacti...
^
: SELECT "transactions".* FROM "transactions" INNER JOIN "accounts" ON
"transactions"."account_id" = "accounts"."id" WHERE "accounts"."user_id" = 17 ORDER BY
transactions.date DESC
My Pages#index controller is:
class PagesController < ApplicationController
def index
if user_signed_in?
#accounts = current_user.accounts.all
#transaction = current_user.transactions.build
#transactions = current_user.transactions.all
#categories = current_user.categories.all
end
end
end
I think Rails is trying to find account_id column in transactions table, because Transaction belongs_to :account. But I need both columns for :account to and from, as you see. So, maybe I need to change this belongs_to condition in Transaction model, but I don't know how.
Thanks for any ideas!
You have to define two belong_to relations in your Transaction class, one for each column in your table transactions:
class Transaction < ActiveRecord::Base
belongs_to :from_account, :class_name => 'Account', :foreign_key => 'from_account_id'
belongs_to :to_account, :class_name => 'Account', :foreign_key => 'to_account_id'
# ...
end
See options in Rails Relation Guide
Yo have to rename the has_many relation in Account model. Something like below will work:
has_many :transactions, :foreign_key => "from_account_id"
Related
I have a simple rails app with the following models and associations:
# app/models/vendor.rb
class Vendor < ActiveRecord::Base
has_many :products
end
# app/models/product.rb
class Product < ActiveRecord::Base
belongs_to :vendor, foreign_key: :account_id
has_many :taxes
end
# app/models/tax.rb
class Tax
belongs_to :product, foreign_key: :item_id
end
Why, when I run Vendor.joins(:products) in the console do I get the following error?
irb(main):039:0> Vendor.joins(:products)
Vendor Load (0.9ms) SELECT "vendors".* FROM "vendors" INNER JOIN "products" ON "products"."vendor_id" = "vendors"."id" LIMIT ? [["LIMIT", 11]]
Traceback (most recent call last):
ActiveRecord::StatementInvalid (SQLite3::SQLException: no such column: products.vendor_id: SELECT "vendors".* FROM "vendors" INNER JOIN "products" ON "products"."vendor_id" = "vendors"."id" LIMIT ?)
I also get a similar error when I try Product.joins(:taxes). Does it have something to do with the foreign keys not being the default ..._id maybe ?
Because the foreign_key in the products table "pointing" to the vendors table is expected to be vendor_id, as Rails by convention prefixes the table name for foreign key constraints through tables and joins makes use of that column.
From belongs_to Rails documentation:
... By default this is
guessed to be the name of the association with an “_id” suffix. So a
class that defines a belongs_to :person association will use
“person_id” as the default :foreign_key. Similarly, belongs_to
:favorite_person, class_name: "Person" will use a foreign key of
“favorite_person_id”.
What you can do is to "tell" Rails that the foreign_key to be used in the vendors table (Vendor model) is account_id instead of vendor_id.
Try with:
class Vendor < ApplicationRecord
has_many :products, foreign_key: :account_id
...
end
Other way is just to create your own join:
Vendor.joins('INNER JOIN products ON products.account_id = vendors.id')
I faced with unpredictable behaviour of has_many association on fetching ids of associated objects. The models:
class Dealer < AR::Base
# attributes :id, :dk_id and so on
with_options class_name: 'Campaign', primary_key: :dk_id, foreign_key: :dk_specific_id do |assoc|
assoc.has_many :campaigns
end
end
class Campaign
# attributes :id, :dk_specific_id and so on
end
I want to fetch 'campaign_ids' for dealer. It returns an error. I can't understand why.
dealer = Dealer.first
dealer.campaigns # => return a list of campaigns to me
dealer.campaign_ids # => exception
ActiveRecord::StatementInvalid - PG::UndefinedColumn: ERROR: column campaigns.dk_id does not exist
LINE 1: SELECT "campaigns".dk_id FROM "camp...
^
: SELECT "campaigns".dk_id FROM "campaigns" WHERE "campaigns"."dk_specific_id" = $1
Please help me to understand how to make this method work. I can't understand why this method asking campaigns table about dk_id, because it should ask it just about id, isn't it?
UPDATED
dk_id is the primary key, which value can be something like abc1. So, campaigns for this dealer having dk_specific_id=abc1
I have two models (Person and Customer) that share a table in the DB using Rails' "type" parameter to separate them:
# Person.rb
class Person < ActiveRecord::Base
...
end
# Customer.rb
class Customer < Person
has_many :orders
end
And also, an orders table:
class Order < ActiveRecord::Base
belongs_to :customer
end
I'm running a test to retreive customers who have ordered within the last 90 days:
# Inside of Customer.rb
def self.ordered_in_last_90_days
scoped.joins(:orders).where('orders.created_at > ?', 90.days.ago)
end
But I'm receiving the following error:
ActiveRecord::StatementInvalid:
SQLite3::SQLException: no such column: orders.customer_id: SELECT "people".* FROM "people" INNER JOIN "orders" ON "orders"."customer_id" = "people"."id" WHERE "people"."type" IN ('Customer') AND (orders.created_at > '2013-06-18 16:47:44.726372')
The join is looking for "orders.customer_id" when it should be looking for "orders.person_id". How can I make this correction?
You need to specify foreign_key:
class Customer < Person
has_many :orders, foreign_key: person_id
end
I have a many-to-many relation between Resumes and Educations which allows for the same education entry to appear on multiple resumes. When displaying Educations on a resume, I want the Educations to be ordered for that specific resume. To do this I set up a join table, Educations_Resumes, with the order information as a property.
However, when I try some like resume.educations I get the following error:
ActiveRecord::StatementInvalid: SQLite3::SQLException: near "order": syntax error:
SELECT "educations".* FROM "educations" INNER JOIN "educations_resumes" ON
"educations"."id" = "educations_resumes"."education_id" WHERE
"educations_resumes"."resume_id" = 2 ORDER BY educations_resumes.order
The models are setup as:
class Resume < ActiveRecord::Base
has_many :educations_resumes
has_many :educations, :through => :educations_resumes,
:order => 'educations_resumes.order'
end
class EducationsResume < ActiveRecord::Base
belongs_to :resume
belongs_to :education
end
class Education < ActiveRecord::Base
has_many :educations_resumes
has_many :resumes, :through => :educations_resumes
end
Any suggestions on how to correctly order resume.educations would be greatly appreciated
You are using a keyword as a column name:
http://www.sqlite.org/lang_keywords.html
Run a migration and change the column name:
rails g migration FixColumnName
This will give you a empty migration file that you should fill in with something like:
class FixColumnName < ActiveRecord::Migration
def change
rename_column :educations_resumes, :order, :ordering
end
end
Suppose you have the following models:
class Category < ActiveRecord::Base
has_one :current_heat, class_name: 'Heat'
has_many :scores, :through => :current_heat
end
class Heat < ActiveRecord::Base
belongs_to :category
has_many :scores
end
class Score < ActiveRecord::Base
belongs_to :heat
end
Surprisingly, when I invoke Category.first.scores ActiveRecord produces the following queries:
SELECT `categories`.* FROM `categories` LIMIT 1
SELECT * FROM `scores` INNER JOIN `heats` ON `scores`.`heat_id` = `heats`.`id` WHERE `heats`.`category_id` = 1
The above query ignores the has_one nature of Category#current_heat. I would have expected something more like:
SELECT `categories`.* FROM `categories` LIMIT 1
SELECT `heats`.* FROM `heats` WHERE `heats`.`category_id` = 1 LIMIT 1
SELECT * FROM `scores` WHERE `scores`.`heat_id` = 6
which is produced only when you explicitly traverse the has_one association from the root with Category.first.current_heat.scores.
It's as if ActiveRecord is silently treating my has_one as a has_many. Can someone explain this behavior to me? Is there an elegant workaround or a "right way" to do it?
Maybe you could remove the
has_many :scores, :through => :current_heat
and instead just delegate :scores through the has_one:
delegate :scores, :to => :current_heat
that would preserve your desired access method Category.first.scores.
has_one doesn't really exist to babysit your database in this fashion. It won't throw errors if there is more than one record that matches the foreign_key, it will just choose the first one. It assumes you haven't errantly added extra records which would break the has_one relation on your own.
In conclusion, the sql that it generates is fine as long as there is only one record attached to the Category. If somehow you've added extra records which shouldn't exist since it is a has_one, then it won't work, but it's not the job of activerecord to tell you that this has happened.