2011-01-12 19 views

Respuesta

36

Carriles no proporciona ninguna manera de añadir restricciones, pero todavía se puede hacerlo a través de la migración, pero al pasar SQL real para ejecutar()

Crear archivo de migración:

ruby script/generate Migration AddConstraint 

Ahora, en el archivo de migración:

class AddConstraint < ActiveRecord::Migration 
    def self.up 
    execute "ALTER TABLE table_name ADD CONSTRAINT check_constraint_name CHECK (check_column_name IN (1, 2, 3))" 
    end 

    def self.down 
    execute "ALTER TABLE table_name DROP CONSTRAINT check_constraint_name" 
    end 
end 
+1

También tenga en cuenta que si agrega una restricción, tendrá que establecer 'config.active_record.schema_format =: sql' en' config/application.rb' ya que ["' db/schema.rb' no puede expresar la base de datos elementos específicos como desencadenantes, procedimientos almacenados o restricciones de verificación. "] (http://edgeguides.rubyonrails.org/active_record_migrations.html#types-of-schema-dumps). –

+0

Ahora hay una gema que admite el almacenamiento de restricciones en 'db/schema.rb': [active_record-postgres-constraints] (https://github.com/on-site/active_record-postgres-constraints). Ver mi respuesta para más detalles. Por ahora, solo se admite postgres. –

3

Acabo de trabajar para obtener una restricción PostgreSQL CHECK para que funcione.

La solución de Nilesh no es del todo completa; el archivo db/schema.rb no incluirá la restricción, por lo que las pruebas y las implementaciones que usen db: setup no tendrán la restricción. Según http://guides.rubyonrails.org/migrations.html#types-of-schema-dumps

Mientras que en una migración puede ejecutar sentencias SQL personalizadas, el dumper esquema no puede reconstituir las declaraciones de la base de datos. Si está utilizando características como esta, entonces debe establecer el formato de esquema en: sql.

es decir, en config/application.rb conjunto

config.active_record.schema_format = :sql 

Desafortunadamente, si usted está utilizando PostgreSQL se puede obtener un error al cargar el volcado resultante, véase la discusión en ERROR: must be owner of language plpgsql. No quería ir por la ruta de configuración de PostgreSQL en esa discusión; Además, en cualquier caso, me gusta tener un archivo legible db/schema.rb. Eso descartó el SQL personalizado en el archivo de migración para mí.

La joya https://github.com/vprokopchuk256/mv-core sugerida por Valera parece prometedora, pero solo admite un conjunto limitado de restricciones (y recibí un error cuando traté de usarlo, aunque puede deberse a incompatibilidades con otras gemas que incluyo) .

La solución (pirateo) que fui es hacer que el código del modelo inserte la restricción. Puesto que es tipode como una validación, que es donde lo pongo:

class MyModel < ActiveRecord::Base 

    validates :my_constraint 

    def my_constraint 
     unless MyModel.connection.execute("SELECT * FROM information_schema.check_constraints WHERE constraint_name = 'my_constraint'").any? 
      MyModel.connection.execute("ALTER TABLE my_models ADD CONSTRAINT my_constraint CHECK (...the SQL expression goes here ...)") 
     end 
    end 

Por supuesto, esto seleccionar un extra antes de cada validación; si eso es un problema, una solución sería ponerlo en un parche "después de conectar" tal como se explica en How to run specific script after connected to oracle using rails? (No puede simplemente almacenar en caché el resultado de la selección, porque la adición de validación/restricción ocurre dentro de una transacción que podría revertido, por lo que debe verificarlo cada vez.)

+0

Esta es una idea interesante, pero no escalable. Como dices, hace una selección adicional antes de cada validación. Si termina con muchas limitaciones, esto se convertirá en un dolor de cabeza de rendimiento a medida que escala. Creo que la publicación a la que vinculó sobre el lanzamiento de scripts después de la conexión db inicial podría ser una mejor manera de hacerlo. Sin embargo, una vez dicho esto, se podría argumentar que tratar de resolver el problema de restricciones no se debe hacer dentro de Rails, pero fuera de él, Rails está destinado a ser independiente de la base de datos. Ver: http://stackoverflow.com/questions/2589509/does-rails-need-database-level-constraints – rmcsharry

2

Puede hacerlo con Migration Validators gem.Ver detalles aquí: https://github.com/vprokopchuk256/mv-core

Con esa gema podrás definir la validación de la inclusión en el nivel db:

def change 
    change_table :table_name do |t| 
    t.integer :column_name, inclusion: [1, 2, 3] 
    end 
end 

Por otra parte usted es capaz de definir la forma en que la validación se debe definir e incluso mensaje de error que debe se mostrará:

def change 
    change_table :posts do |t| 
    t.integer :priority, 
       inclusion: { in: [1, 2, 3], 
          as: :trigger, 
          message: "can't be anything else than 1, 2, or 3" } 
    end 
end 

incluso se puede subir de nivel que la validación de la migración derecho de su modelo:

class Post < ActiveRecord::Base 
    enforce_migration_validations 
end 

y después de validación define en la migración será también definido como la validación ActiveModel en su modelo:

Post.new(priority: 3).valid? 
=> true 

Post.new(priority: 4).valid? 
=> false 

Post.new(priority: 4).errors.full_messages 
=> ["Priority can't be anything else than 1, 2, or 3"] 
0

Puede utilizar Sequel joya https://github.com/jeremyevans/sequel

Sequel.migration do 
    change do 
    create_table(:artists) do 
     primary_key :id 
     String :name 
     constraint(:name_min_length){char_length(name) > 2} 
    end 
    end 
end 
2

Acabo de publicar una joya para esto: active_record-postgres-constraints. A medida que el README no describe, se puede usar con un archivo db/schema.rb, y añade soporte para los siguientes métodos de migraciones:

create_table TABLE_NAME do |t| 
    # Add columns 
    t.check_constraint conditions 
    # conditions can be a String, Array or Hash 
end 

add_check_constraint TABLE_NAME, conditions 
remove_check_constraint TABLE_NAME, CONSTRAINT_NAME 

Tenga en cuenta que en este momento, sólo se postgres es compatible.

Cuestiones relacionadas