Question: Includes with a related element

Question

Includes with a related element

Answers 3
Added at 2017-11-08 15:11
Tags
Question

I would like to use the includes method with the related element of my Post

My Post can be associated with different type of element. And I use a value :cat to knows witch kind of element is associated.

The value work as this (cat: (1 => Message, 2=>Question, 3=>Task, 4=>Event) with the association has_one

Example : If post.cat == 3, I can call the task related with a method post.task

Now, I would like to optimize the SQL requests of my Post/Index with the method includes. But is not working for the moment. Can you help me to find the error of my code ?

Post_controller :

def index
  @posts = current_user.posts
  @posts.each do |post|
    if post.cat == 3
      @task = post.task.includes(:users)
    elsif post.cat ==  4
      @event = post.event.includes(:reminds)
    end
  end
end

Error: undefined method `includes'

Edit :

Post_model:

class Post < ApplicationRecord
  has_one :post_message, dependent: :destroy
  has_one :question, dependent: :destroy
  has_one :task, dependent: :destroy
  has_one :event, dependent: :destroy
end

Task_model :

class Task < ApplicationRecord
  belongs_to :post
  has_many :users_task, dependent: :destroy
  has_many :users, through: :users_task
end
Answers to

Includes with a related element

nr: #1 dodano: 2017-11-08 15:11

If you're getting an undefined method: 'includes' error, it means that either post.task or post.event are not returning ActiveRecord objects like your code is expecting. Are you sure there will always be values set for .task or .event at that point in execution? Are there any cases where that value might be nil or blank?

By the way, have you heard about 'polymorphic associations'? Defining an association as polymorphic allows you to associate records of arbitrary types with a specific column (by storing both object ID and class name on each record behind the scenes). It seems like this exactly matches your use case. It would be much easier to use the built-in mechanism than trying to do all the if-then switching based on category in your code.

nr: #2 dodano: 2017-11-08 16:11

Why are you using @posts.each ?

For me, the best solution for that is to find all the posts whith the defined cat to run the includes method. In your case, it would be like that :

@posts.where(cat: 1).includes(:message)
@posts.where(cat: 2).includes(:question)
@posts.where(cat: 3).includes(task: :users)
@posts.where(cat: 4).includes(event: :reminds)
nr: #3 dodano: 2017-11-09 11:11

Well, after many tries, I opted for a scope method to run the includes method. It's not a really elegant solution, but I think it's the best in my case.

So I'm preparing the scopes in my Post_Model:

scope :with_tasks, -> { where(cat: 3).includes(:user).includes(task: :users) }
scope :with_events, -> { where(cat: 4).includes(:user).includes(event: :reminds) }

And after, I render them in my index action like this :

@posts = current_user.posts.with_tasks + current_user.posts.with_events

So the code is generating 2 SQL Requests to find the posts (one for each category).

I think there is a way to join all that directly into a new global scope, but I don't know how. So if there is anyone knows that, he can edit the answer

Enjoy !

Source Show
◀ Wstecz