Вопрос

У меня есть проект Rails 3.С Rails 3 появился Arel и возможность повторно использовать одну область видимости для создания другой.Мне интересно, есть ли способ использовать области при определении отношений (например,«has_many»).

У меня есть записи со столбцами разрешений.Я хотел бы создать default_scope, который учитывает мои столбцы разрешений, чтобы фильтровать записи (даже те, к которым доступ осуществляется через отношения).

В настоящее время в Rails 3 default_scope (включая найденные мной патчи) не предоставляет работоспособных средств передачи процедуры (которая мне нужна для позднего связывания переменных).Можно ли определить has_many, в который можно передать именованную область?

Идея повторного использования именованной области будет выглядеть так:

Orders.scope :my_orders, lambda{where(:user_id => User.current_user.id)}
has_many :orders, :scope => Orders.my_orders

Или неявное кодирование этой именованной области в отношениях будет выглядеть так:

has_many :orders, :scope => lambda{where(:user_id => User.current_user.id)}

Я просто пытаюсь применить default_scope с поздним связыванием.Я бы предпочел использовать подход Arel (если он есть), но использовал бы любой работоспособный вариант.

Поскольку я имею в виду текущего пользователя, я не могу полагаться на условия, которые не оцениваются в самый последний момент, например:

has_many :orders, :conditions => ["user_id = ?", User.current_user.id]
Это было полезно?

Решение

Я предлагаю вам взглянуть на «Именованные области мертвы»

Там автор объясняет, насколько мощный Арел :)

Надеюсь, это поможет.

РЕДАКТИРОВАНИЕ № 1, март 2014 г.

Как утверждают некоторые комментарии, разница теперь является вопросом личного вкуса.

Тем не менее, я по-прежнему лично рекомендую избегать раскрытия области действия Arel верхнему уровню (будучи контроллером или чем-то еще, имеющим прямой доступ к моделям), и для этого потребуется:

  1. Создайте область действия и откройте ее с помощью метода в вашей модели.Этот метод будет тем, который вы предоставляете контроллеру;
  2. Если вы никогда не предоставляете свои модели контроллерам (поэтому у вас есть какой-то сервисный уровень поверх них), тогда все в порядке.Антикоррупционный слой является ваш сервис, и он сможет получить доступ к области вашей модели, не слишком беспокоясь о том, как реализованы области.

Другие советы

Как насчет расширений ассоциации?

class Item < ActiveRecord::Base
  has_many :orders do
    def for_user(user_id)
      where(user_id: user_id)
    end
  end
end

Item.first.orders.for_user(current_user)

ОБНОВЛЯТЬ:Я хотел бы отметить, что преимущество расширений ассоциации по сравнению с методами или областями классов заключается в том, что у вас есть доступ к внутренним компонентам прокси-ассоциации:

proxy_association.owner возвращает объект, частью которого является ассоциация.proxy_association.reflection возвращает объект отражения, описывающий ассоциацию.proxy_association.target возвращает связанный объект для own_to или has_one или коллекцию связанных объектов для has_many или has_and_belongs_to_many.

Более подробная информация здесь: http://guides.rubyonrails.org/association_basics.html#association-extensions

Вместо областей видимости я просто определял методы класса, и это отлично работало.

def self.age0 do
  where("blah")
end

Я использую что-то вроде:

class Invoice < ActiveRecord::Base
  scope :aged_0,  lambda{ where("created_at IS NULL OR created_at < ?", Date.today + 30.days).joins(:owner) }
end 

Вы можете использовать слить метод для объединения областей действия из разных моделей.Для получения более подробной информации найдите слияние в этом рельскаст

Если вы просто пытаетесь получить заказы пользователя, почему бы вам просто не использовать эту связь?

Предполагая, что текущий пользователь доступен из метода current_user в вашем контроллере:

@my_orders = current_user.orders

Это гарантирует, что будут показаны только конкретные заказы пользователя.Вы также можете выполнять произвольные вложенные соединения, чтобы получить более глубокие ресурсы, используя joins

current_user.orders.joins(:level1 => { :level2 => :level3 }).where('level3s.id' => X)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top