validates_presence_of состоит_в ассоциации, правильный путь - PullRequest
6 голосов
/ 27 мая 2011

Я изучаю, как на самом деле работает validates_presence_of. Предположим, у меня есть две модели

class Project < ActiveRecord::Base
  [...]
  has_many :roles
end

и

class Role < ActiveRecord::Base
  validates_presence_of :name, :project

  belongs_to :project
end

Я хочу, чтобы роль всегда принадлежала существующему проекту, но я только что из этого примера обнаружил, что это может привести к тому, что недопустимые (потерянные) роли будут сохранены в БД. Поэтому правильный способ сделать это - вставить validates_presence_of :project_id в мою ролевую модель, и, похоже, это сработает, даже если я думаю, что семантически имеет больше смысла проверять наличие проекта вместо идентификатора проекта.

Кроме того, я думал, что мог бы поставить недопустимый идентификатор (для несуществующего проекта), если бы я только проверял наличие project_id, так как по умолчанию AR не добавляет проверки целостности в миграции, и даже если я добавляю их вручную некоторые БД их не поддерживают (т.е. MySQL с MyISAM или sqlite). Этот пример доказывает, что

# with validates_presence_of :name, :project, :project_id in the role class
Role.create!(:name => 'foo', :project_id => 1334, :project => Project.new)
  AREL (0.4ms)  INSERT INTO "roles" ("name", "project_id") VALUES ('foo', NULL)
+----+------+------------+
| id | name | project_id |
+----+------+------------+
| 7  | foo  |            |
+----+------+------------+

Конечно, я не буду писать такой код, но я хочу предотвратить неправильные данные в БД.

Мне интересно, как убедиться, что с ролью ВСЕГДА связан (реальный и сохраненный) проект.

Я нашел камень validates_existence , но я предпочитаю не добавлять камень в мой проект, если это не является строго необходимым.

Есть мысли по этому поводу?

Обновление

validates_presence_of :project и добавление :null => false для столбца project_id в миграции кажется более чистым решением.

Ответы [ 2 ]

6 голосов
/ 31 мая 2011

Rails попытается найти идентификатор и добавить ошибку проверки, если объект с идентификатором не найден.

class Role < AR::Base
  belongs_to :project
  validates_presence_of :project, :name
end


Role.create!(:name => "admin", :project_id => 1334)# Project 1334 does not exist
# => validation error raised

Я вижу, что ваша проблема также хочет иметь дело с ситуацией, когда объект автора предоставляется, но является новым, а не в БД. В случае проверки наличия не работает. Буду решать.

Role.create!(:name => "admin", :project => Project.new) # Validation passes when it shouldn't.

Обновление: В некоторой степени вы можете смягчить эффект передачи фиктивного нового объекта, выполнив проверку связанного проекта:.

class Role < ActiveRecord::Base
  belongs_to :project
  validates_presence_of :project
  validates_associated :project
end

Если Project.new.valid? равно false, Role.create!(:name => "admin", :project => Project.new) также вызовет ошибку. Если, однако, Project.new.valid? имеет значение true, то вышеописанное создаст объект проекта при сохранении.

Вам помогает validates_associated :project? 1016 *

2 голосов
/ 06 июня 2011

Я перепробовал множество комбинаций валидаторов, но самое чистое решение - использовать гем validates_existence . С этим я могу написать такой код

r = Role.new(:name => 'foo', :project => Project.new) # => #<Role id: nil, name: "foo", project_id: nil, created_at: nil, updated_at: nil> 
r.valid? # => false 
r.errors # => {:project=>["does not exist"], :project_id=>["does not exist"]} 

Так что моя окончательная модель так же проста, как

class Role < ActiveRecord::Base
  belongs_to :project
  validates_existence_of :project
  # or with alternate syntax
  validates :project, :existence => true
  [...]
end

С проверкой БД и решением Aditya (то есть: null => false в миграции и validates_presence_of: project в модели) Role#valid? вернет true и Role#save вызовет исключение на уровне базы данных, когда project_id равно нулю.

...