Create a model by passing a relational id from strong parameters - ruby-on-rails

I'm building an API with Rails 4. There are Org and User models which are related. (below)
Now, I'd like to create new user from parameters like this.
POST /users parameter:
{
"name": "Rails",
"org_id": 1 // existing Org.id, Org.name == 'Ruby'
}
and response should be like this:
{
"name": "Rails",
"org": {
"name": "Ruby"
}
}
I'd like to pass org_id to User.create in order to relate a new user and the existing org. How can I do efficiently with strong parameters?
CODES
models/org.rb
class Org < ActiveRecord::Base
has_many :users
end
models/user.rb
class User < ActiveRecord::Base
belongs_to :org
validates :org_id, presence: true
validates :name, presence: true
end
controllers/users_controller.rb
class API::UsersController < API::ApplicationController
def create
#user = User.create! create_params # can I do this?
end
private
def create_params
params.permit(:name, :org_id)
end
end

Change app/models/user.rb:
class User < ActiveRecord::Base
belongs_to :org
validates :org_id, presence: true
validates :name, presence: true
accepts_nested_attributes_for :org
end
Now, your params should be like:
params = { "name" => "User name here!", "org_attributes" => { "id"=> "1", "name" => "Ruby" } }
Here, passing id in org_attributes will update the existing record for user, when you do:
#user = User.create! user_params
inside your controller, method user_params:
private
def user_params
params.permit(:name, org_attributes:[:id, :name])
end
And now, #user = User.create! user_params should create a user with existing org only if you pass id inside org_attributes params.

Related

Rails: Unpermitted parameter in Rails 5

First of all I want simply get an object inside the current object that I'm sending to my backend.
I have this simple JSON (generated from a form):
{
"name": "Project 1",
"project_criteria": [
{
"name": "Criterium 1",
"type": "Type 1",
"benefit": "1"
},
{
"name": "Criterium 2",
"type": "Type 2",
"benefit": "3"
}
]
}
My classes:
class Project < ApplicationRecord
has_many :project_criteria
accepts_nested_attributes_for :project_criteria
end
class ProjectCriterium < ApplicationRecord
belongs_to :project
end
ProjectsController:
def project_params
params.require(:project).permit(:name, project_criteria: [] )
end
But I still can't access project_criteria parameter as you can see below:
Started POST "/projects" for 127.0.0.1 at 2016-08-19 16:24:03 -0300
Processing by ProjectsController#create as HTML
Parameters: {"project"=>{"name"=>"Project 1", "project_criteria"=>{"0"=>{"benefit"=>"1", "name"=>"Criterium 1", "type"=>"Type 1"}, "1"=>{"benefit"=>"3", "name"=>"Criterium 2", "type"=>"Type 2"}}}}
Unpermitted parameter: project_criteria # <-----------
Note:
By the way, I already tried to use criterium instead of criteria(which - in my opinion - is the correct since it should be pluralized) in has_many and accepts_nested_attributes_for, but it also doesn't work.
Does someone have a solution for this?
It's not the inflection of the word "criteria" that's giving you problems (although you can add a custom inflector to get the singular and plural versions you prefer if you really want).
The issue is that you have to explicitly permit the fields of nested objects.
Change your current params:
params.require(:project).permit(:name, project_criteria: [] )
To this (for a single nested object):
params.require(:project).permit(:name, project_criteria: [:name, :type, :benefit] )
Your case is somewhat compounded by the fact that you're dealing with multiple nested objects, so you'll have to pass a hash instead:
params.require(:project).permit(:name, { project_criteria: [:name, :type, :benefit]} )
I had this issue when working on a Rails 6 application.
My application consists of a User model that has a one-to-one relationship a Personal_Info model
My original code was this:
User Model
class User < ApplicationRecord
has_one :personal_info, class_name: 'PersonalInfo', dependent: :destroy
accepts_nested_attributes_for :personal_info, allow_destroy: true
end
Personal Info Model
class PersonalInfo < ApplicationRecord
belongs_to :user
end
User Controller
class UsersController < ApplicationController
def index
#users = User.all
end
.
.
def user_params
params.require(:user).permit(:email, :password, :password_confirmation,
personal_info_attributes: [:first_name,
:last_name, :phone, :gender, :dob,
:address, :city, :state, :country])
end
end
The issue was that I did not add the Personal_Info id to the accepted user params (parameters).
Here's how I fixed it:
I simply had to add the Personal_Info id to the UsersController params this way:
User Controller
class UsersController < ApplicationController
def index
#users = User.all
end
.
.
def user_params
params.require(:user).permit(:email, :password, :password_confirmation,
personal_info_attributes: [:id, :first_name,
:last_name, :phone, :gender, :dob,
:address, :city, :state, :country])
end
end
Another way is to add the update_only option to the Users Model this way:
class User < ApplicationRecord
has_one :personal_info, class_name: 'PersonalInfo', dependent: :destroy
accepts_nested_attributes_for :personal_info, update_only: true, allow_destroy: true
end
That's all.
I hope this helps

