29 votes

comment (remplacer|créer) un champ enum sur les migrations rails 2.0 ?

J'aimerais créer un champ de type enum dans le cadre d'une migration que je suis en train d'effectuer. J'ai essayé de faire des recherches dans Google, mais je ne trouve pas le moyen de le faire dans la migration.

la seule chose que j'ai trouvée est

  t.column :status, :enum, :limit => [:accepted, :cancelled, :pending]

mais il semble que le code ci-dessus ne fonctionne que sur rails 1.xxx et comme j'utilise rails 2.0

C'est ce que j'ai essayé mais ça ne marche pas

class CreatePayments < ActiveRecord::Migration
  def self.up
    create_table :payments do |t|
      t.string :concept
      t.integer :user_id
      t.text :notes
      t.enum :status, :limit => [:accepted, :cancelled, :pending]

      t.timestamps
    end
  end

  def self.down
    drop_table :payments
  end
end

Donc, au cas où cela ne serait pas autorisé, quelle serait, selon vous, une bonne solution ? Un simple champ de texte, et une validation à partir du modèle ?

37voto

Adam Lassek Points 18918

Vous pouvez spécifier manuellement le type en utilisant l'option t.column à la place. Rails l'interprétera comme une colonne de chaînes de caractères, et vous pourrez simplement ajouter un validateur au modèle comme l'a suggéré Pavel :

class CreatePayments < ActiveRecord::Migration
  def self.up
    create_table :payments do |t|
      t.string :concept
      t.integer :user_id
      t.text :notes
      t.column :status, "ENUM('accepted', 'cancelled', 'pending')"

      t.timestamps
    end    
  end

  def self.down
    drop_table :payments
  end
end

class Payment < ActiveRecord::Base
  validates_inclusion_of :status, :in => %w(accepted cancelled pending)
end

24voto

Pavel Nikolov Points 3595

Regardez le conseil n°3 sur http://zargony.com/2008/04/28/five-tips-for-developing-rails-applications

C'est exactement ce dont vous avez besoin !

 class User < ActiveRecord::Base
   validates_inclusion_of :status, :in => [:active, :inactive]

   def status
     read_attribute(:status).to_sym
   end

   def status= (value)
     write_attribute(:status, value.to_s)
   end
 end

HTH

9voto

Lailson Bandeira Points 519

Vous pouvez essayer le (très) complet jeff's gemme d'attributs énumérés OU optez pour cette solution de rechange simple :

class Person < ActiveRecord::Base
  SEX = [:male, :female]

  def sex
    SEX[read_attribute(:sex)]
  end

  def sex=(value)
    write_attribute(:sex, SEX.index(value))
  end
end

Et ensuite, déclarer le sex en tant que nombre entier :

t.integer :sex

Cela a très bien fonctionné pour moi ! =D

4voto

Sarah Mei Points 10673

J'ai des dizaines de ces petits enums, avec 3 à 300 entrées dans chacun. Je les implémente comme des tables de consultation. Je n'ai pas de fichier modèle pour chacun d'entre eux ; j'utilise une métaprogrammation pour générer un modèle pour chacun d'entre eux, puisque chaque table a le même ensemble de colonnes (id, nom, description).

Puisque certains des ensembles avaient suffisamment d'éléments pour justifier leur propre table, il était plus cohérent de les déplacer tous vers des tables. C'est juste une autre option si vous avez d'autres de ces enums plus tard.

EDITAR: Voici comment je génère les modèles :

ACTIVE_RECORD_ENUMS = %w{
  AccountState
  ClientType
  Country
  # ...
}

ACTIVE_RECORD_ENUMS.each do |klass|
  eval "class #{klass} < ActiveRecord::Base; end"
  klass.constantize.class_eval do
    class << self

      def id_for(name)
        ids[name.to_s.strip.humanize.downcase]
      end

      def value_for(id)
        values[id.to_i]
      end

      def values
        @values ||= find(:all).inject({}) {|h,m| h[m.send(primary_key)] = m.name; h}
      end

      def ids
        @ids ||= self.values.inject({}) {|h, {k, v}| h[v.downcase] = k; h}
      end

    end
  end
end

Ce fichier se trouve dans le répertoire models, et est inclus dans le fichier application_config.rb . Cela me permet de faire des choses comme ça :

AccountState.ids 
# => {"active" => 1, "deleted" => 2}
AccountState.values 
# => {1 => "Active", 2 => "Deleted"}
AccountState.id_for("Active") 
# => 1
AccountState.value_for(1) 
# => "active"

3voto

user89447 Points 138

Avez-vous regardé le colonne-anum sur RubyForge ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X