Rails 2.3 динамические условия has_many, основанные на другой ассоциации

У меня есть модель ActiveRecord под названием Books, которая имеет ассоциацию has_one для authors и ассоциацию has_many для publishers. Итак, следующий код хорош

books.publishers

Теперь у меня есть другая модель AR, digital_publishers, которая похожа, но которую я хотел бы прозрачно использовать, если автор книги ответит на digital? - позвольте мне объяснить с помощью некоторого кода.

normal_book = Book.find(1)
normal_book.author.digital? #=> false
normal_book.publishers #=> [Publisher1, Publisher2, ...]

digital_book = Book.find(2)
digital_book.digital? #=> true
digital_book.publishers #=> I want to use the DigitalPublishers class here

Итак, если автор книги является цифровым (автор устанавливается через ассоциацию has_one :author, поэтому это не так просто, как иметь has_many с условием SQL в таблице books), я все еще хочу иметь возможность вызывать .publishers для нее, но иметь это вернуть список DigitalPublishers, поэтому мне нужно какое-то условие для моей ассоциации has_many, которое сначала проверяет, является ли книга цифровой, и если это так, используйте класс DigitalPublishers вместо класса Publishers.

Я попытался использовать обратный вызов after_find, используя следующий код:

after_find :alias_digital_publisher

def alias_digital_publisher
  if self.author.digital?
    def publishers
      return self.digital_publishers
    end
  end
end

Но это, похоже, не помогло. Кроме того, я использую Rails 2.3.


person jackbot    schedule 03.09.2012    source источник
comment
Я решил аналогичную проблему полиморфных отношений (Книга имеет_много издателей [Обычный или цифровой]), но использовал рельсы 3.0.1. Поэтому я не знаю, будет ли решение работать для рельсов 2.3.   -  person prasad.surase    schedule 09.10.2012
comment
Вы можете добавить сюда модели ключей (Книга, Автор, Издатель)? также вы используете прием вложенных атрибутов?   -  person prasad.surase    schedule 09.10.2012


Ответы (3)


Мне нужно больше информации о проекте, чтобы действительно дать рекомендацию, но вот некоторые мысли, которые следует учитывать:

1. Издатели не должны принадлежать книгам

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

#Book.rb
has_many :publishers, :through=>:publications

2. Храните цифровых издателей в таблице издателей.

Либо используйте наследование одной таблицы (STI) для цифровых издателей со столбцом type из DigitalPublisher, либо просто добавьте логическое значение, указывающее, является ли издатель цифровым.

Таким образом, вы можете просто вызвать book.publishers, и вы получите издателей, которые могут быть или не быть цифровыми, в зависимости от того, какие из них были назначены.

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

3. (как вариант) Добавить метод для издателей

def book_publishers
   author.digital? ? digital_publishers : publshers
end

Я не очень сторонник этого варианта, я думаю, вам лучше иметь всех издателей в одной таблице.

person klochner    schedule 10.10.2012

Взгляните на этот раздел из Руководство по Rails v-2.3.11. В частности, обратите внимание на следующее:

Обратные вызовы after_initialize и after_find немного отличаются от других. У них нет аналогов before_*, и единственный способ зарегистрировать их — определить их как обычные методы. Если вы попытаетесь зарегистрировать after_initialize или after_find с помощью методов класса в стиле макросов, они просто будут проигнорированы.

По сути, попробуйте определить свой after_find как

def after_find
  ...
end

Если это не сработает, возможно, поля книги не были инициализированы, поэтому вместо этого попробуйте after_initialize.

person cdesrosiers    schedule 09.10.2012

Мое решение очень простое.

class Book < ActiveRecord::Base

    has_many :normal_publishers, :class_name => 'Publisher'
    has_many :digital_publishers, :class_name => 'DigitalPublisher'

    def publishers
        if self.digital?
            self.digital_publishers
        else
            self.normal_publishers
        end
    end

end

Вы все еще можете связать некоторые методы, такие как book.publishers.count, book.publishers.find(...).

Если вам нужны book.publisher_ids, book.publisher_ids=, book.publishers=, вы можете определить эти методы как book.publishers.

Приведенный выше код работает на Rails 2.3.12.

ОБНОВЛЕНИЕ: Извините, я заметил альтернативное решение Клохнера после того, как опубликовал это.

person Yanhao    schedule 12.10.2012