简体   繁体   中英

Rails Devise - How to add more data to current_user

Suppose I have a User model

user.rb

class User < ActiveRecord::Base
    ...
end

with attributes like: name, username, access

access is an enum that tells me if the user is "staff" or "customer"

To get the name and username of the logged in user, I can do:

current_user.name
current_user.username

And suppose I have a Staff model

staff.rb

class Staff < ActiveRecord::Base
    belongs_to :user
end

with attributes like: salary, phone_number

And I also have a Customer model

customer.rb

class Customer < ActiveRecord::Base
    belongs_to :user
end

with attributes like: address, phone_number


I want to be able to call this on my staff's controller:

current_user.staff.salary

And this on my customer's controller:

current_user.customer.address

WHAT I TRIED SO FAR

I overwrote sessions_controller.rb

def create
  super
  model_name = current_user.access.capitalize.constantize
  spec = model_name.where(user_id: current_user.id).take
  session[:spec] = spec
end

So I'm able to access it via session[:spec], but not via current_user. Any ideas?

Well to begin with, your User model should reference the staff or customer, even if they are to stay blank

class User

  has_one :staff
  has_one :address

Just by doing this, you should be able to use current_user.customer.address . However...

I suggest you add some convenient methods in ApplicationController or a module that you include

def staff_signed_in?
  @staff_signed_in ||= (user_signed_in? and current_user.access == :staff)
end

def current_staff
  @current_staff ||= (current_user.staff if staff_logged_in?)
end

# same for customer
# Note that I use instance variables so any database queries are executed only once !

Then you can simply call

<% if customer_signed_in? %>
  <h2>Logged in as customer</h2>
  <p>Address : <%= current_customer.address %>
<% end %>

EDIT : about your concerns concerning database hits

You gave the example of current_user.customer.cart.products

This is indeed quite a nested association. My suggestion above already reduces it by one level (ie current_customer == current_user.customer). Then you have to go through carts to reach products... it isn't so bad in my opinion. If you need to call that often (current_customercustomer.cart) you can override the current_customer for a given controller and eager load the resources you know you will use use.

def UserShopController < ApplicationController
# Let's assume current_customer is defined in ApplicationController like I showed above
# UserShopController always uses the customer cart, so let's load it right at the beginning
...

private
    # Override with eager loading
    def current_customer
      @current_customer ||= (current_user.customer.includes(:cart) if customer_logged_in?)
    end

add has_one :customer to your user.rb

Your user model should be like below to accessing related model.

class User < ActiveRecord::Base
    has_one :customer
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM