Trying to get along with Sphinx/Thinking Sphinx for the first time.
I've got my models defined as follows (simplified):
class Branch < ActiveRecord::Base
has_many :salesmen, :class_name => "User"
has_many :leads, :through => :salesmen
end
class User < ActiveRecord::Base
belongs_to :branch
has_many :leads, :foreign_key => "owner_id"
end
class Lead < ActiveRecord::Base
belongs_to :owner, :class_name => "User"
define_index do
indexes company_name
indexes :name, :sortable => true
has owner.branch_id, :as => :branch_id
indexes [owner.last_name, owner.first_name], :as => :owner_full_name, :sortable => true
end
end
Anytime I call
Branch.first.leads.search
I get
RuntimeError: Missing Attribute for Foreign Key branch_id
What am I doing wrong?
The issue is that Thinking Sphinx needs branch_id as an attribute in your index, so it can restrict results to just the relevant branch (because you're searching within an association).
It's not clear from your associations (or maybe that just my dire need for sleep) whether a lead belongs to a branch via the owner, or directly as well. If the former, Ben's suggestion is probably correct. Otherwise, try adding the following to your define_index block:
has branch_id, :as => :direct_branch_id
An alternative approach, after reading the comments, is to add your own search method to the leads association in Branch. A vague attempt (you will need to debug, I'm sure):
has_many :leads, :through => :salesmen do
def search(*args)
options = args.extract_options!
options[:with] ||= {}
options[:with][:branch_id] = proxy_owner.id
args << options
Lead.search(*args)
end
end
This should get around the fact that you do not have a direct reference to the branch from Lead. The only possible issue is that I'm not sure whether custom extensions get loaded before or after what Thinking Sphinx injects. Give it a shot, see if it helps.
If you say a Lead belongs_to a Branch, then you must have a branch_id in the leads table. Since you don't, it's not a belongs_to relationship. I think you need something like this:
class Branch < ActiveRecord::Base
has_many :leads, :through => :salesmen
has_many :salesmen, :class_name => "User"
end
class User < ActiveRecord::Base
belongs_to :branch # users table has branch_id
has_many :leads, :foreign_key => "owner_id"
end
class Lead < ActiveRecord::Base
belongs_to :owner, :class_name => "User" # leads table has owner_id
define_index do
indexes :company_name
indexes :name, :sortable => true
has owner.branch_id, :as => :branch_id
indexes [owner.last_name, owner.first_name], :as => :owner_full_name, :sortable => true
end
end
I believe you're missing a :through option on your branch relation. Try updating to:
class Lead < ActiveRecord::Base
has_one :branch, :through => :owner
Related
Here is what I'm trying to do:
class Cashflow < ActiveRecord::Base
belongs_to from_account, :class_name => 'Account'
belongs_to to_account, :class_name => 'Account'
end
class Account < ActiveRecord::Base
has_many :cashflows
end
where Account::cashflows is obviously a list of all cashflows that either have the account_id stored in from_account or in to_account.
I'm confused. What is the proper way of handling such a case? How bad design is this? What would be the proper way of designing such a relation?
I think you have the right structure as there can only two accounts be involved in a particular transaction/cashflow. if you use many to many association you would need to handle the validation for not involving more or less than 2 accounts. For your current structure you can change your moidel associations to be:
class Cashflow < ActiveRecord::Base
belongs_to from_account, :class_name => 'Account', :foreign_key => :from_account
belongs_to to_account, :class_name => 'Account', :foreign_key => :to_account
end
class Account < ActiveRecord::Base
has_many :debits, :class_name => 'Cashflow', :foreign_key => :from_account
has_many :credits, :class_name => 'Cashflow', :foreign_key => :to_account
def cashflows
transactions = []
transactions << self.debits
transactions << self.credits
transactions.flatten!
## or may be the following commented way
# Cashflow.where('from_account = ? OR to_account = ?', self.id, self.id)
end
end
This way you can keep track of the amount debited/credited in a particular account and also get the accounts involved in a particular transaction/cashflow.
Suggestions on top of my mind
1) Your class (table) cashflows should have two columns from_account and to_account.
2) from_account and to_account should have the id of the account concerned
3) cashflows should belongs_to :account
4) account should has_many :cashflows. Ideally it should be cash_flows
These should be good starting points. Don't they meet your requirements?
I think you should use has and belongs to many association here:
class Account < ActiveRecord::Base
has_and_belongs_to_many :incoming_cashflows, :class_name => 'Cashflow', :join_table => :incoming_cashflows_accounts
has_and_belongs_to_many :outcoming_cashflows, :class_name => 'Cashflow', :join_table => :outcoming_cashflows_accounts
end
class Cashflow < ActiveRecord::Base
has_and_belongs_to_many :from_accounts, :class_name => 'Account', :join_table => :incoming_cashflows_accounts
has_and_belongs_to_many :to_accounts, :class_name => 'Account', :join_table => :outcoming_cashflows_accounts
end
Also you will need some validation code allows to add only one account to Cashflow.
I have the following:
belongs_to :type, :class_name => :activity_type
belongs_to :activity_type # needed for has_one :through?
has_one :category, :through => :activity_type, :class_name => :activity_category
Is there a way to do this "has_one through" relationship using "type" instead of "activity_type"?
Edit: This was not working, and I failed to see thanks to the magic word "type".
What I have now is this:
belongs_to :company
belongs_to :type, :class_name => 'ActivityType', :foreign_key => 'activity_type_id'
has_one :category, :through => :type, :class_name => 'ActivityCategory', :foreign_key => 'activity_category_id'
But it fails with
no such column: activity_types.category_id
which is correct, since the expected column would be "activity_types.activity_category_id". How can I fix that?
You're using associations in a wrong way. First of all when you use through: then class_name, foreign_key will be omitted. So this is why it's expecting category_id. Also I think that you shouldn't overload your associations with rewriting defaults options since when there's a lot of associations on a model it starts to look like a mess and hard to understand. So you probably should just write it as belongs_to :activity_type and add whatever name suits you best like alias_method :type, :activity_type.
So in short, this is what I'm proposing you:
belongs_to :company
belongs_to :activity_type
has_one :activity_category, :through => :activity_type
alias_method :type, :activity_type
alias_method :category, :activity_category
I have started off by following this guide here Rails - Multiple Index Key Association
I have tried to add in :include to speed up the association but im getitng this error:
ActiveRecord::StatementInvalid in ItemsController#show
SQLite3::SQLException: no such column: links.item_id: SELECT "links".* FROM "links" WHERE ("links".item_id = 19)
here's what i have:
item.rb
class Item < ActiveRecord::Base
has_many :links, :dependent => :destroy, :uniq => true
belongs_to :category
belongs_to :user
is_sluggable :name
def links
Link.by_item(self)
end
link.rb
class Link < ActiveRecord::Base
belongs_to :item1, :class_name => 'Item', :foreign_key => :item1_id
belongs_to :item2, :class_name => 'Item', :foreign_key => :item2_id
belongs_to :user
validates_presence_of :item1_id
validates_presence_of :item2_id
validates_uniqueness_of :item1_id, :scope => :item2_id, :message => "This combination already exists!"
def self.by_item(item)
where("item1_id = :item_id OR item2_id = :item_id", :item_id => item.id)
end
end
items_controller.rb
def show
#item = Item.find_using_slug(params[:id], :include => [:category, :user, :links])
It works okay without :links inside :include. But otherwise I get the error.
From what I understand, the item_id is stored in the links table as item1_id or item2_id, which is why it cannot be found. Is there a workaround for this because I will be heavily referencing the links records. I am not so good with the SQL stuff.
Also unsure what's the best way to set up an Index
Willing to try out any advice. Thanks
The problem lies with the has_many :links association setup in Item. By default, this gets converted to a SQL query which looks for all links which have an item_id of the current Item. To override this default behavior, specify the find SQL yourself like this:
has_many :links, :dependent => :destroy, :uniq => true, :finder_sql => 'SELECT DISTINCT(*) FROM links WHERE item1_id = #{id} OR item2_id = #{id}'
Currently I insert a new relationship by everytime checking, if it doesn't exist:
unless Relationship.exists?(:entry_id => entry.id, :tag_id => tag.id)
How could I implement such validation inside the Relationship model, so that it wouldn't allow to have more than one relationship between the same entry and tag?
class Relationship < ActiveRecord::Base
belongs_to :entry
belongs_to :tag
validates :tag_id, :uniqueness => { :scope => :entry_id }
end
Assuming your models look something like this:
class Entry < ActiveRecord::Base
has_many :relationships
has_many :tags, :through => :relationships
end
class Tag < ActiveRecord::Base
has_many :relationships
has_many :entries, :through => :relationships
end
class Relationship < ActiveRecord::Base
belongs_to :entry
belongs_to :tag
end
You could add a unique validation to your Relationship join model:
validates_uniqueness_of :tag_id, :scope => :entry_id
The validates_uniqueness_of method will ensure that the Relationship doesn't already exist, and the :scope option will scope the match to the given column. The SQL generated by rails via this validation will look like:
SELECT `relationships`.id
FROM `relationships`
WHERE (`relationships`.`tag_id` = <tag id> AND `relationships`.`entry_id` = <entry id>)
LIMIT 1
(which you'll notice is essentially the same SQL generated by your explicit use of Relationship.exists?(:entry_id => entry.id, :tag_id => tag.id)), and if a record is found, validation will fail.
As well, as with any case where you want to validate uniqueness, ensure that you have a unique key on tag_id, entry_id in your relationships table. See this article and the "Concurrency and integrity" of the API page I linked above for more info.
I want to access a legacy database schema from Rails. I have one table NAGIOS_OBJECTS with a primary key OBJECT_ID and one table NAGIOS_HOST_CHECKS that refers to NAGIOS_OBJECTS with a column HOST_OBJECT_ID. I thus defined the relations as follows:
class NagiosObject < ActiveRecord::Base
has_one :nagios_host_check, :foreign_key => :host_object_id, :primary_key => :object_id
end
class NagiosHostCheck < ActiveRecord::Base
belongs_to :nagios_object, :foreign_key => :host_object_id, :primary_key => :object_id
end
However, when calling a_nagios_object.nagios_host_check or a_nagios_host_check.nagios_object, I always get nil.
Any idea what is wrong with my code?
foreign_key and primary_key should be strings, not symbols
ex:
class NagiosObject < ActiveRecord::Base
has_one :nagios_host_check, :foreign_key => 'host_object_id', :primary_key => 'object_id'
end
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#M001317