Несколько типов пользователей в Ruby on Rails

#ruby-on-rails

#ruby-on-rails

Вопрос:

Я новичок в Ruby on Rails и следил за руководством. Но мне нужно больше информации.

Я создаю приложение, которое позволит получить доступ к различным типам пользователей: учащимся, родителям, учителям и администрации (все они имеют одинаковые атрибуты). Пока у меня есть модель для пользователя, в которой есть поля, адрес электронной почты и пароль, которые служат учетными данными для входа на сайт..

Тогда у меня есть шаблон для каждого типа пользователя. Образцовый Ученик , Образцовый Учитель ….

Что я хотел знать, так это как мне зарегистрировать пользователя, ссылаясь на каждый тип пользователя, и у меня уже есть пользователь таблицы.

Я искал и понял, что использование ‘devise’ было бы жизнеспособным решением, но не совсем понимаю, как это работает.

Кто-нибудь объяснит мне лучший способ сделать это?

Ответ №1:

Существует несколько способов решения этой проблемы, как указывают другие ответы. Во-первых, убедитесь, что вы понимаете разницу между аутентификацией (создание пользователя и вход в систему) и авторизацией на основе ролей (управление доступом к различным частям веб-приложения в зависимости от роли пользователя). Вы можете реализовать аутентификацию самостоятельно, но большинство разработчиков Rails используют Devise gem, потому что он надежный, полнофункциональный и хорошо протестирован. Авторизация может быть реализована с помощью простых дополнений к пользовательской модели, хотя разработчики часто используют драгоценные камни CanCanCan или Pundit для сложных приложений.

Для описанного вами варианта использования я рекомендую реализовать очень простую форму авторизации на основе ролей в вашей пользовательской модели. Сначала вам нужно будет создать модель пользователя с помощью Devise.

Новая функция Active Record, Enum, представленная в Rails 4.1, является самым простым способом реализации авторизации на основе ролей.

Создайте миграцию:

 $ rails generate migration AddRoleToUsers role:integer
  

Миграция будет выглядеть следующим образом:

 class AddRoleToUsers < ActiveRecord::Migration
  def change
    add_column :users, :role, :integer
  end
end
  

Добавьте код в свою пользовательскую модель для реализации перечисления:

 class User < ActiveRecord::Base
  enum role: [:student, :parent, :teacher, :admin]
  after_initialize :set_default_role, :if => :new_record?

  def set_default_role
    self.role ||= :student
  end

end
  

Вы определяете имена ролей и при необходимости можете изменять имена по мере необходимости (целочисленные значения, сохраненные с каждой записью пользователя, остаются неизменными). Активная запись ограничит присвоение атрибута набором предопределенных значений, поэтому вам не нужно добавлять какой-либо код, чтобы ограничить имена ролей определенным набором. Лучше всего то, что перечисления поставляются с набором удобных методов, которые позволяют напрямую запрашивать роль без какого-либо дополнительного кода. Для атрибута enum, показанного выше, вы можете использовать следующие методы:

 User.roles # => {"student"=>0, "parent"=>1, "teacher"=>2, "admin"=>1} # list all roles
user.student! # make a student user
user.student? # => true # query if the user is a student
user.role # => "student" # find out the user’s role
@users = User.student # obtain an array of all users who are students
user.role = 'foo' # ArgumentError: 'foo' is not a valid, we can’t set invalid roles
  

Вы можете использовать условные обозначения в контроллере:

 class UsersController < ApplicationController
  def index
    unless current_user.admin?
      redirect_to :back, :alert => "Access denied."
    end
    @users = User.all
  end
end
  

Или используйте его в представлении:

 <% if current_user.parent? %>
  <li><%= link_to 'Grade Reports', some_path %></li>
<% end %>
  

