Rails query include not working - ruby-on-rails

In my app I have 3 models: Item, Category and Categorization defined as below:
class Item < ActiveRecord::Base
attr_accessible :name, :description
has_many :categorizations
has_many :categories, :through => :categorizations
end
class Category < ActiveRecord::Base
attr_accessible :name, :description, :parent, :children, :items, :parent_id
has_many :children, :class_name => "Category", :foreign_key => "parent_id", :dependent => :nullify
belongs_to :parent, :class_name => "Category"
has_many :categorizations
has_many :items, :through => :categorizations
end
class Categorization < ActiveRecord::Base
attr_accessible :category, :item
belongs_to :category
belongs_to :item
end
However, doing this:
Category.where(:parent_id => self.id).includes(:items)
won't return me the items associated to the category. What am I missing here?

Try this:
Category.where(parent_id: id).joins(:items)

Try this:
Category.where(:parent_id => id).collect(&:items)
It will return the items of all categories matching the where clause.

Related

Rails: How to create a has_many through association with an alias/class_name

I am trying to transform my HABTM to has_many through relations. Sometimes I have to connect the same models in different ways. For example to specify different roles for authors.
With a HABTM I would do this through the declaration of a class_name option. Just like:-
class Project < ActiveRecord::Base
has_and_belongs_to_many :curators, :class_name => :author, :through => :projects_curators
end
class ProjectsCurator < ActiveRecord::Base
attr_accessible :project_id, :author_id
belongs_to :project
belongs_to :author
end
class Author < ActiveRecord::Base
has_and_belongs_to_many :projects, :through => :projects_curators
end
But when I transform everything into a has_many through:
class Project < ActiveRecord::Base
has_many :project_curators
has_many :curators, :class_name => :author, :through => :project_curators
end
class ProjectCurator < ActiveRecord::Base
attr_accessible :project_id, :author_id
belongs_to :project
belongs_to :author
end
class Author < ActiveRecord::Base
has_many :project_curators
has_many :projects, :through => :project_curators
end
I get: Could not find the source association(s) :curator or :curators in model ProjectCurator. Try 'has_many :curators, :through => :project_curators, :source => <name>'. Is it one of :author or :project?
When I add :source
has_many :curators, :class_name => :author, :through => :project_curators, :source => :author
I get:
uninitialized constant Project::author
How can I get this working? Thank you very much in advance!
Understanding :source option of has_one/has_many through of Rails should help you out
When you declare the source you're not declaring the class of the has_many relationship, but the name of the relationship you're using as the middle man. In your case, try:
has_many :curators, :through => :project_curators, :source => :author
As someone in the above post states:
Most simple answer - the name of the relationship in the middle

Two associations between same tables with Ruby On Rails

