Как вы оцениваете Ассоциации ActiveRecord в Rails 3?


у меня есть проект 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 58

6 ответов:

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

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

Я надеюсь, что это поможет.

редактировать #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)

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

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

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

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

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 

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

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

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

@my_orders = current_user.orders

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

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