Делаю регистрацию с devise
Добавил поле username для User. Для авторизации использую email + pwd.
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys:[:username])
end
end
class User < ApplicationRecord
validates :username, presence: true
devise :database_authenticatable,
:registerable,
:rememberable,
:trackable,
:validatable
end
ActiveRecord::Schema.define(version: 20170228132910) do
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "username", default: "", null: false
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index [nil], name: "index_users_on_reset_password_token", unique: true
end
end
Первый пользователь зарагистрировался нормально, на втором получаю ошибку
ActiveRecord::RecordNotUnique in
Devise::RegistrationsController#create SQLite3::ConstraintException:
UNIQUE constraint failed: index 'index_users_on_reset_password_token':
INSERT INTO "users" ("email", "encrypted_password", "username",
"created_at", "updated_at") VALUES (?, ?, ?, ?, ?)
Что там ни так с index_users_on_reset_password_token ?
Добавил миграцию
class DeviseCreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
# Registrable
t.string :username, null: false, default: ""
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, default: 0, null: false
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
end
end
Ответ
Об этом сообщали в баг-трекер Rails (#27782) и это проблема в SQLite:
SQLite has a (mis-)feature that double-quoted names that cannot be
resolved to a table or column name are treated as strings. This was a
very early design decision, made long before SQLite went viral and
found itself running in everything device on the planet, and was
intended to make SQLite more compatible with MySQL, which at the time
was the most widely deployed database engine in the world. I regret
that choice now, but I cannot undo it without breaking backwards
compatibility.
— D. Richard Hipp, создатель SQLite
По-русски:
У SQLite есть (упоротая) особенность: идентификаторы в двойных кавычках, которые не удалось разрешить в название таблицы или столбца, воспринимаются как строки. Это был осознанный выбор, сделанный задолго до того, как SQLite "выстрелил" и оказался в самых разных устройствах по всему миру, и сделан он был для обеспечения лучшей совместимости с MySQL, в то время самой популярной СУБД в мире. Сейчас я об этом выборе жалею, но избавиться от него, не ломая обратной совместимости, уже не могу.
В Rails вроде полны решимости это поправить на своей стороне, но получится ли у них, вопрос открытый. Нельзя просто так взять и убрать кавычки у колонок в определении индекса. Это исправит именно этот баг, но добавит ограничений на то, какими могут быть имена колонок, а то и просто добавит новых багов.
Вы в миграции добавили уникальный индекс на колонку, которой не существует, reset_password_token. Из-за "особенности" выше в результате получился функциональный (вычисляемый) индекс по константной строке ("reset_password_token"). В результате вы не можете иметь в таблице под этим индексом больше одной строчки, потому что вставка ещё одной потребует добавления в уникальный индекс значения, которое там уже есть. Красота!
Решение сейчас — сделать новую миграцию, в которой стереть этот индекс
remove_index :users, name: :index_users_on_reset_password_token