Страницы

Поиск по вопросам

понедельник, 8 июля 2019 г.

Rails SQLite3::ConstraintException: UNIQUE constraint failed: index 'index_users_on_reset_password_token'

Делаю регистрацию с 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

Комментариев нет:

Отправить комментарий