Тип Rails column_hash отличается от фактического типа базы данных

Я создаю свое первое приложение на Rails и сталкиваюсь с этой странной проблемой. Я использую Postgres, который поддерживает очень полезный для меня тип ENUM. Однако Rails не поддерживает это, поэтому я использую много «выполнений» в своих миграциях. Все они работают успешно и, кажется, работают. Моя база данных - именно та, которую я хочу, когда проверяю ее через pgAdmin. На данный момент я использую 3 типа ENUM в таблице users:

gender ('female', 'male')
interested_in ('men', 'women', 'men_and_women')
relationship_status ('single', ..., 'divorced')

Со стороны приложения я использую гем classy_enum. Затем я попытался обновить профиль пользователя, чтобы проверить, все ли работает. Сначала я удалил interested_in из формы редактирования профиля, потому что в нем была другая логика. Все нормально работало. Затем я добавил interested_in, обновил логику для нового типа и снова отправил форму. У меня такая ошибка:

PG::InvalidTextRepresentation: ERROR: invalid input value for enum interested_in: "0" :
UPDATE "users" SET "interested_in" = $1, "remember_token" = $2, "updated_at" = $3 WHERE "users"."id" = 1

Он пытается сохранить 0 в поле перечисления, которое не включает эту опцию, а затем явно терпит неудачу. Итак, когда я впервые увидел подобную ошибку, я подумал «откуда, черт возьми, этот 0?» но оказалось, что Rails конвертирует значение в целое число перед его сохранением, и всякий раз, когда вы конвертируете строку с нечисловыми символами, результатом будет 0. Я полагаю, что это происходит снова. Я подошел к консоли и набрал:

2.0.0p247 :001 > User.columns_hash['interested_in'].type
 => :integer

Когда я запускаю ту же команду для gender и relationship_status, я получаю nil, что имеет смысл, потому что Rails не понимает тип ENUM:

2.0.0p247 :007 > User.columns_hash['gender'].type
 => nil

В pgAdmin у меня есть:

введите описание изображения здесь

а также

введите описание изображения здесь

соответственно.

Я попытался перезапустить свой веб-сервер (Pow), сервер postgres, но типы по-прежнему не совпадают. Как это может быть возможным? Как работают адаптеры базы данных Rails? Что я могу сделать, чтобы разрешить эту запутанную ситуацию?


person Ariel    schedule 03.08.2013    source источник


Ответы (2)


Что ж, я отвечаю на свой вопрос, но пока не принимаю его, потому что все еще надеюсь, что кто-то с большим опытом прольет свет на эту проблему.

У меня было предчувствие, что это как-то связано с именем interested_in, которое начинается с "int". Затем я изменил имя на is_interested_in, но безуспешно, поэтому отказался от гипотезы. Потратив несколько часов на то, чтобы решить эту проблему, отбросив всю мою базу данных, перенеся ее снова, снова отбросив, загрузив ее из structure.sql - все безуспешно, я решил попробовать еще раз и изменил имя типа на user_interested_in ( потому что это даже не начинается с «i»). Опять не повезло. Прошло еще несколько часов, и, поскольку я не мог ничего найти в Интернете или придумать какую-либо другую гипотезу, я внимательно проверил имена других типов перечислений и увидел, что они вообще не включают "int" ни в одной позиции строки. . Затем я изменил имя типа с interested_in на attracted_to и знаете что? Это сработало.

User.columns_hash['interested_in'].type
 => nil

Я предполагаю, что в Rails есть ошибка. Похоже, он пытается быть умным и угадывает тип столбца с некоторой логикой вроде:

model_column_type = :integer if db_column_type.include?("int")

Но это какое-то безумие. Почему он не проверяет полное соответствие? Для меня это не имеет никакого смысла.

ИЗМЕНИТЬ

Ну вот и все. Только что проверил исходный код Rails, и это точно что оно делает:

# rails / activerecord / lib / active_record / connection_adapters / column.rb

require 'set'

module ActiveRecord
  # :stopdoc:
  module ConnectionAdapters
    # An abstract definition of a column in a table.
    class Column
      TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON'].to_set
      FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].to_set
      ...

      def simplified_type(field_type)
          case field_type
          when /int/i
            :integer
          when /float|double/i
            :float
          .
          .
          .
          when /boolean/i
            :boolean
          end
        end
    end
  end

Это серьезная проблема для баз данных, поддерживающих создание типов. Неужели регулярное выражение должно быть таким инклюзивным? Заставляет меня задуматься, должен ли я создавать проблему.

person Ariel    schedule 03.08.2013
comment
Это определенно кажется проблемой, о которой кто-то из команды Rails должен знать, если они еще этого не сделали. Я бы рекомендовал разместить сообщение в основном списке рассылки Rails с описанием проблема. - person Peter Brown; 15.08.2013
comment
ps Приятно видеть людей, использующих ClassyEnum в дикой природе. Дайте мне знать, если я могу что-нибудь улучшить! :) - person Peter Brown; 15.08.2013
comment
Да, я должен. Я был немного застенчивым, потому что я новичок в Rails, но мне кажется довольно очевидным, что это проблема Rails. Я только что отправил сообщение, ожидает утверждения. И ClassyEnums великолепен, он уже очень полный, но я дам вам знать, если что-нибудь придумаю. Молодец! ;) - person Ariel; 15.08.2013

Взгляните на следующую ссылку на репозиторий Github: здесь. Я не уверен, под какой лицензией он выпущен, поэтому будьте осторожны, обращаясь к нему за идеями по реализации.

person mike    schedule 16.08.2013
comment
Как правило, более полезно скопировать соответствующую информацию из предоставленной ссылки в свой ответ в дополнение к самой ссылке. - person Justin Ohms; 16.08.2013