2009-04-08 10 views
2

Je dois créer une migration AR pour une table de fichiers image. Les images sont en cours de vérification dans l'arborescence source et devraient agir comme des fichiers attach_fu. Cela étant le cas, je crée une hiérarchie pour eux sous/public/système. En raison de la manière dont attachment_fu génère des liens, j'ai besoin d'utiliser la convention de dénomination de répertoire pour insérer des valeurs de clé primaire. Comment remplacer l'auto-incrément de MySQL ainsi que toute la magie Rails pour que je puisse faire quelque chose comme ceci:Comment utiliser une migration Rails ActiveRecord pour insérer une clé primaire dans une base de données MySQL?

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

Répondre

2

Aïe, pas un problème agréable à avoir. Le moyen le moins kludgy que je peux penser pour le faire est d'avoir du code dans votre migration qui "télécharge" tous les fichiers via attachment-fu, et laisse donc le plugin créer les IDs et placer les fichiers.

Quelque chose comme ceci:

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 

Un regard sur l'attachement-fu tests de pourrait être utile.

+0

Je ne l'ai pas fait, mais je pense que c'est la bonne façon de le faire ... laisser le code le faire et ne pas essayer de le subvertir. –

1

Contrairement, dit Sybase, MySQL si vous spécifiez la colonne id dans la colonne de l'instruction d'insertion liste, vous pouvez insérer toute valeur valide et non dupliquée dans l'ID. Pas besoin de faire quelque chose de spécial.

Je soupçonne que la magie des rails est simplement de ne pas laisser les rails savent que l'identifiant est auto-incrémenté. Si c'est la seule façon que vous allez insérer dans cette table, alors ne faites pas faites l'id auto_increment. Faites simplement une clé primaire int non nulle.

Bien que franchement, cela utilise une clé comme donnée, et donc cela me rend mal à l'aise. Si attachment_fu recherche simplement une colonne appelée nommée "id", faites une colonne nommée id qui est vraiment donnée, et faites une colonne nommée "actual_id" la clé réelle, synthétique, auto_incremented.

+0

Malheureusement, je n'ai pas le temps de remplacer attachment_fu. Je suis d'accord que l'utilisation du champ clé primaire pour construire un chemin de fichier est un peu incertain. –

+0

Je pense que je cours contre la magie Rails, pas la magie MySQL. –

0

Voici mon Kluge:

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 
+0

Cela semble faire exactement ce que fait la clé primaire par défaut ... quelle est la différence de fonctionnalité? –

+0

La méthode create() n'utilise pas la valeur de ": id => ##" si elle est donnée. Alors que j'ai utilisé le kluge ci-dessus, je pense que la bonne réponse est de laisser la classe AR faire le travail, comme le suggère @Sarah Mei. –

0

Je ne suis pas entièrement sûr de comprendre pourquoi vous devez faire cela, mais si vous n'avez besoin de le faire qu'une seule fois, pour une migration, utilisez simplement execute dans la migration pour définir l'ID (en supposant que ce n'est pas déjà fait pris, que je ne peux pas imaginer que ce serait):

execute "INSERT INTO images (id, nom de fichier) VALUES (42, 'foo.jpg')"

0

Je suis d'accord avec AdminMyServer bien que je crois que vous pouvez toujours effectuer cette tâche directement sur l'objet:

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

Vous devrez également vous assurer que votre auto-incrément d'identifiant est mis à jour à la fin du processus pour éviter les conflits dans le futur.

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

Hope this helps.

1
image = Image.create(:filename => "foo.jpg") { |r| r.id = 42 } 
Questions connexes