Rails problème de création de schéma
-
23-08-2019 - |
Question
J'utilise JRuby et Rails 2.2.2. Mon problème est que j'ai une migration qui n'est pas écrit correctement au schéma de base de données.
Voici ma migration:
class CreateNotes < ActiveRecord::Migration
def self.up
create_table(:notes, :options => 'ENGINE=MyISAM') do |t|
t.string :title
t.text :body
t.timestamps
end
execute "alter table notes ADD FULLTEXT(title, body)"
end
Voici ce qu'elle produit dans schema.rb
create_table "notes", :force => true do |t|
t.string "title"
t.text "body"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "notes", ["title", "body"], :name => "title"
J'ai deux questions:
- Comment puis-je obtenir
'ENGINE=MyISAM'
dans le schéma? - Pourquoi mon instruction execute devenir
add_index "notes", ["title", "body"], :name => "title"
? et comment puis-je forcer les migrations de le laisser comme instruction execute?
Merci à Christian Lescuyer pour la réponse. Cependant, quand j'ai essayé ce rien changé. Je décommenté la config.active_record ... mais la ligne, mon schéma n'a pas changé. Je l'ai essayé en JRuby et Ruby 1.8.6 avec rails 2.2.2 et rails de bord et il n'y a pas de changements dans le schéma. Quelqu'un peut-il me dire ce que je fais mal?
La solution
Comme je l'utilise les clés étrangères, j'utilise le format SQL pour les migrations. Dans environment.rb :
# Use SQL instead of Active Record's schema dumper when creating the test database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
config.active_record.schema_format = :sql
Autres conseils
Moi aussi je pensais voir un nouveau fichier sql apparaissent après un « rake db: migrate », une fois que je mets
config.active_record.schema_format = :sql
dans config / environment.rb.
Apparemment, ce n'est pas comment cela fonctionne, cependant. Je dois faire explicitement pour obtenir un db / [développement | Test | production] Fichier _structure.sql:
rake db:structure:dump
Juste une mise à jour pour ceux qui Rails 3 (beta 4, actuellement) - La solution de Christian est encore correcte, seul le bon endroit pour mettre la ligne est config/application.rb
, sous la portée de la classe Application
qui doit être défini dans un module du nom de votre projet Rails.
chrétien est droit.
do
config.active_record.schema_format =: sql
dans environment.rb
mais alors vous devez utiliser un autre format de vidage de schéma et l'emplacement du fichier. essayez de faire votre migration et la recherche de « schema.sql » au lieu de scehema.rb
la raison de tout cela est que le point du fichier système est une base de données non spécifique (fonctionne pour tous les types de bases de données) fichier. donc quand vous utilisez des fonctions qui ne fonctionnent que sur MySQL par une déclaration unsupoorted exécuter, ils ne peuvent pas être shoehorned pour schema.rb
Pour utiliser la variante SQL pour tester (au lieu de schema.rb), vous devrez utiliser
rake db: Test: clone_structure
Notre schéma utilise UUID (gem UUID) et aussi Red Hill on Rails (Rhôr) nice plug-in __gVirt_NP_NN_NNPS<__ FK. Malheureusement, les clefs étrangères exigent que seuls PKs peuvent être ajoutées à l'aide des migrations EXÉCUTE.
Il est bien connu que ces exécute ne permettent pas à la schema.rb; cependant, il est plus difficile de trouver l'alternative rake db: test:. préparer pour les applications qui ne peuvent pas utiliser schema.rb
Le suivant résout à la fois monkey-patch la question de l'index FULLTEXT et option de moteur DB pour votre schéma dumper (Rails 3.2). Vous pouvez le mettre dans config/initializers/
(par exemple schema_dumper_monkeypatch.rb
):
module ActiveRecord
class SchemaDumper
def table(table, stream)
columns = @connection.columns(table)
begin
tbl = StringIO.new
# first dump primary key column
if @connection.respond_to?(:pk_and_sequence_for)
pk, _ = @connection.pk_and_sequence_for(table)
elsif @connection.respond_to?(:primary_key)
pk = @connection.primary_key(table)
end
tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
if columns.detect { |c| c.name == pk }
if pk != 'id'
tbl.print %Q(, :primary_key => "#{pk}")
end
else
tbl.print ", :id => false"
end
tbl.print ", :force => true"
# Add table engine
res = @connection.execute "SHOW TABLE STATUS LIKE '#{table}'"
engine = res.first[res.fields.index("Engine")] rescue nil
tbl.print ", :options => 'ENGINE=#{engine}'" if engine
res = nil # Free the result
tbl.puts " do |t|"
# then dump all non-primary key columns
column_specs = columns.map do |column|
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
next if column.name == pk
spec = {}
spec[:name] = column.name.inspect
# AR has an optimization which handles zero-scale decimals as integers. This
# code ensures that the dumper still dumps the column as a decimal.
spec[:type] = if column.type == :integer && [/^numeric/, /^decimal/].any? { |e| e.match(column.sql_type) }
'decimal'
else
column.type.to_s
end
spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && spec[:type] != 'decimal'
spec[:precision] = column.precision.inspect if column.precision
spec[:scale] = column.scale.inspect if column.scale
spec[:null] = 'false' unless column.null
spec[:default] = default_string(column.default) if column.has_default?
(spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
spec
end.compact
# find all migration keys used in this table
keys = [:name, :limit, :precision, :scale, :default, :null] & column_specs.map{ |k| k.keys }.flatten
# figure out the lengths for each column based on above keys
lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
# the string we're going to sprintf our values against, with standardized column widths
format_string = lengths.map{ |len| "%-#{len}s" }
# find the max length for the 'type' column, which is special
type_length = column_specs.map{ |column| column[:type].length }.max
# add column type definition to our format string
format_string.unshift " t.%-#{type_length}s "
format_string *= ''
column_specs.each do |colspec|
values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
values.unshift colspec[:type]
tbl.print((format_string % values).gsub(/,\s*$/, ''))
tbl.puts
end
tbl.puts " end"
tbl.puts
indexes(table, tbl)
tbl.rewind
stream.print tbl.read
rescue => e
stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
stream.puts "# #{e.message}"
stream.puts
end
stream
end
def indexes(table, stream)
if (indexes = @connection.indexes(table)).any?
add_index_statements = indexes.map do |index|
if index.name =~ /fulltext/i
" execute \"CREATE FULLTEXT INDEX #{index.name} ON #{index.table} (#{index.columns.join(',')})\""
elsif index.name =~ /spatial/i
" execute \"CREATE SPATIAL INDEX #{index.name} ON #{index.table} (#{index.columns.join(',')})\""
else
statement_parts = [
('add_index ' + remove_prefix_and_suffix(index.table).inspect),
index.columns.inspect,
(':name => ' + index.name.inspect),
]
statement_parts << ':unique => true' if index.unique
index_lengths = (index.lengths || []).compact
statement_parts << (':length => ' + Hash[index.columns.zip(index.lengths)].inspect) unless index_lengths.empty?
index_orders = (index.orders || {})
statement_parts << (':order => ' + index.orders.inspect) unless index_orders.empty?
' ' + statement_parts.join(', ')
end
end
stream.puts add_index_statements.sort.join("\n")
stream.puts
end
end
end
end