ActiveRecordでMySQLのviewに対してinsertする


はじめに

ActiveRecordでMySQLのviewにinsertをかけようとした時に困ったのでメモ

viewを使うように設定する

users テーブルから view_users というviewを作り、ActiveRecordから参照するように設定する

CREATE VIEW view_users AS SELECT * from users;
class User < ActiveRecord::Base
  self.primary_key = :id
  self.table_name = "view_users"
end

createしたinstanceのidが0になる

pry(main) > User.create(name: 'hoge')
=> #<User: 0x007fc91201c150
 id: 0,
 name: "hoge",
 created_at: Sun, 10 Jan 2016 13:23:03 UTC +00:00,
 updated_at: Sun, 10 Jan 2016 13:23:03 UTC +00:00>

どこでidを設定しているか

def _create_record(attribute_names = self.attribute_names)
  attributes_values = arel_attributes_with_values_for_create(attribute_names)

  new_id = self.class.unscoped.insert attributes_values
  self.id ||= new_id if self.class.primary_key

  @new_record = false
  id
end

primary_key が存在する、かつ self.id がセットされていない場合に id が設定される
この id は、 LAST_INSERT_ID() が設定される

そもそもインスタンス初期化時に id = 0 となっている

pry(main) > User.new(name: 'hoge')
=> #<User: 0x007fc90f201db0
 id: 0,
 name: "hoge",
 created_at: Sun, 10 Jan 2016 13:23:03 UTC +00:00,
 updated_at: Sun, 10 Jan 2016 13:23:03 UTC +00:00>

MySQLのデフォルト値が0になっている

オリジナルのテーブルの id の定義は default null となっているが、viewでは default 0 となっている

root@localhost [paynote_development] > desc users;
+--------------------+--------------+------+-----+---------+----------------+
| Field              | Type         | Null | Key | Default | Extra          |
+--------------------+--------------+------+-----+---------+----------------+
| id                 | int(11)      | NO   | PRI | NULL    | auto_increment |
| name               | varchar(255) | YES  |     | NULL    |                |
| created_at         | datetime     | NO   |     | NULL    |                |
| updated_at         | datetime     | NO   |     | NULL    |                |
+--------------------+--------------+------+-----+---------+----------------+
10 rows in set (0.00 sec)
root@localhost [paynote_development] > desc view_users;
+--------------------+--------------+------+-----+---------+-------+
| Field              | Type         | Null | Key | Default | Extra |
+--------------------+--------------+------+-----+---------+-------+
| id                 | int(11)      | NO   |     | 0       |       |
| name               | varchar(255) | YES  |     | NULL    |       |
| created_at         | datetime     | NO   |     | NULL    |       |
| updated_at         | datetime     | NO   |     | NULL    |       |
+--------------------+--------------+------+-----+---------+-------+
10 rows in set (0.00 sec)

解決法

綺麗な解決法か分からないが、インスタンス生成時に id を修正すれば良い

class User < ActiveRecord::Base
  self.primary_key = :id
  self.table_name = "view_users"
  after_initialize :fix_id

  def fix_id
    self.id = nil if self.id == 0
  end
end