У меня есть таблица, в которой есть записи. Я хотел бы получить доступ к этим записям как к переменным как в моих моделях, так и в контроллерах, не запрашивая базу данных каждый раз, чтобы установить эти переменные.
Я могу заставить его работать, создавая дубликаты «проблем» для моих моделей и контроллеров. Я также мог бы установить глобальные переменные в моем ApplicationController. Или я мог бы инициализировать их в каждом месте, где они мне нужны. Каким будет правильный способ рельсов для установки и доступа к глобальным переменным, к которым можно получить доступ как в контроллерах, так и в моделях?
class ItemType
has_many :items
end
class Item
belongs_to :item_type
belongs_to :foo
end
class Foo
has_many :items
def build_item
bar_item_type = ItemType.find_by(:name => "bar")
self.items.build(
:foo_id => self.id,
:item_type_id => bar_item_type.id
)
end
end
class ItemsController
def update
bar_item_type = ItemType.find_by(:name => "bar")
@item.update(:item_type_id => bar_item_type.id)
end
end
В примере вы можете видеть, что я объявляю переменную bar_item_type
как в моей модели Foo, так и в моем ItemsController. Я хотел бы высушить свою кодовую базу, имея возможность создать и получить доступ к этой переменной один раз для моего проекта rails вместо того, чтобы везде делать один и тот же вызов базы данных.
Я думаю, это мой вопрос. Каков обычный/лучший способ сделать это, используя приведенный выше пример? Я хотел бы, чтобы переменная была доступна в обоих этих случаях использования.
Проблема в том, как вы можете быть уверены, что ItemType.find_by(:name => "bar")
вообще что-то вернет? Ваш код делает предположения о базе данных.
База данных заполнена определенными типами item_types. База данных предполагается и предназначена для их наличия.
Я бы выступал против такого жестко запрограммированного кода или кода, зависящего от состояния БД. Если вы должны это сделать, вот как это можно сделать одним из известных мне способов:
# models
class ItemType < ActiveRecord::Base
has_many :items
# caches the value after first call
def self.with_bar
@@with_bar ||= transaction { find_or_create_by(name: "bar") }
end
def self.with_bar_id
with_bar.id
end
end
class Item < ActiveRecord::Base
belongs_to :item_type
belongs_to :foo
scope :with_bar_types, -> { where(item_type_id: ItemType.with_bar_id) }
end
class Foo < ActiveRecord::Base
has_many :items
# automatically sets the foo_id, no need to mention explicitly
# the chained with_bar_types automatically sets the item_type_id to ItemType.with_bar_id
def build_item
self.items.with_bar_types.new
end
end
# Controller
class ItemsController
def update
@item.update(item_type_id: ItemType.with_bar_id)
end
end
OP может также захотеть проверить уникальность имени для типа элемента.
@lacostenycoder: Тогда ОП должен упомянуть об этом в примере кода в сообщении. В любом случае, это довольно легко обойти.
Я согласен, а также думаю, что вы дали прекрасный ответ.
Если вы ДОЛЖНЫ использовать константу, есть несколько способов сделать это. Но вы должны учитывать, что вы создаете экземпляр объекта модели ActiveRecord, который зависит от данных, присутствующих в базе данных. Это не рекомендуется, потому что теперь у вас есть логика модели и контроллера, основанная на данных, присутствующих в базе данных. Это может быть нормально, если вы заполнили свою базу данных и она не изменится.
class ItemType
BAR_TYPE ||= where(:name => "bar").limit(1).first
has_many :items
end
Теперь, когда вам понадобится этот объект, вы можете вызвать его так:
bar_item_type = ItemType::BAR_TYPE
Спасибо. Я считаю, что это то, что я ищу. Я не знал, что вы можете объявить BAR_TYPE
в модели ItemType вот так.
Это допустимый синтаксис ruby, но не совсем обычный стиль Rails. Ответ Сурьи ниже гораздо более традиционен для Rails и имеет некоторые дополнительные преимущества, тогда как мой пример больше похож на взлом.
Я не уверен, что ваш пример ясно показывает, ПОЧЕМУ вы хотите это сделать. Вы действительно будете использовать
ItemType.find_by(:name => "bar")
в своем приложении? Использование глобальных переменных для вашего варианта использования нарушает MVC и основные соглашения Rails. Сначала вам нужно спросить себя, ПОЧЕМУ вы думаете, что вам нужно это сделать. Есть, вероятно, лучшие более традиционные способы сделать это.