【问题标题】:Active Record Validations, presence of not working活动记录验证,存在不工作
【发布时间】:2014-02-28 14:07:30
【问题描述】:

我正在制作一个表单,为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)

【问题讨论】:

  • UsersController 中的create 方法在哪里?
  • 另外,你提交表单的时候可以分享服务器日志吗?
  • 我实际上没有进入聊天室的声誉。我发布了日志,UsersController 中没有create 方法。我是 Rails 的新手,所以我不确定为什么没有。我认为我们从某个地方继承了它。很抱歉我没有提供更多帮助。

标签: ruby-on-rails ruby forms validation


【解决方案1】:

这就是你的代码失败的原因。

Unpermitted parameters: institution_pid

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

或者……像这样。

查看更多 http://edgeapi.rubyonrails.org/classes/ActionController/Parameters.html

【讨论】:

    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多