Как вы оцениваете ассоциации ActiveRecord в Rails 3? - PullRequest
59 голосов
/ 09 марта 2010

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

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

В настоящее время в Rails 3 default_scope (включая найденные мной исправления) не обеспечивают работоспособного способа передачи proc (который мне нужен для поздней привязки переменных). Можно ли определить 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]

Ответы [ 6 ]

21 голосов
/ 30 марта 2010

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

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

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

РЕДАКТИРОВАТЬ # 1 марта 2014

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

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

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

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

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 возвращает связанный объект для has_many или has_one или коллекцию связанных объектов для has_many или has_and_belongs_to_many.

Подробнее здесь: http://guides.rubyonrails.org/association_basics.html#association-extensions

15 голосов
/ 18 августа 2010

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

def self.age0 do
  where("blah")
end
10 голосов
/ 15 июля 2010

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

class Invoice < ActiveRecord::Base
  scope :aged_0,  lambda{ where("created_at IS NULL OR created_at < ?", Date.today + 30.days).joins(:owner) }
end 
9 голосов
/ 26 июля 2012

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

3 голосов
/ 20 апреля 2011

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

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

@my_orders = current_user.orders

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

current_user.orders.joins(:level1 => { :level2 => :level3 }).where('level3s.id' => X)
...