I'm a newbie in RoR and I'm trying to make a simple experiment. I want to have a User model and a Role model. Each user a single Role, but a single Role may refer to multiple Users.
So, here are my models:
class Role < ActiveRecord::Base
has_many :user
end
class User < ActiveRecord::Base
has_one :role
end
And here are migrations:
class CreateRoles < ActiveRecord::Migration
def change
create_table :roles do |t|
t.string :name, null: false, index: true, unique: true, limit: 16
t.integer :permissions, null: false
end
end
end
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email, null: false, index: true, unique: true, limit: 128
t.string :password_digest, null: false, limit: 40
t.string :password_salt, null: false, limit: 40
t.string :screen_name, default: '', limit: 32
t.belongs_to :role, index: true, foreign_key: true
t.timestamps null: false
end
end
end
What I want is to make it raise an exception when I try to connect user with a role that does not exist:
user = User.create(email: 'user@example.com', password_digest: 'pwd_dig',
password_salt: 'salt', role_id: 10)
Unfortunately this works and the new user is created, no matter that the role with id 10 does not exist.
So, how can I force foreign_key check here?
And another question about that. If I try to do like this:
user = User.create(email: 'user@example.com', password_digest: 'pwd_dig',
password_salt: 'salt', role: role)
it raises an exception because role does not have attribute user_id. There is no way to do like this, does it?
I'm a newbie in RoR
Welcome!!
#app/models/role.rb
class Role < ActiveRecord::Base
has_many :users
end
#app/models/user.rb
class User < ActiveRecord::Base
belongs_to :role
validates :role_id, presence: true, on: :create
validate :check_role, on: :create
private
def check_role
errors.add(:role_id, "Role doesn't exist") unless Role.exists? role_id
end
end
This will allow you the following:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def new
@user = User.new
@roles = Role.all
end
def create
@user = User.new user_params
@user.save
end
private
def user_params
params.require(:user).permit(:email, :etc, :role)
end
end
#app/views/users/new.html.erb
<%= form_for @user do |f| %>
<%= f.email_field :email %>
<%= f.collection_select :role_id, @roles, :id, :name %>
<%= f.submit %>
<% end %>
Because you're new, I'll give you some info:
1. Association
Your association is slightly incorrect - you'll be best using a belongs_to/has_many
relationship :
The belongs_to
will assign the foreign_key
in your User
model, allowing you to both reference and validate against it.
-
Secondly, you'll be able to use a validation to check whether the role
has been set correctly.
Each time a Rails model is saved, it calls a series of validators you've set -- allowing you to call such functionality as checking whether a role exists.
What you can do is add a custom validation:
class User < ActiveRecord::Base
belongs_to :role
validate :existance_of_role
private
def existance_of_role
if role_id && !Role.exists?(role_id)
errors.add(:role, "must already exist.")
end
end
end
Also you need to use belongs_to :role
. has_one
would place the foreign_key in the relationship on the Role
model instead of on the user.
Also if you want to enforce on the database level that the user has a role you would add a NOT NULL constraint to users.role_id
.
Run: rails g migration AddNotNullConstraintToUsers
And then edit the migration file created:
class AddNotNullConstraintToUsers < ActiveRecord::Migration
def change
change_column_null(:users, :role_id, false)
end
end
After running the migration you can change the validation so that it will add an error also when the role_id is nil.
private
def existance_of_role
errors.add(:role, "must already exist.") unless Role.exists?(role_id)
end
You could do the same thing with validates_presence_of :role
but that will not enforce that the Role is pre-existing.
You can add this validator at your User model
class User < ActiveRecord::Base
has_one :role
validate :role_id_exists
def role_id_exists
return false if Role.find_by_id(self.role_id).nil?
end
end
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.