Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-Table Inheritance with acts_as_relation and Devise #2712

Closed
toobulkeh opened this issue Oct 31, 2013 · 1 comment
Closed

Multi-Table Inheritance with acts_as_relation and Devise #2712

toobulkeh opened this issue Oct 31, 2013 · 1 comment

Comments

@toobulkeh
Copy link

To make a long story short, I'm attempting to use MTI to create different roles in an application. While this isn't really documented well anywhere, I was hoping to combine the two models that I see as "best practice" (for lack of a better term).

My current issue is when creating or logging into the system using devise, devise seems to query ActiveRecord for specific column names, instead of using ruby attribute calls.

That is, when I try to login to the system, I receive a column not found:

SQLite3::SQLException: no such column: customers.email: SELECT  "customers"."id" AS t0_r0, "customers"."created_at" AS t0_r1, "customers"."updated_at" AS t0_r2, "users"."id" AS t1_r0, "users"."as_user_id" AS t1_r1, "users"."as_user_type" AS t1_r2, "users"."email" AS t1_r3, "users"."encrypted_password" AS t1_r4, "users"."reset_password_token" AS t1_r5, "users"."reset_password_sent_at" AS t1_r6, "users"."remember_created_at" AS t1_r7, "users"."sign_in_count" AS t1_r8, "users"."current_sign_in_at" AS t1_r9, "users"."last_sign_in_at" AS t1_r10, "users"."current_sign_in_ip" AS t1_r11, "users"."last_sign_in_ip" AS t1_r12, "users"."created_at" AS t1_r13, "users"."updated_at" AS t1_r14 FROM "customers" INNER JOIN "users" ON "users"."as_user_id" = "customers"."id" AND "users"."as_user_type" = 'Customer' WHERE "customers"."email" = 'testcustomer@test.com' LIMIT 1

and when I try to sign up, I receive what appears to be a validation problem:

NoMethodError (undefined method `text?' for nil:NilClass):
  activerecord (3.2.15) lib/active_record/validations/uniqueness.rb:57:in `build_relation'
  activerecord (3.2.15) lib/active_record/validations/uniqueness.rb:25:in `validate_each'
  activemodel (3.2.15) lib/active_model/validator.rb:153:in `block in validate'
  activemodel (3.2.15) lib/active_model/validator.rb:150:in `each'
  activemodel (3.2.15) lib/active_model/validator.rb:150:in `validate'
  activesupport (3.2.15) lib/active_support/callbacks.rb:310:in `_callback_before_55'
  activesupport (3.2.15) lib/active_support/callbacks.rb:429:in `_run__2666856343956316215__validate__3987179287797953709__callbacks'
  activesupport (3.2.15) lib/active_support/callbacks.rb:405:in `__run_callback'
  activesupport (3.2.15) lib/active_support/callbacks.rb:385:in `_run_validate_callbacks'
  activesupport (3.2.15) lib/active_support/callbacks.rb:81:in `run_callbacks'
  activemodel (3.2.15) lib/active_model/validations.rb:228:in `run_validations!'
  activemodel (3.2.15) lib/active_model/validations/callbacks.rb:53:in `block in run_validations!'
  activesupport (3.2.15) lib/active_support/callbacks.rb:425:in `_run__2666856343956316215__validation__3987179287797953709__callbacks'
  activesupport (3.2.15) lib/active_support/callbacks.rb:405:in `__run_callback'
  activesupport (3.2.15) lib/active_support/callbacks.rb:385:in `_run_validation_callbacks'
  activesupport (3.2.15) lib/active_support/callbacks.rb:81:in `run_callbacks'
  activemodel (3.2.15) lib/active_model/validations/callbacks.rb:53:in `run_validations!'
  activemodel (3.2.15) lib/active_model/validations.rb:195:in `valid?'
  activerecord (3.2.15) lib/active_record/validations.rb:69:in `valid?'
  activerecord (3.2.15) lib/active_record/validations.rb:77:in `perform_validations'
  activerecord (3.2.15) lib/active_record/validations.rb:50:in `save'
  activerecord (3.2.15) lib/active_record/attribute_methods/dirty.rb:22:in `save'
  activerecord (3.2.15) lib/active_record/transactions.rb:259:in `block (2 levels) in save'
  activerecord (3.2.15) lib/active_record/transactions.rb:313:in `block in with_transaction_returning_status'
  activerecord (3.2.15) lib/active_record/connection_adapters/abstract/database_statements.rb:192:in `transaction'
  activerecord (3.2.15) lib/active_record/transactions.rb:208:in `transaction'
  activerecord (3.2.15) lib/active_record/transactions.rb:311:in `with_transaction_returning_status'
  activerecord (3.2.15) lib/active_record/transactions.rb:259:in `block in save'
  activerecord (3.2.15) lib/active_record/transactions.rb:270:in `rollback_active_record_state!'
  activerecord (3.2.15) lib/active_record/transactions.rb:258:in `save'
  devise (3.1.1) app/controllers/devise/registrations_controller.rb:15:in `create'

To me, this looks like devise is relying on activerecord directly instead of the models I've since created. Is there a way to change this functionality or is there a reason it is written this way?

For reference, my routes is devise_for :customers
my user.rb:

class User < ActiveRecord::Base
  #Multi-Table Inheritance. A user can be a Customer, Employee, or an Admin
  acts_as_superclass

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

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

my customer:

class Customer < ActiveRecord::Base
  acts_as :user

  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
  attr_accessible :email, :password, :password_confirmation, :remember_me

end

(side note: I noticed that you need the devise statements here for the routes to generate properly, and the attr_accessible was added to see if that would permit devise to access the proper variables, to no avail)

and my related schema:

  create_table "customers", :force => true do |t|
    t.datetime "created_at", :null => false
    t.datetime "updated_at", :null => false
  end

  create_table "users", :force => true do |t|
    t.integer  "as_user_id"
    t.string   "as_user_type"
    t.string   "email",                  :default => "", :null => false
    t.string   "encrypted_password",     :default => "", :null => false
    t.string   "reset_password_token"
    t.datetime "reset_password_sent_at"
    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
  end

  add_index "users", ["email"], :name => "index_users_on_email", :unique => true
  add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true

Thanks for listening!

@josevalim
Copy link
Contributor

My current issue is when creating or logging into the system using devise, devise seems to query ActiveRecord for specific column names, instead of using ruby attribute calls.

Yes, Devise requiress the attribute names because Active Record does not provide mapping at the query level. For example, Devise needs to a database lookups by tokens, there is no way such lookups can rely on attributes.

It seems your error though is coming through by using the validatable module, which you can just exclude and roll your own validations (should be straight-forward to do, just copy the validations in validatable module and tweak accordingly).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants