活动记录验证,存在不工作

时间:2014-02-28 14:07:30

标签: ruby-on-rails ruby forms validation

我正在使用表单为Ruby on Rails项目创建新用户。我想首先提一下,在正确填写表单时,表单本身运行正常,并以正确的方式创建用户。但是,并非所有Active Record Validation似乎都有效,特别是validates :institution_pid, presence: true位。表单的其余部分按照应有的方式工作。如果元素为空白或填写正确,则会在屏幕上弹出错误消息,以便用户可以修复它并且不会提交表单。但是如果表单是在没有选择机构的情况下提交的,那么无论如何都会出现错误提交,然后我会收到此错误:

ActiveFedora::ObjectNotFoundError in UsersController#create

显然,这种情况正在发生,因为它试图从尚未填写的表格中使用该机构。我无法弄清楚为什么它正在这样做,并提交,而不是验证弹出错误,因为表单没有正确填写。

这是我的用户模型:

class User < ActiveRecord::Base
  # Connects this user object to Hydra behaviors. 
  include Hydra::User

  # Connects this user object to Blacklights Bookmarks. 
  include Blacklight::User
  include Aptrust::SolrHelper

  # Connects this user object to Role-management behaviors. 
  include Hydra::RoleManagement::UserRoles

  # Include default devise modules. Others available are:
  # :database_authenticatable,
  # :recoverable, :rememberable, :trackable, :validatable,
  # :token_authenticatable, :confirmable,
  # :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :recoverable, :rememberable, :trackable, :timeoutable,     :validatable

  validates :email, :phone_number, :role_ids, presence: true
  validates :email, uniqueness: true
  validates :institution_pid, presence: true
  validate :institution_pid_points_at_institution

  # Custom format validations.  See app/validators
  validates :name, person_name_format: true, if: ->{ name.present? }
  validates :email, email: true

  # Handle and normalize phone numbers
  phony_normalize :phone_number, :default_country_code => 'US'

  validates :phone_number, :phony_plausible => true

  # This method assigns permission groups
  def groups
    super + institution_groups
  end

  def institution_groups
    if institutional_admin?
      ["Admin_At_#{institution_group_suffix}"]
    elsif institutional_user?
      ["User_At_#{institution_group_suffix}"]
    else
      []
    end
  end

  # Blacklight uses #to_s on youruser class to get a user-displayable 
  # login/identifier for the account. 
  #
  # Method modified from the Blacklight default.
  def to_s
    name || email
  end

  def as_json(options = nil)
    json_data = super
    json_data.delete('api_secret_key')
    json_data.delete('encrypted_api_secret_key')
    json_data
  end

  # Roles are managed through the hydra-role-management gem.
  def is?(role)
    self.roles.pluck(:name).include?(role.to_s)
  end

  def admin?
    is? 'admin'
  end

  def institutional_admin?
    is? 'institutional_admin'
  end

  def institutional_user?
    is? 'institutional_user'
  end

  def role_id
    if(admin?)
      Role.where(name: 'admin').first_or_create.id
    elsif(institutional_admin?)
      Role.where(name: 'institutional_admin').first_or_create.id
    elsif(institutional_user?)
      Role.where(name: 'institutional_user').first_or_create.id
    end
  end

  # Since an Institution is an ActiveFedora Object, these two objects cannot be related as normal (i.e. belongs_to)
  # They will be connected through the User.institution_pid.
  def institution
    @institution ||= Institution.find(self.institution_pid)
  rescue ActiveFedora::ObjectNotFoundError => e
    logger.warn "#{self.institution_pid} is set as the institution for #{self}, but it doesn't exist"
    @institution = NilInstitution.new
  end

  def institution_group_suffix
    clean_for_solr(institution_pid)
  end

  # Guest users are disabled in this application.  The default Blacklight installation includes the gem devise-guests
  # which is not bundled with this app.  hydra-role-management gem requires a guest boolean, so we must provide it here.
  # This will be fixed in hydra-role-management 0.1.1
  def guest?
    false
  end

  attr_reader :api_secret_key

  def api_secret_key=(key)
    @api_secret_key = key
    self.encrypted_api_secret_key = if key.blank?
      nil
    else
       password_digest(key)
    end
  end

  # Generate a new API key for this user
  def generate_api_key(length = 20)
    self.api_secret_key = SecureRandom.hex(length)
  end

  # Verifies whether an API key (from sign in) matches the user's API key.
  def valid_api_key?(input_key)
    return false if encrypted_api_secret_key.blank?
    bcrypt  = ::BCrypt::Password.new(encrypted_api_secret_key)
    key = ::BCrypt::Engine.hash_secret("#{input_key}#{User.pepper}", bcrypt.salt)
    Devise.secure_compare(key, encrypted_api_secret_key)
  end

  class NilInstitution
    def name
      "Deleted Institution"
    end

    def to_param
      'deleted'
    end

    def brief_name
      "Deleted Institution"
    end

    def users
      []
    end

    def intellectual_objects
      []
    end

    def bytes_by_format
      {}
    end
  end

  private

  def institution_pid_points_at_institution
    errors.add(:institution_pid, "is not a valid institution") unless Institution.exists?(institution_pid)
  end

end

这是我的用户控制器:

