【问题标题】:Constraint failed during the OmniauthCallbacksControllerOmniauthCallbacksController 期间约束失败
【发布时间】:2013-03-01 02:44:57
【问题描述】:

我对 Ruby on Rails 还很陌生,所以请多多包涵。

我希望用户能够通过 Facebook 登录。我关注了 railscast #265 Devise and OmniAuth (revised)。在 railscast 中,用户在登录 Facebook 之前会被要求提供电子邮件地址。我希望用户能够通过 Facebook 登录,而无需事先提供电子邮件地址。

我使用什么:Devise 和 Omniauth-facebook gems

我的期望:用户点击链接,通过 Facebook 登录,Facebook 返回电子邮件、姓名、uid,我的应用程序将其保存到数据库并创建用户。

我得到了什么:错误消息ActiveRecord::StatementInvalid in OmniauthCallbacksController#facebook

SQLite3::ConstraintException: constraint failed: INSERT INTO "users" ("created_at", "current_sign_in_at", "current_sign_in_ip", "email", "encrypted_password", "last_sign_in_at", "last_sign_in_ip", "name", "provider", "remember_created_at", "reset_password_sent_at", "reset_password_token", "sign_in_count", "uid", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

这是我的 user.rb 模型:

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :token_authenticatable, :confirmable,
  # :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable, :omniauthable,
         :recoverable, :rememberable, :trackable, :validatable

  # Setup accessible (or protected) attributes for your model
  attr_accessible :email, :password, :password_confirmation, :remember_me
  # attr_accessible :title, :body

  # Nodig als :validatable uit is
  #validates_presence_of   :email, :if => :email_required?
  #validates_uniqueness_of :email, :allow_blank => true, :if => :email_changed?
  #validates_format_of     :email, :with  => Devise.email_regexp, :allow_blank => true, :if => :email_changed?
  #
  #validates_presence_of     :password, :if => :password_required?
  #validates_confirmation_of :password, :if => :password_required?
  #validates_length_of       :password, :within => Devise.password_length, :allow_blank => true


  def self.from_omniauth(auth)
    where(auth.slice(:provider, :uid)).first_or_create do |user|
      user.provider = auth.provider
      user.uid = auth.uid
      user.email = auth.email
      user.name = auth.info.name
    end
  end

  def self.new_with_session(params, session)
    if session["devise.user_attributes"]
      new(session["devise.user_attributes"], without_protection: true) do |user|
        user.attributes = params
        user.valid?
      end
    else
      super
    end
  end

  def email_required?
    provider == :facebook
  end

  def password_required?
    super && provider.blank?
  end

  def update_with_password(params, *options)
    if encrypted_password.blank?
      update_attributes(params, *options)
    else
      super
    end
  end

end

这是我向用户添加 OmniAuth 的迁移(用户已经有电子邮件)

class AddOmniauthToUsers < ActiveRecord::Migration
  def change
    add_column :users, :provider, :string
    add_column :users, :uid, :string
    add_column :users, :name, :string
  end
end

这是我的 OmniAuth 回调控制器:

class OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def all
      user = User.from_omniauth(request.env["omniauth.auth"])
      if user.persisted?
        flash.notice = "Loged in!"
        sign_in_and_redirect user
      else
        session["devise.user_attributes"] = user.attributes
        redirect_to new_user_registration_url
      end
    end
    alias_method :facebook, :all  
end

提前致谢

编辑:堆栈跟踪:

app/models/user.rb:23:in `from_omniauth'
app/controllers/omniauth_callbacks_controller.rb:3:in `all'

编辑:服务器日志 奇怪的是。当我打印auth 时,我可以在服务器日志中看到名称、电子邮件、提供商和 uid。然而,这会出现:

INSERT INTO "users" ("created_at", "current_sign_in_at", "current_sign_in_ip", "email", "encrypted_password", "last_sign_in_at", "last_sign_in_ip", "name", "provider", "remember_created_at", "reset_password_sent_at", "reset_password_token", "sign_in_count", "uid", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)  [["created_at", Tue, 26 Feb 2013 17:20:23 UTC +00:00], ["current_sign_in_at", nil], ["current_sign_in_ip", nil], ["email", nil], ["encrypted_password", ""], ["last_sign_in_at", nil], ["last_sign_in_ip", nil], ["name", nil], ["provider", "facebook"], ["remember_created_at", nil], ["reset_password_sent_at", nil], ["reset_password_token", nil], ["sign_in_count", 0], ["uid", "XXXXX"], ["updated_at", Tue, 26 Feb 2013 17:20:23 UTC +00:00]]
SQLite3::ConstraintException: constraint failed: INSERT INTO "users" ("created_at", "current_sign_in_at", "current_sign_in_ip", "email", "encrypted_password", "last_sign_in_at", "last_sign_in_ip", "name", "provider", "remember_created_at", "reset_password_sent_at", "reset_password_token", "sign_in_count", "uid", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

名称,电子邮件例如nil,但提供者和uid(现在为XXXXX)已填写。我在用户模型中添加了姓名和电子邮件。也许这里有问题?这可能有帮助吗?

【问题讨论】:

  • 你能发布错误的堆栈跟踪吗?在我看来,数据库似乎不允许字段为空(不知道是哪个),但您却提供了一个。

标签: ruby-on-rails ruby devise omniauth constraintexception


【解决方案1】:

每当我输入p auth.info.email(对于很多事情,您需要添加.info),服务器日志都会显示电子邮件地址。

这与我更改了用户模型有关:

  def self.from_omniauth(auth)
    where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
      user.provider = auth.provider
      user.uid = auth.uid
      user.email = auth.info.email
      user.name = auth.info.name
      user.save!
    end
  end

错误现在消失了。我添加了user.save! 我更改为auth.info.email 并将first_or_create 替换为first_or_initialize.tap

所以当我查看控制台 (User.all) 时,电子邮件、姓名、uid 和提供者都在数据库中。

显然现在一切正常。坦克伤害你的帮助!

p.s:服务器日志仍然给出问号,但确实将数据保存在记录中。

【讨论】:

    【解决方案2】:

    我猜你要么尝试创建一个没有所有必填字段的用户,要么你试图创建一个已经存在的用户(我怀疑电子邮件存在唯一性约束)。

    消除后者;从 users 表中删除所有用户。

    测试前者;尝试使用与 from_omniauth 方法相同的参数创建具有 User.create(some_params) 的用户。要弄清楚这些,请使用 pry 之类的东西,或者干脆这样做:

    def self.from_omniauth(auth)
        p auth # Add this line and watch the logs.
        ....
    end
    

    还有可能有帮助的是查看您的 db/schema.rb(或运行 rake db:structure:dump 后的 db/structure.sql),看看是否有任何 NOT NULL 或 UNIQUE 约束。

    【讨论】:

    • 我从数据库中删除了所有用户(rake db:drop 等)。当我尝试使用 Pry 添加用户时,它无法批量分配名称。我在我的用户模型中添加了 :uid 和 :name 。现在我可以在 Pry 中为用户添加名称或 uid。但是我仍然在浏览器中遇到同样的错误(我已经重新启动了服务器)
    • 您是否尝试过使用from_omniauth 接收的参数创建用户?
    • 老实说,我不知道该怎么做。
    • 好吧,就像我在回答中所说的那样。转到 User 模型中的第 23 行,直接在方法定义之后添加 p auth。这会将变量auth 打印到您的日志中。在您运行rails s 的控制台中查找它。为了让它脱颖而出,添加另一行阅读:p "X"*100。然后寻找一长串X。
    • 好的,谢谢你的解释。我也错过了:provider。现在我可以在控制台中使用来自from_omniauth 的所有参数(提供者、uid、电子邮件和名称)创建一个用户。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-05
    • 2021-12-09
    相关资源
    最近更新 更多