I have two tables:
currencies and rates
currencies: id:int, code:string, name: string
rates: id:int, top_currency_id:int, bottom_currency_id:int, rate:float
And I have two active records for them:
class Rate < ActiveRecord::Base
attr_accessible :bottom_currency, :rate, :top_currency, :top_currency_id
belongs_to :top_currency, :class_name => 'Currency', :foreign_key => 'top_currency_id'
belongs_to :bottom_currency, :class_name => 'Currency', :foreign_key => 'bottom_currency_id'
end
class Currency < ActiveRecord::Base
attr_accessible :code, :name
has_many :rates
end
So the problem is:
When I'm tring to execute following code:
top_currency = Currency.find_by_id(1)
#test = Rate.where(:top_currency=>top_currency)
I getting following error:
Mysql2::Error: Unknown column 'rates.top_currency' in
'where clause': SELECT `rates`.* FROM `rates` WHERE `rates`.`top_currency` = 1
Why Rails's magic doesn't work?
Many thanks.
In your two belongs_to methods, change the foreign_key option to primary_key, leaving everything else as is.
belongs_to :top_currency, :class_name => 'Currency', :primary_key => 'top_currency_id'
# ...
By default, an associated object's primary key is id. However, your currency model has three primary keys, the expected id plus two extra keys: top_currency_id and bottom_currency_id. Active Record needs to know which key to look for. Tell it with the primary_key option.
The foreign_key option is needed when a foreign key is different than the association's name (belongs_to :name) plus "_id". Since your foreign key matches the association name plus "_id," you do not need to use the foreign_key option.
From what I see, your code should work in theory. But I do think you are being a bit redundant.
It should be enough to just do this:
class Rate < ActiveRecord::Base
belongs_to :top_currency, class_name: 'Currency'
belongs_to :bottom_currency, class_name: 'Currency'
end
Rails will infer that the foreign key for top_currency is top_currency_id, and bottom_currency_id for bottom_currency.
I don't think you can query on the relationship like that. To use your example:
top_currency = Currency.find_by_id(1)
#test = Rate.where(:top_currency=>top_currency)
You'd have to change it to this:
top_currency = Currency.find_by_id(1)
#test = Rate.where(:top_currency_id => top_currency.id)
But it might just be easier to do this:
top_currency = Currency.find_by_id(1)
#test = top_currency.rates
Related
In Rails 5, given a relationship between two tables that involves joining them on multiple shared attributes, how can I form an association between the models corresponding to these tables?
SQL:
SELECT *
FROM trips
JOIN stop_times ON trips.guid = stop_times.trip_guid AND trips.schedule_id = stop_times.schedule_id
I tried the following configuration, which works in general...
class Trip < ApplicationRecord
has_many :stop_times, ->(trip){ where("stop_times.schedule_id = ?", trip.schedule_id) }, :inverse_of => :trip, :primary_key => :guid, :foreign_key => :trip_guid, :dependent => :destroy
end
class StopTime < ApplicationRecord
belongs_to :trip, :inverse_of => :stop_times, :primary_key => :guid, :foreign_key => :trip_guid
end
Trip.first.stop_times.first #> StopTime object, as expected
Trip.first.stop_times.first.trip #> Trip object, as expected
... but when I try to use it in more advanced queries, it triggers ArgumentError: The association scope 'stop_times' is instance dependent (the scope block takes an argument). Preloading instance dependent scopes is not supported....
Trip.joins(:stop_times).first #=> the unexpected ArgumentError
StopTime.joins(:trip).first #> StopTime object, as expected
I understand what the error is referencing, but I'm unsure of how to fix it.
EDIT:
I was hoping a single association would be sufficient, but it has been noted two different associations can do the job:
class Trip < ApplicationRecord
has_many :stop_times,
->(trip){ where("stop_times.schedule_id = ?", trip.schedule_id) },
:primary_key => :guid,
:foreign_key => :trip_guid # use trip.stop_times instead of trip.joined_stop_times to avoid error about missing attribute due to missing join clause
has_many :joined_stop_times,
->{ where("stop_times.schedule_id = trips.schedule_id") },
:class_name => "StopTime",
:primary_key => :guid,
:foreign_key => :trip_guid # use joins(:joined_stop_times) instead of joins(:stop_times) to avoid error about instance-specific association
end
Trip.first.stop_times
Trip.eager_load(:joined_stop_times).to_a.first.joined_stop_times # executes a single query
If anyone reading this knows how to use a single association, please at-mention me.
I don't think it is the right solution, but it can help. You can add another similar instance independent association that will be used for preloading only. It will work with :joins and :eager_load but not with :preload.
Note that :includes might internally use either :eager_load or :preload. So, :includes will not always work with that association. You should explicitly use :eager_load instead.
class Trip < ApplicationRecord
has_many :preloaded_stop_times,
-> { where("stop_times.schedule_id = trips.schedule_id") },
class_name: "StopTime",
primary_key: :guid,
foreign_key: :trip_guid
end
# Usage
trips = Trip.joins(:preloaded_stop_times).where(...)
# ...
# with :eager_load
trips = Trip.eager_load(:preloaded_stop_times)
trips.each do |trip|
stop_times = trip.preloaded_stop_times
# ...
end
I have the following models
class Product < ActiveRecord::Base
belongs_to :sub_category
end
class SubCategory < ActiveRecord::Base
belongs_to :category
has_many :products
end
class Category < ActiveRecord::Base
has_many :sub_categories , -> { where("activate = 1") }
end
I need to index my products table.I need to search using category name(which is in category table) and subcategory name(in subcategories table)
ThinkingSphinx::Index.define :product , :with => :active_record do
indexes description
indexes name
indexes merchant_name
indexes sub_category(:sub_category) , :as => :sub_category_name
indexes category(:name) , :as => :cat_name
has sub_category_id
end
The category(:name) is failing.The subcategory is working fine.
Could somebody please help.I tried sub_category.category(:name) but thats also failing
Error Message
ERROR: index 'link_core': sql_range_query: You have an error in your
SQL syntax; check the manual that corresponds to your MySQL server
version for the right syntax to use near 'AS cat_name, products.id AS
sphinx_internal_id, 'Product' AS `sphinx_internal_' at line 1
(DSN=mysql://root:***#localhost:3306/xxxx_dev_phase4)
name should be passed as a chained method, not as an argument
indexes sub_category.category.name , :as => "category_name"
Thanks to the owner Pat for helping me out
concerned github thread
I'm having trouble trying to figure out the best way to associate/join my tables 'teams' and 'schedules'. My models are:
class Team < ActiveRecord::Base
attr_accessible :city, :conf, :div, :key, :name
self.primary_key = "key" #Abbreviations for strings 'CHI', 'TB', 'SEA' etc.
has_many :schedules, foreign_key: [:away_team, :home_team]
end
class Schedule < ActiveRecord::Base
attr_accessible :away_team, :date, :home_team, :season, :week, :team_key
self.primary_keys = :away_team, :home_team #Abbreviations for strings 'CHI', 'TB', 'SEA' etc.
belongs_to :team, primary_key: "key"
end
I installed the gem "composite_primary_keys", "~> 5.0.13".
In rails console when I assign a variable
>> team = Team.find("SEA")
SELECT "teams".* FROM "teams" WHERE "teams"."key" = ? LIMIT 1 [["key", "SEA"]]
=> #<Team key: "SEA", city: "Seattle", name: "Seahawks", conf: "NFC", div: "West">
it displays Seattle perfectly, but when I run:
>> team.schedules
SELECT "schedules".* FROM "schedules" WHERE ("schedules"."away_team" = 'SEA' AND "schedules"."home_team" IS NULL)
=> []
The 'schedule' table has data for 'SEA' in the home_team column.
Would a join table be a better solution? If so, how would you do it? Any help would be greatly appreciated!
First: don't use composit keys, if you don't have to (and you don't in this case).
Primary keys are for accessing individual items, not for constraints or business logic.
In your case, the primary index can't be used to select schedules by home_team.
Also it's a good idea to use Rails conventions as much as possible. It makes life easier.
Use id as primary key and for joining tables.
I see that your schedule belongs to two teams, and team have two kinds of schedules (home and away). So you models could look like:
class Team < ActiveRecord::Base
attr_accessible :city, :conf, :div, :key, :name
has_many :home_schedules, class_name: 'Schedule', foreign_key: :home_team_id
has_many :away_schedules, class_name: 'Schedule', foreign_key: :away_team_id
def schedules
home_schedules + away_schedules
end
end
class Schedule < ActiveRecord::Base
attr_accessible :away_team, :date, :home_team, :season, :week, :team_key
belongs_to :home_team, class_name: 'Team'
belongs_to :away_team, class_name: 'Team'
end
then
team = Team.find_by_key('SEA')
team.schedules
Added
I would generate the model with
rails g model Team city conf div short_cut name
rails g model Schedule day:date season week:integer home_team_id:integer away_team_id:integer
You link teams to fans by adding team_id to Fan. You can always use the team's short_cut (as I named your former key attribute) to display or select the team, but let Rails use id for all the internal stuff.
I'm using single table inheritance in conjunction with a polymorphic association. Here are my models.
class ChangeInformation < ActiveRecord::Base
belongs_to :eventable, :polymorphic => true
end
class Race < ActiveRecord::Base
has_many :track_condition_changes, :as => :eventable, :class_name => "ChangeInformation"
#other associations omitted
end
class TrackConditionChange < ChangeInformation
end
The change_informations table has the following fields:
type #sti field
change_code
eventalbe_id #polymorphic id
eventable_type #polymorphic type
description
When I use the following create method:
TrackConditionChange.create(:change_code => 1, :eventable_id => 3 :description => "test")
a TrackConditionChange record is created, with the type field populated, however, the eventable_type field (which should be Race) is not populated. I was under the impression that rails populated this field automatically similar to the STI type field. Was I under the wrong impression or is there a problem with my associaition setup.
Thanks for the input.
If you're only passing in the eventable_id, how will it know what type it is? You will have to either pass the entire eventable object or build it based on the track_condition_changes relationship:
1. Pass the eventable object:
race = Race.find(3)
TrackConditionChange.create(:change_code => 1, :eventable => race, :description => "test")
2. Build and save based on the relationship:
race = Race.find(3)
race.track_condition_changes << TrackConditionChange.new(:change_code => 1, :description => "test")
I have a table Foo that has a polymorphic belongs_to association called bar. The foos table has the standard bar_id column. However, instead of a string-based bar_type column, I have an integer bar_type_id column. This column references the id column in the table bar_types. bar_types.name holds the name of the class that represents the class of the particular bar instance.
Does Rails (ideally >=2.3.10) allow for this type of polymorphic association?
We did it by overriding the association_class method in a new module and included it using the :extend option. Also created a integer to string mapping hash to make things easier.
In config/initializers directory or anywhere you like, create a file and define the hash
INT_OBJECT_TYPE_TO_CLASSNAME = { 0 => "Project", 1 => "Task", 2 => "Timesheet" }
class CommentObjectType < ActiveRecord::Base
module ClassNamesAsInt
def association_class
return INT_OBJECT_TYPE_TO_CLASSNAME[restricted_object_type].constantize
end
end
end
In comments.rb
belongs_to :commentable, :polymorphic => true, :extend => CommentObjectType::ClassNamesAsInt
I'm making use of the polymorphic integer type gem, written by one of my co-workers. It's slightly easier to use than the examples given above, in my opinion. For example, after configuring the mapping, you change from:
belongs_to :actor, polymorphic: true
to the new format:
belongs_to :actor, polymorphic: true, integer_type: true
There are two approaches for this.
First is easy:
has_many :bars, :conditions => "whatever you want"
Second could be tricky:
set_inheritance_column :bar_type_id
I am not sure, but you can play around
belongs_to :bar, :class_name => proc{ BarType.find(self.bar_type_id).name }, :foreign_key => :bar_id