Using devise with multiple models


Different devise models can be a good idea if they have very little or none in common. Extending and overriding is simple but respect basic rules.

While building an admin section for the web application you might consider introducing devise for both frontend and backend users but then – separate. That would be desired way in case your user and admins have very little or none in common.

Before choosing this approach make sure you understand the consequences and difficulties that might arise with time:

  • managing different sign in points or merging them in the future will take you additional time
  • if you have a need to override devise controllers for both models you will have to duplicate the overridden classes in separate folders which makes things harder to manage in the future
  • the same complexity goes if your mailers look and behave in a different way
  • users will not be able to reuse their accounts for both sides of application
  • be ready to bring in additional mess 😉

On the other hand, if you really need devise on both of the models – there is no other way. While implementing solution keep in mind a few things.

For this purpose we will use user and admin as a two different models that need devise.

Models

When it comes to models, things are pretty simple, install devise for each model separately adding all necessary fields that match your needs of devise modules. Nice thing about this is that you can easily make your users registerable, while admins are not, they can be created only in the backend.

Mailer

In case you need to override some or both mailers to have maybe different style and content, you can do it by overriding the config.mailer. For overriding both to be the same but still costumed, you can edit the config.mailer in your devise.rb.

For overriding both models with different mailers, override devise_mailer in the model, so for ex:

class User
  def devise_mailer
    SpecialUserMailer
  end
end

The best place to keep those mailers would be of course in the mailers folder.

Controllers

Most probably you will have to override some of your actions for each model separately. As long as you keep them nicely organized you will not have issues. Good practice is to namespace the controllers. In the case of user and admin we could have devise controllers in /app/users/*_controller.rb and for admin in /app/admins/*controller.rb. routes.rb have to follow the same approach:

devise_for :admins,
controllers: { sessions: 'admins/sessions',
passwords: 'admins/passwords' }

devise_for :users,
controllers: { registrations: 'users/registrations',
confirmations: 'users/confirmations',
passwords: 'users/passwords' }

If the controller is not overriden in a different way, you can also place it under /app/devise/*_controller.rb which is the default location. *Quick note about session, devise creates one with a dynamic name, for user it creates currentuser and for admin currentadmin!

Views & I18n

Views follow the same approach, it is advised to use /app/views/users/... and /app/views/admins/..., or /app/views/devise/... for views that are exactly the same for user and admin users. As for the translations, if you are using 2 devise models at the same time, organize your translations well! Flash messages will request the specific resource translation so do not be surprised. It is always better on example, sessions module translations, devise.yml:

en:
  devise:
    sessions:
      admin:
        signed_in: You are signed in.
        signed_out: You are signed out
      user:
        signed_in: Welcome back!
        signed_out: See you another time!

Tests

Whichever framework you use for testing, respect the organization within app folder!

Conclusion

There, you got the point! Having and overriding devise for two models is simple but don’t let the simplicity misguide you. If you really need this approach keep your models/views/controllers clean and think twice before overriding things you need.