I have a HMT relationship between tab_projects, tab_proj_platforms, and ref_platforms. In my tab_projects.index() I wrote this assignment:
#tab_projects = TabProject.includes(:ref_platforms)
And I want to return a list of projects along with their associated platforms. The SQL generated was correct and I validated it directly in the database. The record results contained columns from both tab_projects and ref_platforms, which was what I wanted. However my results in #tab_projects only contained the columns in TabProject and nothing from RefPlatform.
I was perusing for potential solutions and it seemed like what I found simply would iterate the collection of #tab_projects.ref_platforms, but I am returning everything as a JSON document so I want to collect everything at runtime.
Just in case my model associations contain some bug I've included them below:
tab_project.rb:
class TabProject < ApplicationRecord
has_many :tab_proj_platforms
has_many :ref_platforms, through: :tab_proj_platforms
end
tab_proj_platform.rb:
class TabProjPlatform < ApplicationRecord
belongs_to :tab_project
belongs_to :ref_platform
end
ref_platform.rb:
class RefPlatform < ApplicationRecord
has_many :tab_proj_platforms
has_many :tab_projects, through: :tab_proj_platforms
end
How can I save projects and their associated platforms into #tab_projects so I can dump them into a JSON document?
inside tab_project.rb: create scope as follow
scope :project_and_platform, lambda { |id_tab_project|
joins(tab_proj_platform: :ref_platform).select('ref_platforms.*','tab_projects.*').
where("tab_projects.id = ?", id_tab_project)
}
from your TabProject controller, you can access scope as follow
#tab_project_with_platforms = TabProject.project_and_platform(params[:id])
Related
I have the follow set up in my models:
class Project < ApplicationRecord
has_many :plans
end
class Plan < ApplicationRecord
belongs_to :project
has_many :documents
end
class Document < ApplicationRecord
belongs_to :plan
end
I'm trying to make a query in which I can easily return all nested associated records. For example, I want to be able to do something like: Project.documents and get all documents for a project. I've tried this with an includes like so:
Project.includes({plans: [:documents]})
but that doesn't appear to give me what I want when I call Project.documents. I really only want all documents for a project, so don't really need to include plans. Is there an easy way todo this in Rails 6?
The relationship between a document and a plan goes first through project, so you can use joins, specifying that a document belongs to a plan, and that a plan belongs to a project. After that you can filter the rows by selecting the plans (whose document belongs to) having the project_id equals ...:
Document.joins(plan: :project).where(plans: { project_id: ... })
I have two classes in my Rails API database, that I have just created a joining table for. I had seeded both classes previously so they have data. Now I would like to seed the joining class with id's from the existing data.
This is what the models look like for the first two classes:
class Stakeholder < ApplicationRecord
has_many :project_stakeholders
has_many :projects, through: :project_stakeholders
end
class Project < ApplicationRecord
has_many :project_stakeholders
has_many :stakeholders, through: :project_stakeholders
end
This is the model for my joining class:
class ProjectStakeholder < ApplicationRecord
belongs_to :project
belongs_to :stakeholder
end
Now I have two questions:
How to populate the joint class with data from the two first classes (project_id and stakeholder_id).
How can I check in the Rails console for values in the ProjectStakeholder class?
I have researched and tried different options from Stackoverflow but they don't seem to work. Rails guides do not provide an answer either, from what I have seen. Would be very grateful for some ideas!
You can just assign stakeholders to the project after creation
Project.first.stakeholders << Stakeholder.limit(5)
Project.second.stakeholders << Stakeholder.limit(5).offset(5)
You can check it in console in different ways:
Project.first.stakeholders.pluck(:id)
ProjectStakeholder.first
I have a user in my application that can have multiple assessments, plans, and materials. There is already a relationship between these in my database. I would like to show all these in a single tab without querying the database too many times.
I tried to do a method that joins them all in a single table but was unsuccessful. The return was the following error: undefined method 'joins' for #<User:0x007fcec9e91368>
def library
self.joins(:materials, :assessments, :plans)
end
My end goal is to just itterate over all objects returned from the join so they can be displayed rather than having three different variables that need to be queried slowing down my load times. Any idea how this is possible?
class User < ApplicationRecord
has_many :materials, dependent: :destroy
has_many :plans, dependent: :destroy
has_many :assessments, dependent: :destroy
end
class Material < ApplicationRecord
belongs_to :user
end
class Assessment < ApplicationRecord
belongs_to :user
end
class Plan < ApplicationRecord
belongs_to :user
end
If all you want to do is preload associations, use includes:
class User < ApplicationRecord
# ...
scope :with_library, -> { includes(:materials, :assessments, :plans) }
end
Use it like this:
User.with_library.find(1)
User.where(:name => "Trenton").with_library
User.all.with_library
# etc.
Once the associations are preloaded, you could use this for your library method to populate a single array with all the materials, assessments and plans of a particular user:
class User < ApplicationRecord
# ...
def library
[materials, assessments, plans].map(&:to_a).flatten(1)
end
end
Example use case:
users = User.all.with_library
users.first.library
# => [ ... ]
More info: https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations
Prefer includes over joins unless you have a specific reason to do otherwise. includes will eliminate N+1 queries, while still constructing usable records in the associations: you can then loop through everything just as you would otherwise.
However, in this case, it sounds like you're working from a single User instance: in that case, includes (or joins) can't really help -- there are no N+1 queries to eliminate.
While it's important to avoid running queries per row you're displaying (N+1), the difference between one query and three is negligible. (It'd cost more in overhead to try to squish everything together.) For this usage, it's just unnecessary.
I've got two models with a has_many / has_many relationship. I have a variable exp_ids which is an array of integers representing the id's of some ExperienceLevel records. I need to write a query that will select all JobDescriptions that have an ExperienceLevel with one of those ids.
The query must work on an existing ActiveRelation object called job_descriptions, which is being passed through some flow controls in my controller to filter the results based on my params.
I've tried these queries below and some other variations, but with little success. As far as I can tell, ActiveRecord thinks that experience_levels is an attribute, which is causing it to fail.
job_descriptions.where(experience_levels: exp_ids)
job_descriptions.joins(:experience_levels).where(experience_levels.id: exp_ids)
job_descriptions.joins(:experience_levels).where(experience_levels: exp_ids)
job_descriptions.joins(:experience_levels).where("experience_levels.id IN exp_ids")
job_descriptions.includes(:experience_levels).where("experience_levels.id = ?", exp_ids).references(:experience_levels)
Here are my models:
class JobDescription < ActiveRecord::Base
has_many :job_description_experience_levels
has_many :experience_levels, through: :job_description_experience_levels
end
class JobDescriptionExperienceLevel < ActiveRecord::Base
belongs_to :job_description
belongs_to :experience_level
end
class ExperienceLevel < ActiveRecord::Base
has_many :job_description_experience_levels
has_many :job_descriptions, through: :job_description_experience_levels
end
I'm not sure if what I want to do is even possible. I've used a similar approach for another job_description filter where I selected the company_id, but in the case, company_id was an attribute of JobDescription.
Try this:
job_descriptions.joins(:job_description_experience_levels).where(job_description_experience_levels: { experience_level_id: exp_ids })
job_descriptions.joins(:experience_levels).where(experience_levels: {id: exp_ids})
Try this one. Note the lack of plural on the experience level.id
job_descriptions.includes(:experience_levels).where("experience_level.id = ?", exp_ids).references(:experience_levels)
I'm new to Rails, and while writing Active Record queries, I notice that all columns of all associated tables are being retrieved. I would like to tell Active Record which fields from which tables ought to be retrieved. How would go about doing that?
My models and their associations are as follows:
class User < ActiveRecord::Base
has_one :profile
has_many :comments
has_many :posts
end
class Profile < ActiveRecord::Base
belongs_to :user
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
end
I'm following the Rails Edge Guides, and when I try to use select("users.id, profiles.first_name, profiles.last_name, comments.comment") to specify the field lists, I get a deprecation warning on the Rails console (and the SQL query that is run is a LEFT OUTER JOIN of all tables involved, but it still includes all columns):
DEPRECATION WARNING: It looks like you are eager loading table(s) (one of: users, posts) that are referenced in a string SQL snippet. For example:
Post.includes(:comments).where("comments.title = 'foo'")
Currently, Active Record recognizes the table in the string, and knows to JOIN the comments table to the query, rather than loading comments in a separate query. However, doing this without writing a full-blown SQL parser is inherently flawed. Since we don't want to write an SQL parser, we are removing this functionality. From now on, you must explicitly tell Active Record when you are referencing a table from a string:
Post.includes(:comments).where("comments.title = 'foo'").references(:comments)
If you don't rely on implicit join references you can disable the feature entirely by setting `config.active_record.disable_implicit_join_references = true`. (called from irb_binding at (irb):34)
Check if following work for you
Class User < ActivcRecord::Base
default_scope select("column1, column2, column3")
end
Buried deep inside the Rails Edge Guides for Active Record Query Interface, I found the answer. The trick is to use scopes for the particular association type where you want to restrict the retrieved fields.
Quoted directly from the guide:
4.1.3 Scopes for belongs_to
There may be times when you wish to customize the query used by belongs_to. Such customizations can be achieved via a scope block. For example:
class Order < ActiveRecord::Base
belongs_to :customer, -> { where active: true },
dependent: :destroy
end
You can use any of the standard querying methods inside the scope block.
So, adding a select method to the above scope, with the list of fields you want retrieved will do the trick.