usos tutorial sintaxis rails programas pagina funciona español ejemplos crear con como comandos caracteristicas basicos ruby-on-rails devise multiple-models

ruby-on-rails - tutorial - ruby on rails usos



Múltiples modelos de usuario con Ruby On Rails y diseñar rutas de registro separadas pero una ruta de inicio de sesión común (3)

De acuerdo, entonces lo resolví y obtuve la siguiente solución.
Necesitaba idear un poco de vestuario, pero no es tan complicado.

El modelo de usuario

# user.rb class User < ActiveRecord::Base devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable attr_accessible :email, :password, :password_confirmation, :remember_me belongs_to :rolable, :polymorphic => true end

El modelo del cliente

# customer.rb class Customer < ActiveRecord::Base has_one :user, :as => :rolable end

El modelo de Diseñador

# designer.rb class Designer < ActiveRecord::Base has_one :user, :as => :rolable end

Entonces, el modelo de Usuario tiene una asociación polimórfica simple que define si es un Cliente o un Diseñador.
Lo siguiente que tuve que hacer fue generar vistas de diseño con rails g devise:views para ser parte de mi aplicación. Como solo necesitaba personalizar el registro, guardé la carpeta app/views/devise/registrations únicamente y eliminé el resto.

Luego personalicé la vista de registros para nuevos registros, que se pueden encontrar en la app/views/devise/registrations/new.html.erb después de generarlos.

<h2>Sign up</h2> <% # customized code begin params[:user][:user_type] ||= ''customer'' if ["customer", "designer"].include? params[:user][:user_type].downcase child_class_name = params[:user][:user_type].downcase.camelize user_type = params[:user][:user_type].downcase else child_class_name = "Customer" user_type = "customer" end resource.rolable = child_class_name.constantize.new if resource.rolable.nil? # customized code end %> <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %> <%= my_devise_error_messages! # customized code %> <div><%= f.label :email %><br /> <%= f.email_field :email %></div> <div><%= f.label :password %><br /> <%= f.password_field :password %></div> <div><%= f.label :password_confirmation %><br /> <%= f.password_field :password_confirmation %></div> <% # customized code begin %> <%= fields_for resource.rolable do |rf| %> <% render :partial => "#{child_class_name.underscore}_fields", :locals => { :f => rf } %> <% end %> <%= hidden_field :user, :user_type, :value => user_type %> <% # customized code end %> <div><%= f.submit "Sign up" %></div> <% end %> <%= render :partial => "devise/shared/links" %>

Para cada tipo de Usuario, creé un parcial por separado con los campos personalizados para ese tipo de Usuario específico, es decir, Diseñador -> _designer_fields.html

<div><%= f.label :label_name %><br /> <%= f.text_field :label_name %></div>

Luego configuré las rutas para diseñar el uso del controlador personalizado en registros

devise_for :users, :controllers => { :registrations => ''UserRegistrations'' }

