I am facing a problem while I try to write a rails query using joins.
What I am building is a hash having candidate_answers grouped according to sections of a given question paper. (N.B. I have a pool of questions from where some are added in different sections of a questiion paper)
I have achieved my solution using map as:
exam_candidate.exam.question_paper.sections.includes(:questions).each do |section|
if section.questions.present?
section_question_hash[section] = candidate_answers.where(question_id: section.questions.map(&:id))
end
end
Since using the above creates a lot of database queries running on the background, it is not healthy to use, and thus I need to use joins. Also, I am able to write a SQL query for the same as
select b.name, group_concat(c.id) from sections b
left join question_papers_questions a on a.section_id = b.id
left join candidate_answers c on a.question_id = c.question_id
where a.question_paper_id = 3 and c.exam_candidate_id = 4
group by (b.name)
But while I try the same in rails I am having lots of issues with it.
Here's my model structure:
class ExamCandidate < ActiveRecord::Base
belongs_to :exam
belongs_to :candidate
has_many :candidate_answers, dependent: :delete_all
accepts_nested_attributes_for :candidate_answers
end
class Exam < ActiveRecord::Base
has_many :exam_candidates, dependent: :destroy
has_many :candidates, through: :exam_candidates
belongs_to :question_paper
end
class QuestionPaper < ActiveRecord::Base
has_many :exams, dependent: :nullify
has_many :exam_candidates, through: :exams
has_many :questions, through: :question_papers_questions
has_many :question_papers_questions
has_many :sections, dependent: :destroy
end
class QuestionPapersQuestion < ActiveRecord::Base
belongs_to :question
belongs_to :question_paper
belongs_to :section
end
class Question < ActiveRecord::Base
has_many :candidate_answers, through: :answers
has_many :exams, through: :question_papers
has_many :exam_candidates, through: :exams
has_many :question_papers_questions
has_many :question_papers, through: :question_papers_questions
end
class Section < ActiveRecord::Base
belongs_to :question_paper
has_many :questions, through: :question_papers_questions
has_many :question_papers_questions
end
class CandidateAnswer < ActiveRecord::Base
belongs_to :exam_candidate
belongs_to :question
end
I have given enough time on it, but being almost a newbie in rails is my disadvantage, if anyone can try it or suggest something it would be very helpful.
sections = exam.question_paper.sections.select("sections.name, group_concat(candidate_answers.id) as candidate_answer_ids")
.joins(:question_papers_questions).joins("inner join candidate_answers on question_papers_questions.question_id = candidate_answers.question_id")
.where(candidate_answers: {exam_candidate_id: id}, question_papers_questions: { question_paper_id: exam.question_paper_id })
.group("sections.name")
Related
I have a Products & Parts model which would each have multiple uploads, which are also polymorphic. Is it possible for me to have a single ItemUpload model to handle the association between the Products/Parts and Uploads, or do they need to be separate? I'd try myself just to see, but don't want to cause any potential headaches down the line! Note that I'm aware I need to do the source: and source_type: stuff to clean up the polymorphic association with has_many, but would like to clarify this point first before proceeding. Current models:
class Product < ApplicationRecord
has_many :uploads, as: :uploadable, dependent: :destroy
end
class Part < ApplicationRecord
has_many :uploads, as: :uploadable, dependent: :destroy
end
class Upload < ApplicationRecord
belongs_to :uploadable, polymorphic: true
end
What I would ideally like:
Class ItemUpload < ApplicationRecord
belongs_to :product, optional: true
belongs_to :part, optional: true
belongs_to :upload
end
Is that ok or would I need a separate ProductUpload and PartUpload model?
I would have thought your associations would look more like:
class Product < ApplicationRecord
has_many :item_uploads, as: :itemable, dependent: :destroy
has_many :uploads, through: :item_uploads
end
class Part < ApplicationRecord
has_many :item_uploads, as: :itemable, dependent: :destroy
has_many :uploads, through: :item_uploads
end
class Upload < ApplicationRecord
has_many :item_uploads
has_many :products, through: :item_uploads, source: :itemable, source_type: 'Product'
has_many :parts, through: :item_uploads, source: :itemable, source_type: 'Part'
end
Class ItemUpload < ApplicationRecord
belongs_to :itemable, polymorphic: true
belongs_to :upload
end
That should allow you to do:
product.uploads
part.uploads
upload.products
upload.parts
BTW, in reference to the link you provided:
Upload ≈ User
ItemUpload ≈ Membership
Product, Part ≈ Project, Group
The above follows the pattern in the linked article.
Doing an ActiveRecord join in RoR seems to work if I look at the generated SQL.
But what I can't figure out is why the result of that SQL isn't returned into the variable.
What I'm doing is:
class Book < ActiveRecord::Base
has_many :readings, dependent: :destroy
has_many :readers, :through => :readings
accepts_nested_attributes_for :readings
end
class Reader < ActiveRecord::Base
has_many :readings, dependent: :destroy
has_many :books, :through => :readings
accepts_nested_attributes_for :books
end
class Reading < ActiveRecord::Base
belongs_to :reader
belongs_to :book
end
Now, when asking:
result = Reading.where(:reader_id => rid, ).joins(:book).select(columns.collect{|c| c[:name]}.join(',')).flatten
It shows the correct generated SQL:
SELECT readings.id,books.title,books.author,readings.when FROM `readings` INNER JOIN `books` ON `books`.`id` = `readings`.`book_id` WHERE `readings`.`reader_id` = 2
BUT: the result variable only contains the values of the Reading record, NOT the fields of the joined table.
What am I missing?
I have made the association changes in question also:-
class Book < ActiveRecord::Base
has_many :readings, dependent: :destroy
has_many :readers, :through => :readings
accepts_nested_attributes_for :readings
end
class Reader < ActiveRecord::Base
has_many :readings, dependent: :destroy
has_many :books, :through => :readings
accepts_nested_attributes_for :books
end
class Reading < ActiveRecord::Base
belongs_to :reader
belongs_to :book
end
Query in this way:-
reader = Reader.find(rid)
result = reader.books.pluck(:name).join(',')
Ultimately, I've rewritten my helper class and fetched the various fields as I needed them. (as krishnar suggested)
Anyways: Thanx you guys for your contributions.
I am a newbie in Rails and have issues with Ransack:
This is model Project
class Project < ApplicationRecord
searchkick
belongs_to :company
belongs_to :m_category
has_many :project_industries, dependent: :destroy
has_many :m_industries, through: :project_industries
end
This is model Industry:
class Industry < ApplicationRecord
include M
belongs_to :m_industry_category
has_many :project_industries, dependent: :destroy, foreign_key: :industry_id
has_many :projects, through: :project_industries
end
And this is model IndustryCategory:
class IndustryCategory < ApplicationRecord
has_many :industries, dependent: :destroy,
foreign_key: :industry_category_id
has_many :projects, through: :industries
end
Now, I want to search the Project by IndustryCategory but I don't know how. please help me!! tks
You can use something like this
#industrty_category = IndustryCategory.find(params[:id])
#project = #industry_category.projects.all
In my database there are 4 models
class MasterPayment < ActiveRecord::Base
has_many :payments
end
class Payment < ActiveRecord::Base
belongs_to :master_payment
belongs_to :payable, polymorphic: :true
end
class TreatmentPlan < ActiveRecord::Base
has_many :payments, as: :payable
end
class ArbitraryBillableItem < ActiveRecord::Base
has_many :payments, as: :payable
end
What i would like to do is set up an association in MasterPayment that will associate payables to master payments.
Currently, the closest i could find was setting up the master payment model as follows
class MasterPayment < ActiveRecord::Base
has_many :payments
has_many :treatment_plans, through: :payments, source: :payable, source_type: "TreatmentPlan"
has_many :arbitrary_billable_items, through: :payments, source: :payable, source_type: "ArbitraryBillableItem"
def payables
self.treatment_plans + self.arbitrary_billable_items
end
end
The only problem i have with this is that it doesn't feel like the "correct" way to do it.
The only reason i can see for rails not having a solution to this is because you would presumably have to union the tables to return it in one sql statement.
Is there an alternative way to accomplish this that will make more use of the active record associations?
This seems too simple but would it work?
def payables
self.payments.joins(:treatment_plans, :arbitrary_billable_items).distinct
end
I was following the answer laid out at the link below to set up a many_to_many relationships on my Rails 4 app. (New to rails, here.)
Implement "Add to favorites" in Rails 3 & 4
I have Users and Exercises, and I want users to be able to have Favorite Exercises. I created a join table called FavoriteExercise with user_id and exercise_id as columns. I've got it populating, and it seems to be working fine, but I'm not able to use it to call directly to my favorites.
Meaning, I want to type:
user.favorite = #list of exercises that have been favorited
I get this error when I try to load that list in my browser:
SQLite3::SQLException: no such column: exercises.favorite_exercise_id:
SELECT "exercises".* FROM "exercises" INNER JOIN "favorite_exercises"
ON "exercises"."favorite_exercise_id" = "favorite_exercises"."id"
WHERE > "favorite_exercises"."user_id" = ?
My models:
class User < ActiveRecord::Base
has_many :workouts
has_many :exercises
has_many :favorite_exercises
has_many :favorites, through: :favorite_exercises, source: :exercises
class Exercise < ActiveRecord::Base
belongs_to :user
has_many :workouts, :through => :exercises_workouts
has_many :favorites
has_many :favorited_by, through: :favorite_exercises, source: :exercises
class FavoriteExercise < ActiveRecord::Base
has_many :exercises
has_many :users
I just tried switching FavoriteExercise to 'belongs_to' instead of 'has_many, because it seems maybe that's the way that should go? but then I get this error:
uninitialized constant User::Exercises
Just trying to figure out how to set up the tables and associations so I can call .favorites on a user and get all their favorites.
If you want the list of exercises of the user and at the same, the list of favorite exercise of the user, then I think your join table should just be users_exercises wherein it will list all the exercises by the users. To list the favorite exercises, just add a boolean field indicating if the exercise is a user favorite and add a :scope to get all the favorite exercises.
So in your migration file:
users_exercises should have user_id, exercise_id, is_favorite
Then in your model:
class User < ActiveRecord::Base
has_many :workouts
has_many :users_exercises
has_many :exercises, through: :users_exercises
scope :favorite_exercises, -> {
joins(:users_exercises).
where("users_exercises.is_favorite = ?", true)
}
class Exercise < ActiveRecord::Base
has_many :workouts, :through => :exercises_workouts
has_many :users_exercises
has_many :users, through: :users_exercises
class UsersExercise < ActiveRecord::Base
belongs_to :exercise
belongs_to :user
You just need to simplify your model logic as follow:
class User < ActiveRecord::Base
has_many :workouts
has_many :exercises
has_many :favorite_exercises
has_many :favorites, through: :favorite_exercises, class_name: "Exercise"
class Exercise < ActiveRecord::Base
belongs_to :user
has_many :workouts, :through => :exercises_workouts
has_many :favorite_exercises
has_many :favorited_by, through: :favorite_exercises, class_name: "User"
class FavoriteExercise < ActiveRecord::Base
belongs_to :favorited_by
belongs_to :favorite
Then you can call user.excercises or excercise.users in your User/Excercise instance.
user.excercises = #list of exercises that have been favorited
Is that the many-to-many relationship you want?