has_many, :through => :multiple - ruby-on-rails

I would like a relationship along the lines of
has_many :foos, :through => :multiple
models
Site:
has_many :pages
has_many :files
Page:
belongs_to :site
has_many :legacy_url_redirects, as: :redirect_resourceable
File:
belongs_to :site
has_many :legacy_url_redirects, as: :redirect_resourceable
LegacyUrlRedirects
belongs_to :redirect_resourceable, :polymorphic => true
What I would like to do is in the Site model add something like:
has_many :legacy_url_redirects, :through => [:pages, :files]
Or an equally good alternative.
Thanks.
EDIT:
Here is what i did:
Site model:
def legacy_url_redirects(options = {})
file_where = {"files.site_id" => self.id}.merge(options)
page_where = {"pages.site_id" => self.id}.merge(options)
LegacyUrlRedirect.includes(:file).where(file_where) + LegacyUrlRedirect.includes(:page).where(page_where)
end
I also added the :pages and :files relationships to the LegacyUrlRedirects model with help from: https://stackoverflow.com/a/16124295/1141264
SECOND EDIT:
I refactored the fix a little so that its a manually written LEFT OUTER JOIN between the three tables. Also not specifying the select part of the query when doing a join implies #readonly = true on the records returned.

Related

Naming polymorphic relationships well

This question is about the naming style of polymorphic relationships.
My database has three types of person: a 'Company', a Client, and an Employee. Each of the three are in polymorphic relationships with tasks and events, and projects.
According to the Rails guides, this would be done like (I've omitted some classes for brevity):
Person.rb
has_many :tasks, :as => :taskable
has_many :events, :as => :eventable
has_many :projects, :as => :projectable # awkward names
Task.rb
belongs_to :taskable, :polymorphic => true
These lead to the rather strange:
#person = #task.taskable
I feel that the following would be far more grammatical and elegant... would it work, and if so, is there a reason that official sources use words like projectable rather than words like owner?
Person.rb
has_many :tasks, :as => :owner
has_many :events, :as => :owner
has_many :projects, :as => :owner
Task.rb
belongs_to :owner, :polymorphic => true
This creates the elegant:
#person_1 = #task.owner
#person_2 = #project.owner
I personally try to keep it as generic as possible.
So :as => :owner does make more sense to me.
In case of doubt, I'd just use
:as => :parent
which I've already seen in some projects.

rails has_one of a has_many association

I have a product, and a product can have many images. This is through an associations table. However, I would like a product to have one main image.
I know this is very easy to do with a method in the model, but I want it to be an association so that I can preload it in the query by using include.
Models:
class Product < ActiveRecord::Base
has_many :image_associations, :as => :imageable
has_many :images, :through => :image_associations
end
class ImageAssociation < ActiveRecord::Base
belongs_to :image
belongs_to :imageable, :polymorphic => true
end
class Image < ActiveRecord::Base
has_many :image_associations
end
In the ImageAssociation table, there is a boolean column called 'feature' which associates an image as the 'main' image.
One of the ways I've thought about doing this is adding a main_image_id column to the products table, and then adding to the Image model:
belongs_to :image, :class => "Image", :foreign_key => "main_image_id"
However, this doesn't allow for any fallback to the other has_many images if the main image is nil -- which I'd like the association to load.
Which is why I was hoping for something in the images model like:
has_one :image, :through => :images, :conditions => 'feature = true', :order => 'created_at DESC'
But that gives me an association error.
Is there any way I can edit that has_one, or do I really need to run a rake task to push an image into every main_image_id field, and then add a validation for future products to make sure a main image has been added?
EDIT:
Should I be using a has_and_belongs_to_many association instead?
You're almost there, I think, although I'm not so clear on polymorphic associations. I think you want the following:
has_one :main_image_assoc, class => "ImageAssociation", :conditions => 'feature = true', :order => 'created_at DESC', :as => :imageable
has_one :image, :through => :main_image_assoc

rails how to add in :include for a multiple index key association

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}'

rails - using :select(distinct) with :has_many :through association produces invalid SQL

User
has_many :posts
has_many :post_tags, :through => :posts
PostTag
belong_to :post
belongs_to :tag
scope :distincttag, :select => ('distinct post_tags.tag_id')
with Rails 3.0.4, i get invalid SQL:
SELECT post_tags.*, distinct tag_id...
at least one other person experienced the same problem: http://www.ruby-forum.com/topic/484938
feature or a bug?
thanks
Does not look like the right thing to put on a scope.
Maybe you are trying to accomplish this:
class PostTag < ...
belong_to :post
belongs_to :tag
def distincttag
find(:all, :select => 'distinct tag_id')
end
end
Edit: now that I know what you need:
User
has_many :posts
has_many :post_tags, :through => :posts, :select => 'distinct tags.*'
# or, if you are not worried about database overhead:
has_many :post_tags, :through => :posts, :uniq => true
Reference: http://blog.hasmanythrough.com/2006/5/6/through-gets-uniq

Thinking Sphinx - RuntimeError: Missing Attribute for Foreign Key

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

Resources