В общем, если управление доступом добавляет много кода к контроллеру, вам рекомендуется использовать CanCanCan или Pundit, потому что вы можете переместить сложную логику авторизации в центральное местоположение, отдельное от контроллеров («тощие контроллеры»). Однако, если ваши потребности так просты, как вы описываете, авторизация на основе ролей в контроллере является оптимальной.

Я написал руководство по Rails Pundit, в котором сравниваются оба подхода и более подробно рассказывается о простой авторизации на основе ролей, а также авторизации на основе ролей с помощью Pundit.

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

1. Очень хороший ответ! Небольшое замечание: я получил сообщение об ошибке, в котором говорится, что я не могу иметь поле «: parent» внутри перечисления, потому что в этом классе уже есть метод «parent», который определяется Active Record .

2. Спасибо, Дэниел, мне нравится использовать пользовательский код вместо gem. Мне просто интересно, что, если я хочу, чтобы у пользователя было несколько ролей. например. пользователь имеет роль : : учитель и: администратор

3. Неплохо. Кроме того, поскольку роль по умолчанию устанавливается после инициализации. Миграция может включать значение по умолчанию вместо использования обратного вызова. что-то вроде: add_column :users, :role, :integer, default: 0

4. По-прежнему ли необходимо / возможно определить user_id модели пользователя в качестве внешнего ключа в модели Student / других моделях? Спасибо!

Ответ №2:

Ну, вот как я бы это реализовал.

1) создайте модель учетной записи и добавьте полиморфную ссылку в ее миграцию следующим образом

   class Account < ActiveRecord:Migration
    def change
      create_table :accounts do |t|
        t.references :user, :polymorphic => true
        t.column :name, :string
      end
    end
  end
  

2) В каждом классе модели, например, классе student, я бы добавил следующую строку кода

   class Student < ActiveRecord::Base
    has_one :account, :as => :user
  end
  

3) В классе модели учетной записи

   class Account < ActiveRecord::Base
    belongs_to :user, :polymorphic => true
  end
  

4) Используйте devise generator для модели учетной записи.

5) Большой проблемой является создание нового учащегося, в основном вы будете создавать запись учетной записи, а затем назначать эту запись учетной записи вновь созданной записи учащегося.

a) Есть 2 способа сделать это: * Использование вложенных форм. * Использование простой формы и заставить контроллер выполнять всю работу.

Я бы рекомендовал второй вариант, выберите то, что подходит вам лучше всего, и дайте мне знать, если у вас возникнут какие-либо проблемы.

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

1. Спасибо за ответ. Может быть, я не очень ясно выразился, но у меня уже есть страница для создания пользователя с помощью моей модели пользователя (я могу выполнять вход и выход и проверять все данные для этой «таблицы»). Теперь я хочу иметь данные обеих моделей (User и UserType — Student / Parents / Admin / etc) на одной странице, а затем сохранить все данные в их соответствующих моделях.

Ответ №3:

Если вам нужны разные разрешения для приложений, возможно, вы хотите назначить роли своим пользователям. Это может быть достигнуто многими различными способами. Простой способ — использовать Rolify gem: https://github.com/EppO/rolify

Ответ №4:

В самой простой форме вы хотите иметь возможность выбирать тип пользователя при его регистрации.

 class User
  belongs_to: user_type
end

class UserType
  has_many: users
end
  

Основываясь на этом простом отношении «один ко многим», вы можете передать user_type в форме контроллеру и позволить контроллеру позаботиться о создании / связывании объектов

 user = User.create(params[:user])
user.user_type = UserType.find(params[:user][:user_type])
  

Это очень простой и быстро собранный код с намерением немного прояснить мою точку зрения.

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

1. type атрибут зарезервирован для наследования одной таблицы, что может быть уместно в этом случае, но, по крайней мере, вы должны указать, что этот ответ подразумевает, что STI и модель Student и Teacher должны наследоваться от User.

2. спасибо за ваш комментарий @CV-Gate. Я изменил свой ответ. Здесь я не использую STI.