Rails“无法写未知属性”

问题描述 投票:0回答:1

我整天都在尝试解决此问题,但我无法解决。

我遇到此错误:

ActiveModel::MissingAttributeError in Users::RegistrationsController#add_data
can't write unknown attribute `[:drivlicense_nr, :birth_nation]`

user.rb:

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  has_one :person
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :confirmable,
         :omniauthable, omniauth_providers: %i[facebook twitter google_oauth2]


  #validate :password_complexity

  private

  def password_complexity
    if password.present? && !password.match(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)./)
        errors.add :password, 'must include at least one lowercase letter, one uppercase letter, and one digit'
    end
  end

  def self.from_omniauth(auth)
    # Either create a User record or update it based on the provider (Google) and the UID
    where(email: auth.email, uid: auth.uid).first_or_create do |user|
      user.token = auth.credentials.token
      user.expires = auth.credentials.expires
      user.expires_at = auth.credentials.expires_at
      user.refresh_token = auth.credentials.refresh_token
      user.provider = auth.provider
      user.uid = auth.uid
      user.email = auth.info.email
      user.password = Devise.friendly_token[0,20]
      user.skip_confirmation!
      user.save!
    end
  end

    def self.new_with_session(params, session)
      super.tap do |user|
        if data = session['devise.facebook_data'] && session['devise.facebook_data']['extra']['raw_info']
            user.email = data['email'] if user.email.blank?
        end
        if data = session['devise.google_data'] && session['devise.google_data']['extra']['raw_info']
            user.email = data['email'] if user.email.blank?
        end
      end
    end
end

person.rb中,我正在使用自定义的primary_key,因为它们对于我的应用程序确实很重要。

class Person < ApplicationRecord
    belongs_to :user, optional: true
    has_many :cars
    self.primary_key = %i[drivlicense_nr birth_nation]

    VALID_FISCAL_CODE_REGEX = /\A^[A-Z]{6}[0-9]{2}[A-Z][0-9]{2}[A-Z][0-9]{3}[A-Z]$\z/
    validates :fiscal_code, presence: true, length: {is: 16}, format: { with: VALID_FISCAL_CODE_REGEX }
end

人员迁移:

class CreatePeople < ActiveRecord::Migration[5.2]
  def change
    create_table :people, primary_key: %i[drivlicense_nr birth_nation] do |t|
      t.integer     :usercode
      t.string      :email, null: false, default: ''
      t.string      :plate_nr, limit: 8, null: false, default: ''
      t.string      :drivlicense_nr, null: false, default: ''
      t.string      :fiscal_code, limit: 16
      t.string      :name
      t.string      :surname
      t.string      :phone_number
      t.date        :birth_date
      t.string      :birth_nation, limit: 2, null: false, default: 'IT'
      t.string      :birth_place
      t.string      :current_address
      t.string      :city
      t.string      :sex                       
      t.string      :region
      t.string      :zipcode, limit: 5
      t.string      :state, limit: 2
      t.timestamps  null: false
    end

    add_index :people, %i[drivlicense_nr birth_nation], name: 'index_people', unique: true
    add_index :people, :usercode,    name: 'index_people_on_usercode', unique: true
    add_index :people, :fiscal_code, unique: true
    # add_index :people, :pcode,                                                      unique: true
  end
end

人员迁移:

class DeviseCreateUsers < ActiveRecord::Migration[5.0]
  def change
      create_table :users do |t|
          t.integer     :usercode
          t.belongs_to  :person, index: true
          t.boolean     :admin, default: false
          t.string      :drivlicense_nr, null: false, default: ''
          t.string      :birth_nation, limit: 2, null: false, default: 'IT'
          t.string      :tpoliceman_id

          ## Database authenticatable
          t.string      :email
          t.string      :encrypted_password

          ## Recoverable
          t.string      :reset_password_token
          t.datetime    :reset_password_sent_at

          ## Rememberable
          t.datetime    :remember_created_at

          ## Trackable
          t.integer     :sign_in_count, null: false, default: 0
          t.datetime    :current_sign_in_at
          t.datetime    :last_sign_in_at
          t.string      :current_sign_in_ip
          t.string      :last_sign_in_ip

          ## Confirmable
          t.string      :confirmation_token
          t.datetime    :confirmed_at
          t.datetime    :confirmation_sent_at
          t.string      :unconfirmed_email # Only if using reconfirmable
          #t.datetime  :updated_at

          ## Omniauthable
          t.string      :provider
          t.string      :uid
          t.string      :refresh_token
          t.string      :token
          t.boolean     :expires
          t.integer     :expires_at

          ## Lockable
          t.integer     :failed_attempts,                      null: false, default: 0 # Only if lock strategy is :failed_attempts
          t.string      :unlock_token # Only if unlock strategy is :email or :both
          t.datetime    :locked_at

          t.timestamps  null: false
      end

      add_index :users, :usercode,             unique: true
      add_index :users, :email,                unique: true
      add_index :users, :reset_password_token, unique: true
      add_index :users, :confirmation_token,   unique: true
      add_index :users, :unlock_token,         unique: true
      #add_index :users, %i[drivlicense_nr birth_nation], name: 'index_users_on_person'#, unique: true
      # validates :drivlicense_nr, uniqueness: { scope: :birth_nation }
  end
end

