2015-06-23 23 views
9

Mam kolumnę w mojej bazie danych MySQL, która jest typu TINYINT (1). Muszę przechowywać rzeczywiste liczby całkowite w tej kolumnie. Problem polega na tym, że z powodu typu kolumny, Railsy 4.1 zakładają, że ta kolumna zawiera tylko wartości logiczne, więc powoduje, że wszystkie wartości oprócz 0 lub 1 mają wartość 0, gdy są zapisywane do bazy danych.Rails 4.1 - Zapis do bazy danych MySQL bez typowania danych

Nie chcę po prostu wyłączać emulacji boolowskiej, ponieważ w naszej bazie danych znajduje się kilka kolumn, w których używamy TINYINT (1) do rzeczywistego reprezentowania wartości logicznej. I obecnie nie jestem w stanie zmienić typów kolumn w MySQL.

Jak zmusić Rails 4.1 do ominięcia etapu typowania i zamiast tego zapisania bezpośrednio w bazie danych?


(ten fragment z Rails 4.1 może być źródłem pewnego użytku: https://github.com/rails/rails/blob/4-1-stable/activerecord/lib/active_record/attribute_methods/write.rb)

+3

Dodatkowy kontekst dla innych czytelników, 'TINYINT (1)' może przechowywać podpisane wartości całkowite -127 .. + 127 http://stackoverflow.com/questions/4401673/mysql- boolean-tinyint1-hold-values-up-to-127 ale MySQL [używa 'BOOLEAN' jako synonimu tego] (https://dev.mysql.com/doc/refman/5.0/en/numeric-type-overview .html). –

+0

Czy to byłby koniec świata, jeśli otworzysz tę kolumnę do zwykłego "INT" używającego migracji? – tadman

+0

@tadman Uwierz mi, ta myśl przyszła mi do głowy, ale z przyczyn pozostających poza moją kontrolą nie jestem w stanie zmienić struktury tej bazy danych. –

Odpowiedz

2

Czy możesz użyć surowego kodu SQL do wstawienia?

Coś jak:

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

To było obejście, które znalazłem również dla siebie. Miałem nadzieję, że uda mi się znaleźć bardziej podobne do Rubiego rozwiązanie, ale jest to ostatecznie najbardziej eleganckie rozwiązanie, jakie udało mi się znaleźć. Więc nagroda trafi do ciebie, sir! –

+0

Jeszcze lepiej, ta metoda działa dobrze, gdy przesłonisz domyślny ustawnik dla atrybutu! –

1

Nie wiem, czy to działa, ale można spróbować zastąpić setter stosując metodę: raw_write_attribute lub: write_attribute. The :raw_write_attribute and :write_attribute methods disable/enable the type casting before writing.

Powiedzmy atrybut/kolumna nazywa się: the_boolean_column_who_wanted_to_be_an_integer, prawdopodobnie można zrobić coś takiego:

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 

Czy to działa?

+0

Zabawnym problemem z tym rozwiązaniem jest ta linia w źródle Rails 4.1: 'alias_method: raw_type_cast_attribute_for_write,: type_cast_attribute_for_write'. Tak więc w Railsach 4.1, raw_write_attribute najwyraźniej nie różni się od write_attribute! –

+0

Myślę, że możesz mieć rację, jednak, że ta metoda zadziała w nowszych wersjach Railsów. Niestety nie sądzę, bym mógł zmienić wersję w projekcie, nad którym pracuję. –

0

Może należy zastąpić całkowicie seter korzystając 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 

Zauważ, że @attributes[attr_name] = send(type_cast_method, column, value) została zmieniona na @attributes[attr_name] = value. Prawdopodobnie można go uprościć w przypadku użycia. Zauważ też, że nie próbowałem tego, a nawet jeśli to działa, powinieneś być ostrożny, kiedy chcesz uaktualnić szyny.

0

Plan A: Zmień na SMALLINT (2 bajty) jako kompromis.

Plan B: Sprawdź, czy TINYINT(3) będzie oszukiwać Railsy, ​​nie myśląc, że jest to Boolean.

Plan C: Sprawdź, czy TINYINT UNSIGNED będzie oszukiwać Railsy, ​​nie myśląc, że jest to Boolean. (Zakładamy, że Twój numer jest nieujemny: 0..255.)

+0

To jest dobra odpowiedź, ale jak wyjaśniłem w komentarzach, nie jestem * w stanie * zmienić typów kolumn w MySQL. (I wiem z doświadczenia, że ​​'TINYINT (2)' działa dobrze.) –