2015-06-23 21 views
9

Ho una colonna nel mio database MySQL che è di tipo TINYINT (1). Ho bisogno di memorizzare interi effettivi in ​​questa colonna. Il problema è che, a causa del tipo di colonna, Rails 4.1 presuppone che questa colonna contenga solo valori booleani, quindi tipizza tutti i valori oltre a 0 o 1 per essere 0 quando scrive nel database.Rails 4.1 - Scrivi nel database MySQL senza typecasting

Non voglio semplicemente disabilitare l'emulazione booleana poiché abbiamo un numero di colonne nel nostro database in cui usiamo TINYINT (1) per rappresentare effettivamente un valore booleano. E al momento non sono in grado di modificare i tipi di colonna in MySQL.

Come posso forzare Rails 4.1 per ignorare il passaggio di tipizzazione e scrivere invece direttamente al database?


(Questo estratto dalle rotaie 4.1 sorgente può essere di qualche utilità: https://github.com/rails/rails/blob/4-1-stable/activerecord/lib/active_record/attribute_methods/write.rb)

+3

contesto aggiuntivo per altri lettori, 'TINYINT (1)' può memorizzare valori interi firmati -127 .. + 127 http://stackoverflow.com/questions/4401673/mysql- boolean-tinyint1-hold-values-up-to-127 ma MySQL [usa 'BOOLEAN' come sinonimo per esso] (https://dev.mysql.com/doc/refman/5.0/en/numeric-type-overview .html). –

+0

Sarebbe la fine del mondo se si aprisse quella colonna con un normale 'INT' usando una migrazione? – tadman

+0

@tadman Credimi, il pensiero mi è passato per la mente, ma per ragioni al di fuori del mio controllo non sono in grado di cambiare la struttura di questo database. –

risposta

2

È possibile utilizzare SQL non elaborato per eseguire l'inserimento?

Qualcosa di simile:

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

Questa è stata la soluzione che ho trovato anche per me stesso. Speravo di trovare una soluzione più simile a Ruby, ma questa è in definitiva la soluzione più elegante che sia stata in grado di trovare. Quindi la taglia va da te, signore! –

+0

Ancora meglio, questo metodo funziona bene quando si sostituisce il setter predefinito per l'attributo! –

1

Non so se funziona, ma si può provare a sovrascrivere il setter utilizzando il metodo: raw_write_attribute o: write_attribute. The :raw_write_attribute and :write_attribute methods disable/enable the type casting before writing.

Diciamo che l'attributo/colonna si chiama: the_boolean_column_who_wanted_to_be_an_integer, probabilmente si può fare qualcosa di simile:

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 

funziona?

+0

L'esilarante problema con questa soluzione è questa riga nel sorgente Rails 4.1: 'alias_method: raw_type_cast_attribute_for_write,: type_cast_attribute_for_write'. Quindi in Rails 4.1, raw_write_attribute non è diverso da write_attribute! –

+0

Penso che potresti avere ragione, però, che quel metodo avrebbe funzionato nelle versioni più recenti di Rails. Sfortunatamente non penso che sarò in grado di cambiare la versione del progetto su cui sto lavorando. –

0

Forse si dovrebbe sovrascrivere il setter completamente, usando 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 

Nota che @attributes[attr_name] = send(type_cast_method, column, value) è stato cambiato a @attributes[attr_name] = value. Probabilmente puoi semplificarlo per il tuo caso d'uso. Si noti inoltre che non ho provato questo, e anche se funziona, si dovrebbe fare attenzione ogni volta che si desidera aggiornare i binari.

0

Piano A: passare a SMALLINT (2 byte) come compromesso.

Piano B: vedere se TINYINT(3) ingannerà Rails nel non pensare che sia booleano.

Piano C: vedere se TINYINT UNSIGNED ingannerà Rails nel non pensare che sia booleano. (Ciò presuppone che il numero sia non negativo: 0..255.)

+0

Questa è una buona risposta, ma come ho chiarito nei commenti, non sono * in grado * di modificare i tipi di colonna in MySQL. (E so per esperienza che 'TINYINT (2)' funziona bene.) –