Rails: форма, устанавливающая значения по умолчанию для объекта?

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

  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

Я подумал о том, чтобы подойти к этому, создав объект «по умолчанию» в коде, который «редактирует» пользователь. Затем в методе модели значения по умолчанию будут установлены на основе значений в этом объекте. Но есть ли лучший способ сделать это?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В общем, я не фанат обратных вызовов для установки значений атрибутов по умолчанию. Это очень устаревший подход, который может иметь странные и неожиданные побочные эффекты из-за проблем со сроками.

Если значения по умолчанию не являются частью схемы базы данных (например, логические значения, которые по умолчанию имеют значение 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

Здесь я предполагаю, что существует система аутентификации и какой-то метод пользователя, который возвращает предпочтительный набор значений по умолчанию.

сначала необходимо включить ActiveModel::Model, иначе PlayerForm.new(level: 1) выдаст ошибку. также initialize.

Alex 16.07.2024 22:25

Потрясающий ответ - спасибо Макс! И спасибо, Алекс, за наводку :)

cheetoriko 17.07.2024 12:30

Спасибо @Alex за указание на это.

max 19.07.2024 13:50

Другие вопросы по теме