2009-03-29 8 views
28

Je voudrais créer un champ ENUM à la migration sone que je fais, j'ai essayé une recherche dans google mais je ne peux pas trouver le moyen de le faire dans la migrationcomment (remplacer | créer) un champ enum sur les migrations de rails 2.0?

la seule chose que j'ai trouvé

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

mais ressemble le code ci-dessus ne fonctionne que sur des rails 1.xxx et depuis que je suis en cours d'exécution rails 2.0

ce que j'ai essayé, mais il ne

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 n'est pas autorisé, que pensez-vous que pourrait être une bonne solution? juste un champ de texte, et valider à partir du modèle?

+0

Je sais que ça fait longtemps que votre question a été publiée et qu'il était destiné à des rails 2.0. Mais je voulais juste noter que les rails 4.1 ont ActiveRecord acceptant des enums. Documentation: http://api.rubyonrails.org/v4.1.0/classes/ActiveRecord/Enum.html –

+0

merci !!! Je me suis déplacé à Django de toute façon :) –

Répondre

2

Ajoutez ce qui suit:

 
module ActiveRecord 
    module ConnectionAdapters #:nodoc: 
    class TableDefinition 
     def enum(*args) 
     options = args.extract_options! 
     column_names = args 

     column_names.each { |name| column(name, 'enum', options) } 
     end 
    end 
    end 
end 

à lib/ENUM/table_definition.rb et l'inclure dans votre init.rb.

+0

joli morceau de code, je vais essayer de l'implémenter, question, ça a l'air facile un joli, alors pourquoi les rails ne l'incluent pas dans le noyau? –

+0

Probablement parce que toutes les bases de données ne prennent pas en charge les ENUM. – wesgarrison

4

J'ai des douzaines de ces petites énumérations, avec 3-300 entrées chacune. Je les implémente en tant que tables de recherche. Je n'ai pas de fichier modèle pour chacun d'eux; J'utilise de la métaprogrammation pour générer un modèle pour chacun, car chaque table a le même ensemble de colonnes (id, nom, description).

Étant donné que 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. Juste une autre option si vous aurez plus de ces énumérations plus tard.

EDIT: Voilà comment je produis 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 des modèles, et est inclus dans application_config.rb. Cela me permet de faire des trucs comme ça:

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

Je serais très intéressé de voir le code ou d'en savoir plus sur la métaprogrammation que vous faites pour y parvenir si ce n'est pas trop de problèmes. –

+0

Modifié pour ajouter du code. Je ne l'ai pas regardé depuis un moment et j'ai trouvé que je devais regarder (encore une fois) ce que fait l'injection. Peut-être que c'est un argument pour l'utilisation de recueillir plutôt ... –

+0

Très bonne idée. J'ai beaucoup de ces tables de recherche pour lesquelles je ne veux jamais faire de fichiers modèles. – wesgarrison

0

ok, il suffit de lire l'ensemble des rails api et trouvé ce que je neeed et je ne aime pas :( Rails ne supporte pas emum comme type natif sur les migrations, here est le information, je dois rechercher un plugin ou une autre méthode

Je vous tiendrai

+0

IIRC, rails ne prend en charge que les fonctionnalités implémentées par toutes les bases de données prises en charge, il n'y a donc pas de support enum "prêt à l'emploi". – MarkusQ

+0

FYI, ni Hibernate Java prend en charge cela sans extension. – yclian

+0

génial! Maintenant, j'ai juste besoin de savoir comment lancer Hibernate sur ROR :) –

0

une autre option:.. goutte à SQL

def self.up 
    execute "ALTER TABLE `payments` ADD `status` ENUM('accepted', 'cancelled', 'pending')" 
end 
24

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

Ce 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

+0

Ceci, cependant résout le problème, est juste une solution de contournement. Et pas de solution au vrai problème demandé par OP. La réponse d'Adam Lassek est plus pertinente. – Waseem

36

Vous pouvez spécifier manuellement le type en utilisant la méthode t.column à la place.Rails interpréteront cela comme une colonne de chaîne, et vous pouvez simplement ajouter un validateur au modèle comme Pavel suggéré:

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

+1: c'est la solution la plus simple – MickaelFM

+0

Qu'en est-il de l'utilisation de 'add_column'? –

+0

add_column: nom_table,: nom_colonne, "ENUM ('xx')" – agate

3

De même, le petit bijou enumerated_attribute gère énumérations au niveau de l'objet.

enum_attr :status, %w(accepted cancelled ^pending) 

définir une chaîne dans la migration

t.string :status 

fournit également quelques fonctionnalités intéressantes comme des méthodes dynamiques sous-jacentes.

http://github.com/jeffp/enumerated_attribute/tree/master

+0

qu'est-ce que le ^? –

9

Vous pouvez le (très) complète enumerated_attribute gem ou aller avec cette solution simple de jeff:

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 déclarer l'attribut sex comme un entier:

t.integer :sex 

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

+0

pouvez-vous expliquer un peu comment cela fonctionne? est-ce que cela change une constante ou? – Tallboy

+0

Il ne fait que mapper des valeurs entières aux symboles. =) –

+1

Que se passe-t-il si vous ajoutez un nouvel élément au milieu de la liste? SEX = [: male,: alien,: female] changera toutes les femelles existantes en aliens, puisque: alien a maintenant un index de 1. – Jordan

0

Avec simple_form j'utilise ceci:

<%= f.input :gender, :collection => {'Male' => 'male','Female' => 'female'}, :include_blank => false %> 
Questions connexes