Скажем, у меня есть объект, у которого есть некоторые атрибуты со значениями по умолчанию. Я установил значения по умолчанию для этих атрибутов на уровне модели следующим образом...
after_initialize :set_defaults
def set_defaults
self.name ||= "Player"
self.level ||= 5
end
Однако я хочу, чтобы у пользователя была возможность установить значения по умолчанию. Рабочий процесс будет заключаться в том, что у пользователя будет действие для установки собственных значений по умолчанию (в представлении это будет сделано через форму), а затем эти значения по умолчанию будут использоваться в методе модели set_defaults
, вот так...
def set_defaults
self.name ||= user_default_for_name
self.level ||= user_default_for_level
end
Я подумал о том, чтобы подойти к этому, создав объект «по умолчанию» в коде, который «редактирует» пользователь. Затем в методе модели значения по умолчанию будут установлены на основе значений в этом объекте. Но есть ли лучший способ сделать это?
В общем, я не фанат обратных вызовов для установки значений атрибутов по умолчанию. Это очень устаревший подход, который может иметь странные и неожиданные побочные эффекты из-за проблем со сроками.
Если значения по умолчанию не являются частью схемы базы данных (например, логические значения, которые по умолчанию имеют значение false), но могут быть определены на уровне класса, вы можете использовать API атрибутов ActiveModel или ActiveRecord для установки значений по умолчанию.
attribute :level, :integer, default: 5
Это также область, где вы могли бы рассмотреть возможность использования шаблона объекта формы, чтобы не превращать вашу модель в еще более божественный класс.
Объекты формы — это классы, которые обертывают модель для обработки пользовательского ввода.
class PlayerForm
include ActiveModel::Model
include ActiveModel::Attributes
attr_accessor :player
attribute :level, :integer, default: 5
attribute :name, :string, default: "Player"
# this lets the form object interact with Rails API's
# for routing
delegate :to_model, :model_name, to: :player
def initialize(**attributes)
super
@player = Player.new
end
def save
return false unless self.valid?
@player.assign_attributes(level: level, name: name)
@player.save
end
end
На уровне контроллера мы просто заменяем класс Player на класс PlayerForm:
class PlayerController < ApplicationController
def new
@player = PlayerForm.new
end
def create
@player = PlayerForm.new(player_attributes)
if @player.save
redirect_to @player
else
render :new
end
end
end
Хотя непосредственный выигрыш здесь может показаться небольшим, теперь установка значений по умолчанию происходит только тогда, когда вы действительно этого хотите — при заполнении формы, а не при каждом создании экземпляра модели.
Я подумал о том, чтобы подойти к этому, создав объект «по умолчанию» в коде, который «редактирует» пользователь. Затем в методе модели значения по умолчанию будут установлены на основе значений в этом объекте. Но есть ли лучший способ сделать это?
Если вы хотите, чтобы значения по умолчанию могли персонализироваться пользователями через графический интерфейс, вам следует создать для этого модель (и соответствующие маршруты и контроллер):
# rails g model UserDefault name:string level:integer
class UserDefault < ApplicationRecord
belongs_to :user
end
Затем вы можете подключить модель пользовательских настроек по умолчанию к объекту формы, который мы создали ранее:
class PlayerForm
include ActiveModel::Attributes
include ActiveModel::Model
attr_accessor :player
attribute :level, :integer
attribute :name, :string
# this lets the form object interact with Rails API's
# for routing
delegate :to_model, :model_name, to: :player
def initalize(**attributes)
super
@player = Player.new
end
def save
return false unless self.valid?
@player.assign_attributes(level: level, name: name)
@player.save
end
def assign_user_defaults(defaults)
assign_attributes(defaults.attributes.slice(:name, :level))
end
end
class PlayerController < ApplicationController
before_action :authenticate_user
def new
@player = PlayerForm.new
@player.assign_user_defaults(current_user.user_default) if current_user.user_default
end
def create
@player = PlayerForm.new(player_attributes)
if @player.save
redirect_to @player
else
render :new
end
end
end
Здесь я предполагаю, что существует система аутентификации и какой-то метод пользователя, который возвращает предпочтительный набор значений по умолчанию.
Потрясающий ответ - спасибо Макс! И спасибо, Алекс, за наводку :)
Спасибо @Alex за указание на это.
сначала необходимо включить
ActiveModel::Model
, иначеPlayerForm.new(level: 1)
выдаст ошибку. такжеinitialize
.