#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.