Luego generé un controlador para manejar el proceso de registro personalizado, copié el código fuente original del método create en Devise::RegistrationsController y lo modifiqué para que funcionara a mi manera (no olvide mover sus archivos de vista a la carpeta apropiada, en my case app/views/user_registrations

class UserRegistrationsController < Devise::RegistrationsController def create build_resource # customized code begin # crate a new child instance depending on the given user type child_class = params[:user][:user_type].camelize.constantize resource.rolable = child_class.new(params[child_class.to_s.underscore.to_sym]) # first check if child instance is valid # cause if so and the parent instance is valid as well # it''s all being saved at once valid = resource.valid? valid = resource.rolable.valid? && valid # customized code end if valid && resource.save # customized code if resource.active_for_authentication? set_flash_message :notice, :signed_up if is_navigational_format? sign_in(resource_name, resource) respond_with resource, :location => redirect_location(resource_name, resource) else set_flash_message :notice, :inactive_signed_up, :reason => inactive_reason(resource) if is_navigational_format? expire_session_data_after_sign_in! respond_with resource, :location => after_inactive_sign_up_path_for(resource) end else clean_up_passwords(resource) respond_with_navigational(resource) { render_with_scope :new } end end end

Lo que básicamente hace todo esto es que el controlador determina qué tipo de usuario debe crearse de acuerdo con el parámetro user_type que se entrega al método create del controlador por el campo oculto en la vista que usa el parámetro mediante un simple parámetro GET en la URL.

Por ejemplo:
Si va a /users/sign_up?user[user_type]=designer , puede crear un Diseñador.
Si va a /users/sign_up?user[user_type]=customer , puede crear un Cliente.

my_devise_error_messages! método es un método auxiliar que también maneja los errores de validación en el modelo asociativo, basado en el original devise_error_messages! método

module ApplicationHelper def my_devise_error_messages! return "" if resource.errors.empty? && resource.rolable.errors.empty? messages = rolable_messages = "" if !resource.errors.empty? messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join end if !resource.rolable.errors.empty? rolable_messages = resource.rolable.errors.full_messages.map { |msg| content_tag(:li, msg) }.join end messages = messages + rolable_messages sentence = I18n.t("errors.messages.not_saved", :count => resource.errors.count + resource.rolable.errors.count, :resource => resource.class.model_name.human.downcase) html = <<-HTML <div id="error_explanation"> <h2>#{sentence}</h2> <ul>#{messages}</ul> </div> HTML html.html_safe end end

ACTUALIZAR:

Para poder admitir rutas como /designer/sign_up y /customer/sign_up , puede hacer lo siguiente en su archivo de rutas:

# routes.rb match ''designer/sign_up'' => ''user_registrations#new'', :user => { :user_type => ''designer'' } match ''customer/sign_up'' => ''user_registrations#new'', :user => { :user_type => ''customer'' }

Cualquier parámetro que no se use internamente en la sintaxis de las rutas se pasa al hash de parámetros. Entonces :user pasa al hash de parámetros.

Eso es todo. Con un poco de tweeking aquí y allá lo conseguí trabajando de una manera bastante general, que es fácilmente extensible con muchos otros modelos de usuarios que comparten una tabla de usuario común.

Espero que alguien lo encuentre útil.

Primero, busqué intensamente con Google y Yahoo y encontré varias respuestas sobre temas como el mío, pero en realidad no cubren todo lo que necesito saber.

Tengo varios modelos de usuario en mi aplicación, por ahora son Clientes, Diseñadores, Minoristas y parece que aún quedan más. Todos ellos tienen diferentes datos almacenados en sus tablas y varias áreas en el sitio que están permitidas o no. Así que pensé en ir de la forma + CanCan y probar suerte con asociaciones polimórficas, así que obtuve la siguiente configuración de modelos:

class User < AR belongs_to :loginable, :polymorphic => true end class Customer < AR has_one :user, :as => :loginable end class Designer < AR has_one :user, :as => :loginable end class Retailer < AR has_one :user, :as => :loginable end

Para el registro, tengo vistas personalizadas para cada tipo de Usuario diferente y mis rutas se configuran así:

devise_for :customers, :class_name => ''User'' devise_for :designers, :class_name => ''User'' devise_for :retailers, :class_name => ''User''

Por ahora, el controlador de registros se deja como estándar (que es "diseño / registros"), pero pensé que, dado que tenía diferentes datos para almacenar en diferentes modelos, ¿tendría que personalizar este comportamiento también?

Pero con esta configuración obtuve ayudantes como customer_signed_in? y designer_signed_in? , pero lo que realmente necesito es un ayudante general como user_signed_in? para las áreas en el sitio que son accesibles para todos los usuarios, sin importar qué tipo de usuario.

También me gustaría un ayudante de rutas como new_user_session_path lugar de varios new_*type*_session_path y así sucesivamente. De hecho, todo lo que necesito para ser diferente es el proceso de registro ...

Así que me preguntaba si esta es la forma de ir para este problema ??? ¿O hay una solución mejor / más fácil / menos debe personalizar para esto?

Gracias por adelantado,
Robert


Estaba siguiendo las instrucciones anteriores y descubrí algunos vacíos y las instrucciones estaban desactualizadas cuando lo estaba implementando.

Así que después de luchar con ello todo el día, permítanme compartir con ustedes lo que funcionó para mí, y con suerte les ahorrará algunas horas de sudor y lágrimas.

  • En primer lugar, si no está familiarizado con el polimorfismo RoR, revise esta guía: http://astockwell.com/blog/2014/03/polymorphic-associations-in-rails-4-devise/ Después de http://astockwell.com/blog/2014/03/polymorphic-associations-in-rails-4-devise/ habrá diseñado y instalado los modelos de usuarios y podrá comenzar a trabajar.

  • Después de eso, siga el gran tutorial de Vapire para generar las vistas con todos los partails.

  • Lo que encontré más frustrante fue que al usar la última versión de Devise (3.5.1), RegistrationController se negó a trabajar. Aquí está el código que lo hará funcionar de nuevo:

    def create meta_type = params[:user][:meta_type] meta_type_params = params[:user][meta_type] params[:user].delete(:meta_type) params[:user].delete(meta_type) build_resource(sign_up_params) child_class = meta_type.camelize.constantize child_class.new(params[child_class.to_s.underscore.to_sym]) resource.meta = child_class.new(meta_type_params) # first check if child intance is valid # cause if so and the parent instance is valid as well # it''s all being saved at once valid = resource.valid? valid = resource.meta.valid? && valid # customized code end if valid && resource.save # customized code yield resource if block_given? if resource.persisted? if resource.active_for_authentication? set_flash_message :notice, :signed_up if is_flashing_format? sign_up(resource_name, resource) respond_with resource, location: after_sign_up_path_for(resource) else set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format? expire_data_after_sign_in! respond_with resource, location: after_inactive_sign_up_path_for(resource) end else clean_up_passwords resource set_minimum_password_length respond_with resource end end end

  • y también agregue estas anulaciones para que las redirecciones funcionen bien:

    protected def after_sign_up_path_for(resource) after_sign_in_path_for(resource) end def after_update_path_for(resource) case resource when :user, User resource.meta? ? another_path : root_path else super end end

  • Para que los mensajes flash sigan funcionando, deberá actualizar config/locales/devise.en.yml lugar de RegistrosControlloer reemplazados por UserRegistraionsControlloer, todo lo que tendrá que hacer es agregar esta nueva sección:

    user_registrations: signed_up: ''Welcome! You have signed up successfully.''

Espero que les ahorre algunas horas.


No pude encontrar ninguna manera de comentar la respuesta aceptada, así que voy a escribir aquí.

Hay un par de cosas que no funcionan exactamente como dice la respuesta aceptada, probablemente porque están desactualizadas.

De todos modos, algunas de las cosas que tuve que resolver por mi cuenta:

  1. Para UserRegistrationsController, render_with_scope ya no existe, solo usa render :new
  2. La primera línea en la función create, nuevamente en UserRegistrationsController no funciona como se indica. Solo intenta usar

    # Getting the user type that is send through a hidden field in the registration form. user_type = params[:user][:user_type] # Deleting the user_type from the params hash, won''t work without this. params[:user].delete(:user_type) # Building the user, I assume. build_resource

en lugar de simplemente build_resource . Algún error de asignación masiva estaba apareciendo sin cambios.

  1. Si desea tener toda la información del usuario en el método current_user de Devise, realice estas modificaciones:

class ApplicationController < ActionController::Base protect_from_forgery

# Overriding the Devise current_user method alias_method :devise_current_user, :current_user def current_user # It will now return either a Company or a Customer, instead of the plain User. super.rolable end end