Как использовать миграцию Rails ActiveRecord для вставки первичного ключа в базу данных MySQL?

Мне нужно создать AR-миграцию для таблицы файлов изображений. Изображения проверяются в исходном дереве и должны вести себя как файлы attachment_fu. В таком случае я создаю для них иерархию в /public/system.

Из-за того, как attachment_fu генерирует ссылки, мне нужно использовать соглашение об именовании каталогов для вставки значений первичного ключа. Как мне переопределить автоинкремент в MySQL, а также любую магию Rails, чтобы я мог сделать что-то вроде этого:

image = Image.create(:id => 42, :filename => "foo.jpg")
image.id #=> 42

person Terry G Lorber    schedule 08.04.2009    source источник


Ответы (5)


Yikes, не приятная проблема иметь. Наименее глупый способ, который я могу придумать, - это иметь некоторый код в вашей миграции, который фактически «загружает» все файлы через вложение-фу и, следовательно, позволяет плагину создавать идентификаторы и размещать файлы.

Что-то вроде этого:

Dir.glob("/images/to/import/*.{jpg,png,gif}").each do |path|

  # simulate uploading the image
  tempfile = Tempfile.new(path)
  tempfile.set_encoding(Encoding::BINARY) if tempfile.respond_to?(:set_encoding)
  tempfile.binmode
  FileUtils.copy_file(path, tempfile.path)

  # create as you do in the controller - may need other metadata here
  image = Image.create({:uploaded_data => tempfile})
  unless image.save
    logger.info "Failed to save image #{path} in migration: #{image.errors.full_messages}"
  end

  tempfile.close!
end

Может быть полезно взглянуть на тесты приложения-фу.

person Sarah Mei    schedule 08.04.2009
comment
Я этого не делал, но я думаю, что это правильный способ сделать это... пусть код делает свое дело и не пытается подорвать его. - person Terry G Lorber; 10.04.2009

В отличие от, скажем, Sybase, в MySQL, если вы укажете столбец id в списке столбцов оператора вставки, вы можете вставить любое допустимое, не повторяющееся значение в id. Не нужно делать что-то особенное.

Я подозреваю, что магия рельсов заключается в том, чтобы не сообщать рельсам, что идентификатор является автоинкрементным. Если это единственный способ вставки в эту таблицу, то не делайте автоматическое увеличение идентификатора. Просто сделайте первичный ключ int не нулевым.

Хотя, честно говоря, это использование ключа в качестве данных, и поэтому меня это беспокоит. Если attachment_fu просто ищет столбец с именем "id", создайте столбец с именем id, который на самом деле является данными, и сделайте столбец с именем "actual_id" фактическим синтетическим ключом с автоматическим приращением.

person tpdi    schedule 08.04.2009
comment
К сожалению, у меня нет времени, чтобы заменить attachment_fu. Я согласен с тем, что использование поля первичного ключа для построения пути к файлу немного сомнительно. - person Terry G Lorber; 08.04.2009

Вот мой клуге:

class AddImages < ActiveRecord::Migration
  def self.up
    Image.destroy_all

    execute("ALTER TABLE images AUTO_INCREMENT = 1")

    image = Image.create(:filename => "foo.jpg")
    image.id #=> 1
  end

  def self.down
  end
end
person Terry G Lorber    schedule 08.04.2009
comment
Это похоже на то, что делает первичный ключ по умолчанию... в чем разница в функциональности? - person Don Werve; 09.04.2009
comment
Метод create() не использует значение :id =› ##, если оно задано. Хотя я использовал приведенный выше kluge, я думаю, что правильный ответ — позволить классу AR работать, как предлагает @Sarah Mei. - person Terry G Lorber; 10.04.2009

Я не совсем уверен, что понимаю, зачем вам это нужно, но если вам нужно сделать это только один раз, для миграции, просто используйте execute в миграции, чтобы установить идентификатор (при условии, что он еще не занят, что я не могу представить, что это будет):

выполнить "ВСТАВИТЬ В изображения (идентификатор, имя файла) ЗНАЧЕНИЯ (42, 'foo.jpg')"

person AdminMyServer    schedule 08.04.2009

Я согласен с AdminMyServer, хотя я считаю, что вы все равно можете выполнить эту задачу на объекте напрямую:

image = Image.new :filename => "foo.jpg"
image.id = 42
image.save

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

newValue = Images.find(:first, :order => 'id DESC').id + 1
execute("ALTER TABLE images AUTO_INCREMENT = #{newValue}")

Надеюсь это поможет.

person Community    schedule 20.04.2009