I have an app with Two Models Stadium & Team, they have a many-to-many relationship and are joined in the middle by a join table.
My current structure looks like this:
class Team < ActiveRecord::Base
has_many :stadiumteams, :class_name => 'StadiumTeam'
has_many :stadiums, :through => :stadiumteams
end
class Stadium < ActiveRecord::Base
has_many :stadiumteams, :class_name => 'StadiumTeam'
has_many :teams, :through => :stadiumteams
end
class StadiumTeam < ActiveRecord::Base
belongs_to :stadium
belongs_to :team
end
This relationship show “current residents for a stadium”. But now I also want to have a relationship that can display old residents.
For example Tottenham (Team) used to play at White Hart Lane (Stadium) But now play at (Wembley) Stadium.
I am planning to do the following. But I am unsure if this will work or if there is a better way to do it?
Team
class Team < ActiveRecord::Base
has_many :stadiumteams, :class_name => 'StadiumTeam'
has_many :stadiums, :through => :stadiumteams
#has_many :oldstadiums, :class_name => 'OldStadium'
#has_many :stadiums, :through => :oldstadiums
end
Stadium
class Stadium < ActiveRecord::Base
has_many :stadiumteams, :class_name => 'StadiumTeam'
has_many :teams, :through => :stadiumteams
#has_many :oldstadiums, :class_name => 'OldStadium'
#has_many :teams, :through => :oldstadiums
end
New Join Table
class OldStadium < ActiveRecord::Base
belongs_to :stadium
belongs_to :team
end
Any help is very appreciated!
EDIT: (Input fields)
Here's how the code for my inputs currently looks:
<%= f.association :teams, label: 'Current Residents', class:'select2-field', placeholder: "Select teams", collection: #teams, input_html: { multiple: true }, hint: 'Select one or multiple teams.'%>
<%= f.association :teams, label: 'Old Residents', class:'select2-field', placeholder: "Select teams", collection: #teams, input_html: { multiple: true }, hint: 'Select one or multiple teams.' %>
And my params
def stadium_params
params.require(:stadium).permit(:name, :capacity, :city, :country, :location_name, :address, :longitude, :latitude, :image, :surface, :official_opening_date, :cost, :web_url, :also_known_as, :record_attendance, :team_ids => [])
end
You can define relations with a lambda to append an additional where clause in the relation:
class Team < ActiveRecord::Base
has_many :stadiumteams, :class_name => 'StadiumTeam'
has_many stadiums, :through => :stadiumteams
has_many :current_stadiums, ->{ where(stadiumteams: { current_home: true }) }, :through => :stadiumteams, :class_name => 'Stadium', :source => :stadium
has_many :previous_stadiums, ->{ where(stadiumteams: { current_home: false }) }, :through => :stadiumteams, :class_name =>'Stadium', :source => :stadium
end
class Stadium < ActiveRecord::Base
has_many :stadiumteams, :class_name => 'StadiumTeam'
has_many :teams, :through => :stadiumteams
has_many :current_teams, ->{ where(stadiumteams: { current_home: true }) }, :through => :stadiumteams, :class_name => 'Team', :source => :team
has_many :previous_teams, ->{ where(stadiumteams: { current_home: false }) }, :through => :stadiumteams, :class_name =>'Team'. :source => :team
end
class StadiumTeam < ActiveRecord::Base
belongs_to :stadium
belongs_to :team
end
And then you should be able to do (in rails console):
team = Team.first
stadium = Stadium.first
team.previous_stadiums <<
team.save
team.reload.previous_stadiums.include?(stadium)
# => should return true
So this way you can simply use in your UI a input for the association :previous_teams and :current_teams:
f.association :current_teams, #...
There are several ways to fulfill your requirement. Your approach also enough, but for efficiency, since the number of Stadium is small ( thousands ), you can use STI for Stadium entity.
class Stadium < ActiveRecord::
has_many :stadiums_teams
has_many :teams, through: :stadiums_teams
end
class OldStadium < Stadium; end
class RecentStadium < Stadium; end
class StadiumTeam < ActiveRecord::Base
belongs_to :stadium
belongs_to :team
end
class Team < ActiveRecord::Base
has_many :stadiums_teams
has_many :stadiums, through: :stadiums_teams, source: :stadium
has_many :old_stadiums, through: :stadiums_teams,
source: :stadium, class_name: OldStadium.name
has_many :recent_stadiums, through: :stadiums_teams,
source: :stadium, class_name: RecentStadium.name
end
So, in most case, you will work with OldStadium / RecentStadium model, but when you want to check if a team ever played at a specific stadium or not, just use Stadium model.

uninitialized constant ruby model

I have 3 classes:
1. Article
class Article < ActiveRecord::Base
has_many :categories_articles
has_many :subcategories_articles
has_many :categories, :through => :categories_articles
has_many :subcategories, :through => :subcategories_articles
end
2.Category
class Category < ActiveRecord::Base
has_many :articles
has_many :categories_articles
has_many :categories_subcategories
has_many :subcategories, :through => :categories_subcategories
has_many :articles, :through => :categories_articles
end
3.The third class is the union of the first two, category_article
class CategoryArticle < ActiveRecord::Base
belongs_to :category
belongs_to :article
end
so, when i called in the view
<% f.collection_select(:category_ids, Category.all, :id, :name, {include_blank:"selects"},{class:'form-control select2 multi', :required=>true, multiple: true}) %>
I get this error:
uninitialized constant Article::CategoriesArticle
The same goes for the class Subcategory and subcategory_article
Try
has_many :category_articles
And
has_many :subcategory_articles
You'll also have to change these:
has_many :categories, :through => :categories_articles
has_many :subcategories, :through => :subcategories_articles
To something like:
has_many :categories, :through => :category_articles
has_many :subcategories, :through => :subcategory_articles
Rails doesn't pluralize both components of the composite table names. Just the last component.