[我想在这里做的是在我通过google / facebook注册后,用户必须填写另一份表格。现在,我还需要更新用户,以使drivlicense和birth_nation(我的主键)具有相同的值。一旦程序执行@ person.save,就会触发错误!Registration_Controller#add_data:

def add_data
    @user = User.find_by_email(params[:email])
    if request.get?
      @person = Person.new()
      render 'oauth_add_data'
    elsif request.put?
        @person = Person.new(person_params)
        @user.update(drivlicense_nr: @person['drivlicense_nr'], birth_nation: @person['birth_nation'])
        if @person.save!
          flash[:success] = "Sign up process successful"
          bypass_sign_in(@user)
          redirect_to root_url
        else
          render 'new'
        end
    end
  end

感谢您的帮助和耐心!

UPDATEschema.rb:

ActiveRecord::Schema.define(version: 2020_02_29_181024) do

  create_table "car_associated_person", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
    t.string "plate_nr", limit: 8
    t.string "drivlicense_nr"
    t.string "birth_nation", limit: 2
    t.string "email"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "cars", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
    t.string "plate_nr", limit: 8, default: "", null: false
    t.string "chassis_nr", default: "", null: false
    t.string "owner_drivlicense_nr", default: "", null: false
    t.string "owner_birth_nation", limit: 2
    t.date "enrollment_date", default: "2018-01-01", null: false
    t.string "enrollment_nr", default: "", null: false
    t.string "enrollment_nation", default: "", null: false
    t.string "brand", default: "", null: false
    t.string "model", default: "", null: false
    t.integer "infraction_nr", default: 0
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["chassis_nr"], name: "index_cars_on_chassis_nr", unique: true
    t.index ["owner_birth_nation"], name: "index_cars_on_owner_birth_nation", unique: true
    t.index ["owner_drivlicense_nr"], name: "index_cars_on_owner_drivlicense_nr", unique: true
    t.index ["plate_nr"], name: "index_cars_on_plate_nr", unique: true
  end

  create_table "fines", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
    t.bigint "car_id"
    t.integer "fine_nr", default: 0
    t.string "plate_nr", limit: 8, default: "", null: false
    t.datetime "fine_datetime", default: "2018-01-01 00:00:00", null: false
    t.string "fine_address", default: "", null: false
    t.string "infraction_article", limit: 10, default: "", null: false
    t.string "infraction_informcode"
    t.string "infraction_motivation", default: "", null: false
    t.integer "deduction_points", default: 0
    t.float "fine_amount_reduced", default: 0.0
    t.float "procedures_amount", default: 0.0
    t.float "fine_total_amount", default: 0.0
    t.integer "days_nr_payment", limit: 1, default: 5
    t.string "paid", default: "0"
    t.string "optional_penalty", default: "None"
    t.string "tpoliceman_idnr_1"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["car_id"], name: "index_fines_on_car_id"
    t.index ["fine_nr"], name: "index_fines_on_fine_nr", unique: true
    t.index ["infraction_informcode"], name: "index_fines_on_infraction_informcode", unique: true
    t.index ["plate_nr"], name: "index_fines_on_plate_nr", unique: true
  end

  create_table "people", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
    t.integer "usercode"
    t.string "email", default: "", null: false
    t.string "plate_nr", limit: 8, default: "", null: false
    t.string "drivlicense_nr", default: ""
    t.string "fiscal_code", limit: 16
    t.string "name"
    t.string "surname"
    t.string "phone_number"
    t.date "birth_date"
    t.string "birth_nation", limit: 2, default: "IT"
    t.string "birth_place"
    t.string "current_address"
    t.string "city"
    t.string "sex"
    t.string "region"
    t.string "zipcode", limit: 5
    t.string "state", limit: 2
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["fiscal_code"], name: "index_people_on_fiscal_code", unique: true
    t.index ["usercode"], name: "index_people_on_usercode", unique: true
  end

  create_table "users", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
    t.integer "usercode"
    t.integer "person_id"
    t.boolean "admin", default: false
    t.string "drivlicense_nr", default: "", null: false
    t.string "birth_nation", limit: 2, default: "IT", null: false
    t.string "tpoliceman_id"
    t.string "email"
    t.string "encrypted_password"
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.integer "sign_in_count", default: 0, null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.string "current_sign_in_ip"
    t.string "last_sign_in_ip"
    t.string "confirmation_token"
    t.datetime "confirmed_at"
    t.datetime "confirmation_sent_at"
    t.string "unconfirmed_email"
    t.string "provider"
    t.string "uid"
    t.string "refresh_token"
    t.string "token"
    t.boolean "expires"
    t.integer "expires_at"
    t.integer "failed_attempts", default: 0, null: false
    t.string "unlock_token"
    t.datetime "locked_at"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
    t.index ["email"], name: "index_users_on_email", unique: true
    t.index ["person_id"], name: "index_users_on_person_id"
    t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
    t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true
    t.index ["usercode"], name: "index_users_on_usercode", unique: true
  end

end
ruby-on-rails ruby activerecord rails-activerecord
1个回答
1
投票

Rails使用id字段作为surrogate key,然后在应用程序和数据库层中对该字段实施唯一性。这样,您就不必担心创建复合主键(如在学校中一样)。

Rails依赖于很多convention over configuration,主键只是其中之一。您可以配置任何您喜欢的主键,但是Rails会与您抗衡,特别是如果您想使用复合键。如果确实需要,可以使用gem来帮助您完成此操作,但是由于它会引入复杂性,因此我会尽量避免。

© www.soinside.com 2019 - 2024. All rights reserved.