validates_uniqueness_of в разрушенных вложенных рельсах модели - PullRequest
18 голосов
/ 05 мая 2010

У меня есть модель проекта, которая принимает вложенные атрибуты для задачи.

class Project < ActiveRecord::Base  
  has_many :tasks

  accepts_nested_attributes_for :tasks, :allow_destroy => :true

end

class Task < ActiveRecord::Base  
validates_uniqueness_of :name end

Проверка уникальности в модели задач создает проблему при обновлении проекта.

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

хэш параметров выглядит как

task_attributes => { {"id" =>
"1","name" => "T1", "_destroy" =>
"1"},{"name" => "T1"}}

Проверка задачи выполнена перед уничтожением старой задачи. Следовательно, проверка не пройдена. Есть идеи, как проверить, чтобы задача не считалась уничтоженной?

Ответы [ 5 ]

16 голосов
/ 21 мая 2010

Эндрю Франс создал патч в этой ветке , где проверка выполняется в памяти.

class Author
  has_many :books

  # Could easily be made a validation-style class method of course
  validate :validate_unique_books

  def validate_unique_books
    validate_uniqueness_of_in_memory(
      books, [:title, :isbn], 'Duplicate book.')
  end
end

module ActiveRecord
  class Base
    # Validate that the the objects in +collection+ are unique
    # when compared against all their non-blank +attrs+. If not
    # add +message+ to the base errors.
    def validate_uniqueness_of_in_memory(collection, attrs, message)
      hashes = collection.inject({}) do |hash, record|
        key = attrs.map {|a| record.send(a).to_s }.join
        if key.blank? || record.marked_for_destruction?
          key = record.object_id
        end
        hash[key] = record unless hash[key]
        hash
      end
      if collection.length > hashes.length
        self.errors.add_to_base(message)
      end
    end
  end
end
3 голосов
/ 05 августа 2013

Насколько я понимаю, подход Райнера к проверке в памяти не будет практичным в моем случае, так как у меня много «книг», 500К и продолжает расти. Это было бы большим успехом, если вы хотите перенести все в память.

Решение, которое я придумал, заключается в следующем:

Поместите условие уникальности в базу данных (что я всегда нашел хорошей идеей, так как по моему опыту Rails здесь не всегда хорошо справляется) добавив следующее в файл миграции в db / migrate /:

  add_index :tasks [ :project_id, :name ], :unique => true

В контроллере поместите save или update_attributes внутри транзакции и спасите исключение базы данных. Например.,

 def update
   @project = Project.find(params[:id])
   begin
     transaction do       
       if @project.update_attributes(params[:project])
          redirect_to(project_path(@project))
       else
         render(:action => :edit)
       end
     end
   rescue
     ... we have an exception; make sure is a DB uniqueness violation
     ... go down params[:project] to see which item is the problem
     ... and add error to base
     render( :action => :edit )
   end
 end

конец

1 голос
/ 31 августа 2016

Райнер Блессинг ответит хорошо. Но лучше, когда мы можем отметить, какие задачи дублируются.

class Project < ActiveRecord::Base
  has_many :tasks, inverse_of: :project

  accepts_nested_attributes_for :tasks, :allow_destroy => :true
end

class Task < ActiveRecord::Base
  belongs_to :project

  validates_each :name do |record, attr, value|
    record.errors.add attr, :taken if record.project.tasks.map(&:name).count(value) > 1
  end
end
1 голос
/ 04 декабря 2013

Для Rails 4.0.1 эта проблема помечена как исправленная этим запросом на получение, https://github.com/rails/rails/pull/10417

Если у вас есть таблица с уникальным индексом поля, и вы отмечаете запись для уничтожения, и вы создаете новую запись с тем же значением, что и уникальное поле, затем при вызове сохранения уникальный индекс уровня базы данных ошибка будет выдана.

Лично это все еще не работает для меня, поэтому я не думаю, что это еще полностью исправлено.

0 голосов
/ 05 мая 2010

Ссылка это

Почему бы вам не использовать: scope

class Task < ActiveRecord::Base
  validates_uniqueness_of :name, :scope=>'project_id' 
end

это создаст уникальное задание для каждого проекта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...