2015-06-23 13 views
9

Ich habe eine Spalte in meiner MySQL-Datenbank, die vom Typ TINYINT (1) ist. Ich muss die tatsächlichen ganzen Zahlen in dieser Spalte speichern. Das Problem ist, dass Rails 4.1 aufgrund des Spaltentyps davon ausgeht, dass diese Spalte nur boolesche Werte enthält, so dass alle Werte außer 0 oder 1 beim Schreiben in die Datenbank auf 0 gesetzt werden.Rails 4.1 - Schreiben Sie in die MySQL-Datenbank ohne Typumwandlung

Ich möchte nicht einfach die boolesche Emulation deaktivieren, da wir eine Reihe von Spalten in unserer Datenbank haben, in denen wir TINYINT (1) verwenden, um einen booleschen Wert darzustellen. Und ich bin derzeit nicht in der Lage, die Spaltentypen in MySQL zu ändern.

Wie kann ich erzwingen, dass Rails 4.1 den Schritt der Typumwandlung überspringt und stattdessen direkt in die Datenbank schreibt?


(Dieser Auszug aus der Rails 4.1 Quelle von Nutzen sein kann: https://github.com/rails/rails/blob/4-1-stable/activerecord/lib/active_record/attribute_methods/write.rb)

+3

Weitere Kontext für andere Leser, 'TINYINT (1)' können signierte speichern ganzzahlige Werte -127 .. + 127 http://stackoverflow.com/questions/4401673/mysql- boolean-tinyint1-holds-values-up-to-127, aber MySQL [verwendet 'BOOLEAN' als Synonym dafür] (https://dev.mysql.com/doc/refman/5.0/en/numeric-type-overview .html). –

+0

Wäre es das Ende der Welt, wenn Sie diese Spalte über eine Migration zu einem regulären 'INT' öffnen würden? – tadman

+0

@tadman Glauben Sie mir, der Gedanke kam mir in den Sinn, aber aus Gründen außerhalb meiner Kontrolle kann ich die Struktur dieser Datenbank nicht ändern. –

Antwort

2

Könnten Sie Raw SQL zum Einfügen verwenden?

Etwas wie:

sql = "INSERT INTO my_table (smallnumber) VALUES (100)" 
ActiveRecord::Base.connection.execute(sql) 
+0

Dies war die Problemumgehung, die ich auch für mich gefunden habe. Ich hatte gehofft, eine Ruby-ähnliche Lösung zu finden, aber das ist letztendlich die eleganteste Lösung, die ich finden konnte. Die Belohnung geht also an Sie, Sir! –

+0

Besser noch, diese Methode funktioniert gut, wenn Sie den Standard-Setter für das Attribut überschreiben! –

1

Ich weiß nicht, ob es funktioniert, aber Sie können versuchen, die Setter mit der Methode zu überschreiben: raw_write_attribute oder: write_attribute The :raw_write_attribute and :write_attribute methods disable/enable the type casting before writing.

sagen Lassen Sie das Attribut/Spalte aufgerufen: the_boolean_column_who_wanted_to_be_an_integer, können Sie wahrscheinlich so etwas wie tun:

def the_boolean_column_who_wanted_to_be_an_integer=(value) 
    raw_write_attribute(:the_boolean_column_who_wanted_to_be_an_integer, value) # or write_attribute(... 
end 

Funktioniert es?

+0

Das urkomische Problem mit dieser Lösung ist diese Zeile in der Rails 4.1-Quelle: 'alias_method: raw_type_cast_attribute_for_write,: type_cast_attribute_for_write'. In Rails 4.1 unterscheidet sich raw_write_attribute anscheinend nicht von write_attribute! –

+0

Ich denke, dass Sie vielleicht Recht haben, dass diese Methode in neueren Versionen von Rails funktionieren würde. Ich glaube leider nicht, dass ich die Version in dem Projekt, an dem ich gerade arbeite, ändern kann. –

0

Vielleicht sollten Sie die Setter vollständig überschreiben, mit rails 4.1 source code:

def the_field=(value) 
    attr_name = 'the_field' 
    attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key 
    @attributes_cache.delete(attr_name) 
    column = column_for_attribute(attr_name) 

    # If we're dealing with a binary column, write the data to the cache 
    # so we don't attempt to typecast multiple times. 
    if column && column.binary? 
     @attributes_cache[attr_name] = value 
    end 

    if column || @attributes.has_key?(attr_name) 
     @attributes[attr_name] = value 
    else 
     raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'" 
    end 
end 

Beachten Sie, dass @attributes[attr_name] = send(type_cast_method, column, value)-@attributes[attr_name] = value geändert wurde. Sie können es wahrscheinlich für Ihren Anwendungsfall vereinfachen. Beachten Sie auch, dass ich dies nicht versucht habe, und selbst wenn es funktioniert, sollten Sie vorsichtig sein, wann immer Sie die Schienen aktualisieren möchten.

0

Plan A: Ändern Sie als Kompromiss auf SMALLINT (2 Bytes).

Plan B: Sehen Sie, wenn TINYINT(3) Rails in Ignorieren denkt, es ist Boolean.

Plan C: Sehen Sie, wenn TINYINT UNSIGNED wird Rails in Ignorieren denken, es ist Boolean. (Dies setzt voraus, dass Ihre Nummer nicht negativ ist: 0..255.)

+0

Dies ist eine gute Antwort, aber wie ich in den Kommentaren geklärt habe, bin ich * nicht * in der Lage, die Spaltentypen in MySQL zu ändern. (Und ich weiß aus Erfahrung, dass 'TINYINT (2)' gut funktioniert.) –