NoMethodError at / undefined method `name' for nil:NilClass

I have tried many solution and came up with good one but still getting error. I am editing my whole question.
I am trying to create Friendly URL with friendly_id gem.
In my project First user need to signup with devise.
Devise will pass some information to profile model with
model/user.rb
delegate :age, :city, :region, :country, to: :profile
I want to make user.name to be Friendly_id candidate. I have tried following code in my Profile model:-
class Profile < ActiveRecord::Base
extend FriendlyId
friendly_id :user_name , use: :slugged
def user_name
user.name
end
But it is giving error
NoMethodError at /
undefined method `name' for nil:NilClass
now After submitting user form.
Please suggest possible solution with explanation.
My User.rb looks like
require 'open-uri'
class User < ActiveRecord::Base
paginates_per 10
validates :name , presence: true, length: { maximum: 200 }
scope :by_name, ->(name) do
joins(:profile).where('lower(name) LIKE ?', "%#{name.downcase}%")
end
delegate :age, :city, :region, :country, to: :profile
has_one :profile, dependent: :destroy
accepts_nested_attributes_for :profile
def self.new_with_session(params, session)
session_params = { 'profile_attributes' => {} }
provider = session['devise.provider']
if provider && data = session["devise.#{provider}"]
session_params['name'] = data[:name] if data[:name]
session_params['email'] = data[:email] if data[:email]
session_params['profile_attributes'] =
{ avatar: data[:image] } if data[:image]
end
params.deep_merge!(session_params)
super.tap do |user|
if auth = Authorization.find_from_session(session, provider)
user.authorizations << auth
end
end
end
def password_required?
super && registered_manually?
end
def registered_manually?
encrypted_password.present?
end
end
And my profile.rb looks like
class Profile < ActiveRecord::Base
extend FriendlyId
friendly_id user.name, use: :slugged
belongs_to :user
accepts_nested_attributes_for :user
validates :website, allow_blank: true, uri: true
def website=(url_str)
if url_str.present?
url_str = "http://#{url_str}" unless url_str[/^https?/]
write_attribute :website, url_str
end
end
end
I think Problem is here:
Request parameters
{"action"=>"new", "controller"=>"users/registrations"}
Please suggest possible solution and explanation.
And users/registration:
class Users::RegistrationsController < Devise::RegistrationsController
layout 'land'
def create
params[:user][:profile_attributes].delete(:place)
end
protected
def after_sign_up_path_for(resource)
welcome_path
end
end
I am creating user in profile controller
def load_profile
#profile = Profile.friendly.find(params[:id])
if !#profile || #profile.user.blocked_users.include?(current_user)
redirect_to home_path
else
#user = #profile.user
end
end
#Rodrigo helped me find out error that error is due to Friendly_id can't create link with user instance.
There is an error on this line:
friendly_id user.name, use: :slugged
The variable user doesn't exists at Profile class scope. You should use something like this:
friendly_id :user_name, use: :slugged
def user_name
user.name
end
extend FriendlyId
friendly_id u_name, use: :slugged
def u_name
user.name
end
belongs_to :user
Have you defined user? what is user.name?

Using Roles for Validations in Rails

Is it possible to use the roles used for attr_accessible and attr_protected? I'm trying to setup a validation that only executes when not an admin (like this sort of http://launchware.com/articles/whats-new-in-edge-scoped-mass-assignment-in-rails-3-1). For example:
class User < ActiveRecord::Base
def validate(record)
unless # role.admin?
record.errors[:name] << 'Wrong length' if ...
end
end
end
user = User.create({ ... }, role: "admin")
After looking into this and digging through the source code, it appears that the role passed in when creating an Active Record object is exposed through a protected method mass_assignment_role. Thus, the code in question can be re-written as:
class User < ActiveRecord::Base
def validate(record)
unless mass_assignment_role.eql? :admin
record.errors[:name] << 'Wrong length' if ...
end
end
end
user = User.create({ ... }, role: "admin")
Sure can would be something like this:
class User < ActiveRecord::Base
attr_accessible :role
validates :record_validation
def record_validation
unless self.role == "admin"
errors.add(:name, "error message") if ..
end
end
You could do this
class User < ActiveRecord::Base
with_options :if => :is_admin? do |admin|
admin.validates :password, :length => { :minimum => 10 } #sample validations
admin.validates :email, :presence => true #sample validations
end
end
5.4 Grouping conditional validations

