Ransack search for far away tables? - ruby-on-rails

I want to search orders by tags, but tags are associated with customers.... is it possible to do that with Ransack?
Order.rb
Class Order < ActiveRecord::Base
belongs_to :customer
....
Customer.rb
Class Customer < ActiveRecord::Base
has_many :orders
has_many :customers_tags
has_many :tags, through: :customers_tags
....
Tag.rb
Class Tag < ActiveRecord::Base
has_many :customers_tags
has_many :customers, through: :customers_tags
....

Yes, you can search through multiple associations. Just reference them as you would in the association name (plural for has_many, singular for belongs_to).
So in your example, searching for a field fieldname, you would use:
customer_tags_fieldname_cont
(Replace fieldname with whatever field you want to search.)

Related

Rails - Has many on model with multiple references

I have a model CompanyIntro which has two references to a Company:
class CompanyIntro < ApplicationRecord
belongs_to :company_one, class_name: "Company", foreign_key: "company_one_id"
belongs_to :company_two, class_name: "Company", foreign_key: "company_two_id"
...
I would like to do something like:
class Company < ApplicationRecord
has_many :company_intros, class_name: 'CompanyIntro', foreign_key: 'company_one_id'
has_many :company_intros, class_name: 'CompanyIntro', foreign_key: 'company_two_id'
...
But this is not valid
In my Company model, how to I create a has_many for both foreign keys? I am using Rails 6 which dos not allow custom sql for has_many (afaik). I also do not want to write a custom company_intros method on the Company model as I'm using another gem which looks for my has_many relationships.
You can't define has_many assocations where the foreign key is one of two columns. Its just not supported by ActiveRecord as the feature would add tons of complexity.
Using the same name for two assocations also just overwrites the previous assocation. If you want to have a single assocation here you need to add a join table.
class Company < ApplicationRecord
has_many :company_intro_participations
has_many :company_intros, through: :company_intro_participations
end
# for lack of a better name
class CompanyIntroParticipation < ApplicationRecord
belongs_to :company
belongs_to :company_intro
end
class CompanyIntro < ApplicationRecord
has_many :company_intro_participations
has_many :companies, through: :company_intro_participations
end
The alternative is creating a method which joins on company_one_id = companies.id OR company_two_id = companies.id but you will not be able to use that in the same way as an association when it comes to stuff like eager loading.

How do I customize the name of an indirect has_many association in Rails?

In Rails, we have the has_many feature:
class Product < ApplicationRecord
has_many :product_sales
has_many :states, through: :product_sales
end
Is there any way I can give a custom name to one of these has_manys?
For example: instead of accessing states from Product by using #product.states, I would like to access it using #product.states_where_it_is_sold.
Yes, there is a way. Do:
class Product < ApplicationRecord
has_many :product_sales
has_many :states_where_sold, through: :product_sales, source: :state
end

has_many :through Association working with one-to-many

rails 4.2
ruby 2.1
I have two very basic models (product and tag) with has_many association through another model (taggings).
I have another model (category) with one-to-many connection with the aforementioned model (product).
Question:
How to show in view the tag list of products with a specific product's category?
In other words: Is it possible to list all tags from a particular category of product?
Models:
class Product < ActiveRecord::Base
has_many :taggings
has_many :tags, through: :taggings
belongs_to :category, counter_cache: true
end
class Tag < ActiveRecord::Base
has_many :taggings
has_many :products, through: :taggings
end
class Tagging < ActiveRecord::Base
belongs_to :product
belongs_to :tag, counter_cache: :products_count
end
class Category < ActiveRecord::Base
has_many :products
end
Quickest way is category_object.products.map(&:tags).flatten . Can be improved. :)
category has many products and product has many tags. Mapping tags method on each product. Flatten to remove duplicates.
You can add a product_tags association to the Category class:
class Category < ActiveRecord::Base
has_many :products
has_many :product_tags, -> { uniq }, through: :products
end
When you access the product_tags association, Rails will use a SELECT DISTINCT query so you won't end up with duplicate tags and the DB will eliminate duplicates.
If the above doesn't feel natural for your model, then you can also use the following (assuming c is a Category instance):
Tag.joins(:products).where(products: { category: c})
The DB query will be very similar to the other example.

How make categories relationships with several model?

I have next models: Articles, Announcements, Catalogs and Media.
For item of every model I need to create a subcategory and a category. I will plan to create a relationship table with two columns: parend_id and child_id, and a column for every model with category_id.
How many relationship models I should create?
One for all?
Or one relationship model for every model?
No need to create a relationship model instead just use belongs_to relationship
because you said following i guess belongs_to relations should do it
For item of every model I need create subcategory and category.
So just add subcategory_id and category_id in your tables: Articles, Announcements, Catalogs and Media and establish has_many belongs_to relationship
EDIT But if you insist for relationship model then I would suggest to use one relationship model for every model.
Last EDIT: I really think you should use belongs_to, has_many relation
Class Article < ActiveRecord::Base
belongs_to :category
belongs_to :subcategory
end
class Category < ActiveRecord::Base
has_many :articles
# the same relation(has_many) will be for Announcements, Catalogs and Media
has_many :subcategories
end
class Subcategory < ActiveRecord::Base
has_many :articles
belongs_to :category
end
In this way you can get all articles using category.subcategories.articles
If the category has only one Subcategory then change the relation between them to has_one and your syntax will become category.subcategory.articles
I would personally use a has_many :through relationship with a polymorphic association in a single join table:
#app/models/article.rb
Class Article < ActiveRecord::Base
has_many :categorizable_categories
has_many :categories, through: :categorizable_categories
end
class Article < ActiveRecord::Base
has_many :tags, as: :taggable,dependent: :destroy
has_many :categories, through: :tags
end
class Category < ActiveRecord::Base
has_many :tags, dependent: :destroy
has_many :articles, through: :tags, source: :taggable, source_type: 'Article'
end
class Tag < ActiveRecord::Base
belongs_to :taggable, polymorphic: true
belongs_to :category
end
This will allow you to call the categories for each model with the likes of #article.categories
To achieve the parent & child categories, I'd recommend using something like the Ancestry gem - you'd set up an ancestry column in your join table - basically allowing you to create relations between a model's categories directly

Recording the date an object is added to a has_many collection

Users on my site each have one list, which consists of a different type of users. I'm using a has_many through relationship to do this as follows:
List.rb:
class List < ActiveRecord::Base
belongs_to :company
has_many :list_applicants
has_many :applicants, through: :list_applicants
end
Applicant.rb:
class Applicant < ActiveRecord::Base
has_many :list_applicants
has_many :lists, through: :list_applicants
end
ListApplicant.rb
class ListApplicant < ActiveRecord::Base
attr_accessible :applicant_id, :list_id
belongs_to :applicant
belongs_to :list
end
Company.rb:
class Company < ActiveRecord::Base
has_one :list
end
When a user adds another user to their list, I'd like to record the date the user is added so they can sort their list of users by date added. What is the best way to do this?
You can use the created_at field of the ListApplicant model if it has one. If not, you may add manually a similar field.
UPDATE:
You can access the field by specifying both applicant and list like this:
#applicant.list_applicants.where(list_id: #list.id).first.created_at

Resources