ActiveRecord: как включить другую модель на основе Enum? - PullRequest
2 голосов
/ 30 октября 2019

Имейте модель поста с Enum

// Post.rb

enum category: {
   job: 'job',
   conference: 'conference'
}

, а организация имеет и принадлежит ко многим постам (через объединяющую таблицу)

Org.includes(:posts).where(category: Post.categories[:job])

выглядит так, чтоего пытается назвать категорию на орг. Есть ли способ написать это, чтобы вернуть только Организацию с включенным постом, где пост содержит перечисление строки 'job'?

1 Ответ

2 голосов
/ 30 октября 2019

tl; др использовать

Org.includes(:posts).where(posts: {category: :job})

Более длинный ответ ...

Я думаю, стоит отметить, что ваш вопрос не 'на самом деле не имеет ничего общего с enums. Это также не связано с «включением другой модели». Что вы действительно пытаетесь сделать, это Укажите условия для объединяемых таблиц , о которых вы можете прочитать в руководстве по интерфейсу запроса Active Record .

Проблемычто вы исказили свой запрос ActiveRecord:

Org.includes(:posts).where(category: Post.categories[:job])

Основная форма того, что у вас сейчас есть:

Model.where(attribute: 'value')

The:

.includes(:joined_models)

.. .bit не меняет существенную форму. Итак, ActiveRecord вернет все Model записей, где attribute имеет value. Или, в вашем случае, все Org модели, где category равно job.

Но это не то, что вы хотите. Вы хотите, чтобы все Orgs имели Posts, где Post category равно job. (Или, я полагаю, «все организации с должностями».)

Вот где появляется бит .includes(:joined_models): он позволяет указать условия для joined_models, которые в своей существенной форме выглядят следующим образом:

Model.includes(:joined_models).where(joined_models: {attribute: 'value'})
                                     ^^^^^^^^^^^^^

Или, в вашем случае:

Org.includes(:posts).where(posts: {category: Post.categories[:job]})

Или, как говорит му в комментариях:

Org.includes(:posts).where(posts: {category: :job})

Теперь я не знаю, чтоконтекст, в котором вы находитесь, но где бы вы ни находились, приведенный выше код требует, чтобы ваш контекст много знал о Org и о том, как он относится к Post и атрибутам Post, что в целом не очень хорошо. Итак, я предлагаю вам добавить метод к Org, который позволит вам отделить знания Org в вашем контексте:

class Org < ApplicationRecord 

  class << self

    def with_job_posts
      includes(:posts).where(posts: {category: :job}})
    end

  end

end

И теперь вы можете просто сделать:

Org.with_job_posts

... и вернуть "все организации с должностями". И ваш контекст должен знать намного меньше о Post и его атрибутах.

Post также имеет категорию conference. Итак, вы можете сделать:

class Org < ApplicationRecord 

  class << self

    def with_job_posts
      includes(:posts).where(posts: {category: :job}})
    end

    def with_conference_posts
      includes(:posts).where(posts: {category: :conference}})
    end

  end

end

Но, если ваши Post categories начнут расти, это станет утомительным. Вместо этого выполните:

class Org < ApplicationRecord 

  Post.categories.each do |post_category|
    define_singleton_method("#{post_category}"_posts) do 
      includes(:posts).where(posts: {category: post_category.to_sym})
    end
  end

end

И теперь у вас будет любое количество методов, таких как:

Org.with_job_posts
Org.with_conference_posts
Org.with_some_other_type_of_posts

Super! Проверьте этот Q & A для получения дополнительной информации от Jörg W Mittag .

BTW, который выглядит как потенциально необычный способ использования enum. В документах написано:

Наконец, также можно явно отобразить связь между атрибутом и целым числом базы данных с помощью хеша:

class Conversation < ActiveRecord::Base
  enum status: { active: 0, archived: 1 }
end

Я всегда полагал, что отображаемое перечисление должно было использовать целые числа в качестве значений, а не строки. Интересно.

...