Rails how to get associated model attributes

I have the method below which saves data to the users table as well as the user_details table.
When i pass the #newUser variable to the EmailMailer, i can't access the user_details attributes. How can i pass the user_details in the #newUser object without having to re-query the database?
Models
class User < ActiveRecord::Base
has_one :user_details, :dependent => :destroy
accepts_nested_attributes_for :user_details
attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :login, :home_phone, :cell_phone, :work_phone, :birthday, :home_address, :work_address, :position, :company, :user_details_attributes
end
class UserDetails < ActiveRecord::Base
belongs_to :user
attr_accessible :first_name, :last_name, :home_phone, :cell_phone, :work_phone, :birthday, :home_address, :work_address, :position, :company
end
Controller
# POST /users
def create
#newUser = User.new(params[:user], :include =>:user_details)
# create password
require 'securerandom'
password = SecureRandom.urlsafe_base64(8)
#newUser.password = password
respond_to do |format|
if #newUser.save
#newUser.build_user_details
# Tell the UserMailer to send a welcome Email after save
EmailMailer.welcome_email(#newUser).deliver
# To be used in dev only. Just tests if the email was queued for sending.
#assert ActionMailer::Base.deliveries.empty?
format.html {
flash[:success] = "User created successfully"
redirect_to(contacts_path)
}
else
format.html {
flash[:error] = flash[:error].to_a.concat resource.errors.full_messages
redirect_to(contacts_path)
}
end
end
end
Something like this might do what you are after.
class User < ActiveRecord::Base
has_one :user_details
accepts_nested_attributes_for :user_details
after_initialize :build_user_details
...
end
# In controller
def create
#new_user = User.new
#new_user.attributes = params[:user]
if #new_user.save
# do mail thing
else
# other thing
end
end
You need to build the UserDetails association prior to saving #newUser
#newUser.build_user_details
if #newUser.save
#send mailer
else
#do something else
end
Alternatively you could use the create action after the #newuser is saved
if #newUser.save
#newUser.create_user_details
#send mailer
else
#do something else
end
By the way, Ruby/Rails convention is to use snake_case for variables. so #newUser should be #new_user.

Rails & Rolify: Add default role on creation?

Currently I'm using Rolify & CanCan to manage roles and abilities in my Rails 3 app. My question is: How can I get a user to have a role by default on creation? for example, if I have a "user" role, ¿How can I make all the users that register in my app have a user Role by default? My Ability.rb has this code:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.has_role? :admin
can :manage, :all
elsif user.has_role? :user
can :update, User, :id => user.id
end
end
end
My User Model has this one:
class User < ActiveRecord::Base
rolify
authenticates_with_sorcery!
attr_accessible :username, :email, :password, :password_confirmation
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :username
validates_uniqueness_of :username
validates_presence_of :email
validates_uniqueness_of :email
end
The Role Model This One:
class Role < ActiveRecord::Base
has_and_belongs_to_many :users, :join_table => :users_roles
belongs_to :resource, :polymorphic => true
end
And From the UsersController we have:
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to users_path, :notice => "Tu usuario se ha guardado"
else
render "new"
end
end
Finally the Rolify Migration is this one:
class RolifyCreateRoles < ActiveRecord::Migration
def change
create_table(:roles) do |t|
t.string :name
t.references :resource, :polymorphic => true
t.timestamps
end
create_table(:users_roles, :id => false) do |t|
t.references :user
t.references :role
end
add_index(:roles, :name)
add_index(:roles, [ :name, :resource_type, :resource_id ])
add_index(:users_roles, [ :user_id, :role_id ])
end
end
Now, I can assign roles manually from the rails console by using:
1 User.all
2 User.find(id)
3 User.add_role(:role)
But how can I assign automatically a default role when every user it's created?
Thanks!
You can use an active record callback to assign the role after the user is created. Something like
class User < ActiveRecord::Base
after_create :assign_default_role
def assign_default_role
add_role(:role)
end
end
Note that there's also an after_save callback but it's called EVERY time the user is saved. So if you edit the user and save it would try to add the role again. That's why I'm using the after_create callback instead.
You'd better check if a role is assigned before add_role. so I prefer:
class User < ActiveRecord::Base
after_create :assign_default_role
def assign_default_role
add_role(:normal) if self.roles.blank?
end
end
Forget it, Just had to add:
#user.add_role(:user)
in my create action right after the #user = User.new(params[:user]) line.
Figured it out by myself... I'm still learning :)
Thanks!
after_create :default_role
private
def default_role
self.roles << Role.find_by_name("user")
self.save
end

Resources