Я хочу присвоить значения моей модели Item
прямо перед ее сохранением в методе create
моей модели ItemsController
, который обрабатывает запросы POST. Но кажется, что ничего не происходит.
# app/models/item.rb
class Item < ApplicationRecord
belongs_to :user
belongs_to :list
...
attr_accessor :list_id, :user_id # this should allow me to assign to fields directly
end
# app/controllers/items_controller.rb
# POST /items or /items.json
def create
@item = Item.new(item_params) # Original generated code
@item.list_id = 1 # I am assigning a value here
@item.user_id = session[:user_id] # and here
logger.debug session[:user_id] # => 1
logger.debug @item # => #<Item id: nil, name: "sad", quantity: 1, user_id: nil, list_id: nil, created_at: nil, updated_at: nil>
...
end
Почему @item.user_id
и @item.list_id
по-прежнему отображаются как nil
сразу после задания?
Удали это:
attr_accessor :list_id, :user_id # this should allow me to assign to fields directly
Вы уже можете назначить list_id
и user_id
по умолчанию. Rails создает средства чтения и записи для каждого столбца в ваших таблицах, которые вы перезаписываете своими собственными методами доступа к атрибутам. За атрибутами стоит гораздо больше логики, чем простое присвоение переменной экземпляра.
Вы совершаете классическую ошибку новичка, объединяя переменные экземпляра и атрибуты ActiveRecord/ActiveModel.
Что делает attr_accessor :list_id, :user_id
, так это определяет простые методы установки и получения для перечисленных переменных экземпляра. Это отлично подходит для простых старых классов Ruby, но ActiveRecord работает иначе.
Вместо этого ActiveRecord запрашивает у базы данных метаданные таблицы, описывающие схему, и использует их для создания методов установки и получения с помощью метапрограммирования. Эти установщики не хранят каждый атрибут в одной переменной экземпляра — скорее, каждый атрибут сохраняется в хеше, содержащем все атрибуты, а также атрибут до приведения типов и многое другое.
Когда вы используете attr_accessor :list_id, :user_id
, вы затираете эти сеттеры, поэтому фактический атрибут, хранящийся в хеше, который ActiveRecord хранит в базе данных, никогда не записывается и не помечается как грязный, что заставляет ActiveRecord сохранять его. Вот почему вы получаете ноль, когда модель считывается из базы данных.
В моделях Rails практически никогда не следует использовать attr_accessor
(или другие макросы доступа Ruby). Для столбцов базы данных YAGNI, и если вам нужен атрибут, который не поддерживается столбцом базы данных, используйте вместо этого ActiveModel::Attributes API.
Возможно, стоит отметить, что в отличие от простого класса Ruby, ActiveRecord не хранит атрибуты модели в отдельных переменных экземпляра, т. е. здесь нет
@list_id
или@user_id
.