Включение ассоциации, если она существует в запросе рельсов - PullRequest
1 голос
/ 02 сентября 2011

Обновление: это может быть что-то, что просто невозможно сделать. Смотрите это

TLDR: Как вы условно загружаете ассоциацию (скажем, загружаете ассоциацию только для текущего пользователя), в то же время включая записи, которые вообще не имеют этой ассоциации?

Rails 3.1, вот примерно модель, с которой я работаю.

class User
  has_many :subscriptions
  has_many :collections, :through => :subscriptions
end

class Collection
  has_many :things
end

class Thing
  has_many :user_thing_states, :dependent => :destroy
  belongs_to :collection
end

class Subscription
  belongs_to :user
  belongs_to :collection
end

class UserThingState
  belongs_to :user
  belongs_to :thing
end

Существует много коллекций, в которых есть много вещей. Пользователи подписываются на множество коллекций и тем самым подписываются на многие вещи. Пользователи имеют состояние по отношению к вещам, но не обязательно, и все еще подписаны на вещи, даже если у них нет состояния для них. Когда пользователь подписывается на коллекцию и связанные с ней вещи, состояние не генерируется для каждой отдельной вещи (которая может быть сотнями). Вместо этого состояния генерируются, когда пользователь впервые взаимодействует с данной вещью. Теперь проблема: я хочу выбрать все подписанные пользователем вещи при загрузке состояния пользователя для каждой вещи, где оно существует.

Концептуально это не так сложно. Для справки, SQL, который должен получить мне данные, необходимые для этого:

SELECT things.*, user_thing_states.* FROM things
# Next line gets me all things subscribed to
INNER JOIN subscriptions as subs ON things.collection_id = subs.collection_id AND subs.user_id = :user_id
# Next line pulls in the state data for the user
LEFT JOIN user_thing_states as uts ON things.id = uts.thing_id AND uqs.user_id = :user_id

Я просто не знаю, как собрать это вместе в рельсы. Что происходит в классе Thing? Thing.include (: user_thing_states) будет загружать все состояния для всех пользователей, и это выглядит как единственный инструмент. Мне нужно что-то вроде этого, но я не уверен, как (или если это возможно):

  class Thing
    has_many :user_thing_states
    delegates :some_state_property, :to => :state, :allow_nil => true

    def state
      # There should be only one user_thing_state if the include is correct, state method to access it.
      self.user_thing_states.first
    end
  end

Мне нужно что-то вроде:

Thing.includes(:user_question_states, **where 'user_question_state.user_id => :user_id**).by_collections(user.collections)

Тогда я могу сделать

things = User.things_subscribed_to 
things.first.some_state_property # the property of the state loaded for the current user.

Ответы [ 3 ]

2 голосов
/ 02 сентября 2011

Вам не нужно ничего делать.

class User
  has_many :user_thing_states
  has_many :things, :through => :user_thing_states
end

# All Users w/ Things eager loaded through States association
User.all.includes(:things)

# Lookup specific user, Load all States w/ Things (if they exist for that user)
user = User.find_by_login 'bob'
user.user_thing_states.all(:include => :things)

Использование include () для этого уже загружает связанный объект, если он существует.

Нет необходимости выполнять какую-либо фильтрацию или добавлять дополнительное поведение для пользователей, у которых нет связанного объекта.

0 голосов
/ 05 октября 2011

Отвечая на мой собственный вопрос дважды ... немного неловко, но в любом случае.

Кажется, что Rails не позволяет вам задавать дополнительные условия для оператора include ().Если бы это было так, мой предыдущий ответ сработал бы - вы могли бы добавить дополнительное условие к оператору include (), которое позволило бы условиям «где» работать правильно.Чтобы решить эту проблему, нам нужно получить include (), чтобы использовать что-то вроде следующего SQL (проблема заключается в получении условия 'И'):

LEFT JOIN user_thing_states as uts ON things.id = uts.thing_id AND uqs.user_id = :user_id

Сейчас я прибегаю к этому, чтонемного ужасно.

class User
  ...

  def subscribed_things
    self.subscribed_things_with_state + self.subscribed_things_with_no_state
  end

  def subscribed_things_with_state
    self.things.includes(:user_thing_states).by_subscribed_collections(self).all
  end

  def subscribed_things_with_no_state
    Thing.with_no_state().by_subscribed_collections(self).all
  end

end
0 голосов
/ 05 октября 2011

* Нет, не решил :( Вот обработка конкретной проблемы, с которой я столкнулся.

Думаю, я понял, проще, чем ожидалось:

class Thing
  has_many :user_thing_states
  delegates :some_state_property, :to => :state, :allow_nil => true

  scope :with_user_state, lambda { |user| 
     includes(:user_thing_states).where('user_thing_states.user_id = :user_id 
                                         OR user_thing_states.user_id IS NULL', 
                                        {:user_id => user.id}) }

  def state
    self.user_thing_states.first
  end
end

Итак:

Thing.with_user_state(current_user).all

Загрузит все Вещи, и у каждого будет только один user_question_state, доступный через состояние, и не исключит Вещи без состояния.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...