class UsersController < ApplicationController
  inherit_resources
  load_and_authorize_resource
  before_filter :authenticate_user!

  def destroy
    name = @user.to_s
    destroy!(notice: "User #{@user.to_s} was deleted.")
  end

  def edit_password
    @user = current_user
  end

  def update_password
    @user = User.find(current_user.id)
    if @user.update_with_password(user_params)
      sign_in @user, :bypass => true
      redirect_to root_path
      flash[:notice] = "Successfully changed password."
    else
      redirect_to root_path
      flash[:alert] = "Current password was incorrect, new password was too short, or passwords did not match. Password has not been changed."
    end
  end

  def generate_api_key
    @user.generate_api_key

    if @user.save
      msg = ["Please record this key.  If you lose it, you will have to generate a new key.",
         "Your API secret key is: #{@user.api_secret_key}"]
      msg = msg.join("<br/>").html_safe
      flash[:notice] = msg
    else
      flash[:alert] = 'ERROR: Unable to create API key.'
    end

    redirect_to user_path(@user)
  end

  private

    def build_resource_params
      [params.fetch(:user, {}).permit(:name, :email, :phone_number, :password, :password_confirmation).tap do |p|
        p[:institution_pid] = build_institution_pid if params[:user][:institution_pid]
        p[:role_ids] = build_role_ids if params[:user][:role_ids]
      end]
    end

    def build_institution_pid
      institution = Institution.find(params[:user][:institution_pid])
      authorize!(:add_user, institution)
      institution.id
    end

    def build_role_ids
      [].tap do |role_ids|
        unless params[:user][:role_ids].empty?
          roles = Role.find(params[:user][:role_ids])

            authorize!(:add_user, roles)
            role_ids << roles.id

        end
      end
    end

  def user_params
    params.required(:user).permit(:password, :password_confirmation, :current_password)
  end

end

这是我正在使用的表单的代码:

<div class="page-header">
  <h1>Registering New User</h1>
</div>

<%= simple_form_for(@user, html: {class: 'form-horizontal'}) do |f| %>
    <%= f.error_notification %>

    <div class="form-inputs">
      <%= f.input :name, autofocus: true %>
      <%= f.input :email %>
      <%= f.input :phone_number %>

      <%= f.input :institution_pid, collection: institutions_for_select, as: :select, label: "Institution" %>

      <%= f.association :roles, collection: roles_for_select, as: :radio_buttons %>

      <%= f.input :password %>
      <%= f.input :password_confirmation %>

    </div>
    <br>
    <div class="form-actions">
      <%= button_tag(type: 'submit', class: "btn doc-action-btn btn-success") do %>
          <i class="glyphicon glyphicon-check"></i> Submit
      <% end %>
      <%= link_to @user, {class: "btn doc-action-btn btn-cancel"} do %>
          <i class="glyphicon glyphicon-remove"></i> Cancel
      <% end %>
    </div>
<% end %>

这是由机构元素的表单生成的HTML,一个给我带来麻烦的,如果有帮助的话。

<div class="controls">
  <select class="select required" id="user_institution_pid" name="user[institution_pid]">
    <option value=""></option>  
    <option value="aptrust-dev:363">APTrust</option>
    <option selected="selected" value="aptrust-dev:365">North Carolina State University</option>
    <option value="aptrust-dev:364">Columbia University</option>
  </select>
</div>

记录:

Started POST "/users" for 127.0.0.1 at 2014-02-28 09:05:15 -0500
Processing by UsersController#create as HTML
Parameters: {"utf8"=>"✓",    "authenticity_token"=>"WjALLu82iJDDNBEnNpdqupVERdYVOg1l1W/t5v7yaog=", "user"=>{"name"=>"", "email"=>"", "phone_number"=>"", "institution_pid"=>"", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}
Unpermitted parameters: institution_pid
Completed 500 Internal Server Error in 3ms

ActiveFedora::ObjectNotFoundError (Unable to find "" in fedora. ):
  app/controllers/users_controller.rb:52:in `build_institution_pid'
  app/controllers/users_controller.rb:46:in `block in build_resource_params'
  app/controllers/users_controller.rb:45:in `tap'
  app/controllers/users_controller.rb:45:in `build_resource_params'


  Rendered /Users/kec6en/.rvm/gems/ruby-2.0.0-p353@fluctus/gems/actionpack-4.0.3/lib/action_dispatch/middleware/templates/rescues/_source.erb (0.7ms)
  Rendered /Users/kec6en/.rvm/gems/ruby-2.0.0-p353@fluctus/gems/actionpack-4.0.3/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.1ms)
  Rendered /Users/kec6en/.rvm/gems/ruby-2.0.0-p353@fluctus/gems/actionpack-4.0.3/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (1.1ms)
  Rendered /Users/kec6en/.rvm/gems/ruby-2.0.0-p353@fluctus/gems/actionpack-4.0.3/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (22.0ms)

2 个答案:

答案 0 :(得分:0)

这是导致代码失败的原因。

Unpermitted parameters: institution_pid

DO params.require(:user).permit(:institution_pid)

或s.th.像这样。

了解更多 http://edgeapi.rubyonrails.org/classes/ActionController/Parameters.html

答案 1 :(得分:-1)

我明白了。无论如何,感谢您查看我的代码。我需要在build_institution_pid方法中添加一行来检查参数是否为空。现在一切都验证了应有的方式。