Many-to-many relationships for models

I generated namespaced models and how can i set many-to-many
relationships, category has many posts, post has many categories
rails g model Blog::Post body:text, title:string
rails g model Blog::Category title:string
rails g model Blog::CategoryPost post_id:integer, category_id:integer
and my models looks like
class Blog::Category < ActiveRecord::Base
attr_accessible :title
has_many :posts, :class_name => 'Blog::Post', :through => :blog_categories_posts
end
class Blog::CategoryPost < ActiveRecord::Base
belongs_to :category, :class_name => 'Blog::Category'
belongs_to :post, :class_name => 'Blog::Post'
end
class Blog::Post < ActiveRecord::Base
attr_accessible :body, :title
has_many :categories, :class_name => 'Blog::Category', :through => :blog_categories_posts
end
This should work. You need to specify relation to intermediate table.
class Blog::Category < ActiveRecord::Base
attr_accessible :title
has_many :categories_posts, :class_name => 'Blog::CategoryPost'
has_many :posts, :class_name => 'Blog::Post', :through => :categories_posts
end
class Blog::CategoryPost < ActiveRecord::Base
belongs_to :category, :class_name => 'Blog::Category'
belongs_to :post, :class_name => 'Blog::Post'
end
class Blog::Post < ActiveRecord::Base
attr_accessible :body, :title
has_many :categories_posts, :class_name => 'Blog::CategoryPost'
has_many :categories, :class_name => 'Blog::Category', :through => :categories_posts
end
Try adding the associations to the CategoryPosts to the Category and Post models. eg:
class Blog::Category < ActiveRecord::Base
...
has_many :blog_category_posts, :class_name => "Blog::CategoryPost"
...
end
I believe you need to do this for both the Category and the Post models.

Ruby on Rails: filter associations based on several relations and conditions

In my application I the have the models Category, Item, Property and PropertyValuation. The idea is that a category contains items, and an item has several properties. PropertyValuation purpose is to store the property value for the specific items. The models are defined as above:
class Category < ActiveRecord::Base
attr_accessible :name, :description, :parent, :children, :items, :parent_id
has_many :children, :class_name => "Category", :foreign_key => "parent_id", :dependent => :nullify
belongs_to :parent, :class_name => "Category"
has_many :categorizations
has_many :items, :through => :categorizations
end
class Item < ActiveRecord::Base
attr_accessible :name, :description, :property_valuations, :barcode
has_many :property_valuations, :dependent => :destroy
has_many :properties, :through => :property_valuations
has_many :categorizations
has_many :categories, :through => :categorizations
end
class Property < ActiveRecord::Base
attr_accessible :name, :description, :value_type, :unit, :unit_id
has_many :property_valuations, :dependent => :destroy
has_many :items, :through => :property_valuations
has_many :property_ranges, :dependent => :destroy
belongs_to :unit
end
class PropertyValuation < ActiveRecord::Base
attr_accessible :property, :item, :value, :categorization
belongs_to :property
belongs_to :item
end
Now my question, I've successfully managed to filter categories items by name by doing this:
#category.items.where("lower(items.name) like ?", "%#{params[:keywords].downcase}%")
But now I also want to filter those items depending on the associated property value. I receive the property ids and the values for each property (exact value, minimum value or maximum value), and the idea is to build the query dynamically. Given my models, how can I do this for example: I want the items whose name contains "foo", and where property with id=1 has value 2, property with id=2 has value<10, and property with id=8 has value>2 and value<5.
You can drive the search off the PropertyValuation model and join it to the product and category models
valuations = PropertyValuation.joins(:item)
.where(value: 2, property_id: 1)
.where('lower(items.name) LIKE ?', "%#{params[keywords].downcase}%")
items = valuations.map(&:item)
There a gems that make this thing a long easier, one is Ransack https://github.com/ernie/ransack
Item.search(name_contains: params[:keywords],
product_valuations_value_equals: 2,
product_valuations_property_id: 1)

Resources