Rails, предотвращающие дублирование в полиморфном has_many: через ассоциации - PullRequest
2 голосов
/ 22 мая 2010

Существует ли простой или хотя бы элегантный способ предотвратить повторяющиеся записи в полиморфном has_many через ассоциации?

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

Чтобы узнать, к чему идет мой вопрос, если я запущу следующее в консоли (при условии, что объекты базы данных и тегов уже существуют в базе данных)

s = Story.find_by_id(1)

t = Tag.find_by_id(1)

s.tags << t

s.tags << t

К моей таблице присоединения тегов будут добавлены две записи, каждая с одинаковыми точными данными (tag_id = 1, taggable_id = 1, taggable_type = "Story"). Это просто не кажется мне подходящим. Поэтому, пытаясь предотвратить это, я добавил в свою модель тегов следующее:

before_validation :validate_uniqueness

def validate_uniqueness
    taggings = Tagging.find(:all, :conditions => { :tag_id => self.tag_id, :taggable_id => self.taggable_id, :taggable_type => self.taggable_type })

    if !taggings.empty?
        return false
    end

    return true
end

И это работает почти так, как задумано, но если я пытаюсь добавить дубликат тега к истории или ссылке, я получаю ActiveRecord :: RecordInvalid: Validation failed исключение. Кажется, что когда вы добавляете ассоциацию в список, это вызывает сохранение! (а не save sans!) метод, который вызывает исключения, если что-то идет не так, а не просто возвращает false. Это не совсем то, чего я хочу. Я полагаю, что я могу окружить любые попытки добавить новые теги попыткой / уловкой, но это противоречит идее, что вы не должны ожидать своих исключений, и я полностью ожидаю, что это произойдет.

Есть ли лучший способ сделать это, чтобы не вызывать исключения, когда все, что я хочу сделать, это просто молча не сохранять объект в базе данных, поскольку существует дубликат?

Ответы [ 3 ]

1 голос
/ 22 мая 2010

Вы можете сделать это несколькими способами.

Определить пользовательский метод add_tags , который загружает все существующие теги, затем проверяет и добавляет только новые.

Пример:

def add_tags *new_tags
  new_tags = new_tags.first if tags[0].kind_of? Enumerable #deal with Array as first argument
  new_tags.delete_if do |new_tag|
    self.tags.any? {|tag| tag.name == new_tag.name}
  end
  self.tags += new_tags
end

Вы также можете использовать фильтр before_save, чтобы убедиться, что в списке тегов нет дубликатов.Это повлечет за собой немного больше накладных расходов, потому что это произойдет при КАЖДОМ сохранении.

1 голос
/ 22 мая 2010

Вы можете установить опцию uniq при определении отношения has_many. Документация по Rails API гласит:

: Uniq

Если true, дубликаты будут исключены из коллекции. Полезно в сочетании с: through.

(взято из: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#M001833 в подзаголовке "Поддерживаемые параметры")

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

Я считаю, что это работает ...

class Tagging < ActiveRecord::Base
   validate :validate_uniqueness 

   def validate_uniqueness
      taggings = Tagging.find(:all, :conditions => {
         :tag_id => self.tag_id,
         :taggable_id => self.taggable_id,
         :taggable_type => self.taggable_type }) 

      errors.add_to_base("Your error message") unless taggings.empty? 
   end 
end

Дайте мне знать, если у вас появятся какие-либо ошибки или что-то с этим:]

...