Rails 4 - Strong Params - white labelling foreign key - ruby-on-rails

I have a rails 4 app.
When creating permitted params in a controller where there is a belongs_to association with another model, do you need to include the foreign key in the permitted params, so that it can be updated when the record is saved, or is that automatic?

It's NOT automatic.
In Rails 4, you have to permit the attribute in order to be able to mass assign its value. The foreign key of the other model is an attribute of your current model that you're trying to update. Without permitting that, you can't update it's value.

The foreign key is not automatic, the associated object is:
This means the following is true:
#app/controllers/your_controller.rb
class YourController < ApplicationController
def create
#item = Item.new new_params
#associated = Associated.find x
#item.associated = #associated #-> this always works & will save
#item.save
end
private
def new_params
params.require(:item).permit(:name, :etc) #-> foreign_key would have to be explicitly defined here if associated_id was passed from a form
end
end
This should give you some perspective on what you can do with your objects.
Update
If you want to assign a post to the current user each time, you'd be able to use the following:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def create
#post = Post.new post_params
#post.user = current_user # -> however you identify the user
#post.save
end
end

Related

Overwrite permited_params if rails controller has been inherited

Here an example:
class Base<ApplicationController
private
def permited_params
params.require(:object_name).permit(:name, :description)
end
end
class Post<Base
private
def permited_params
params.require(:post).permit(:name, :description, :owner)
end
end
I'm getting an error ActiveModel::ForbiddenAttributesError when call action create. How I can overwrite this permited_params
Params, in general, have a good reason to exist and make sure that not everything can be saved into your database. However, if you want to permit all params you can call
params.require(:post).permit!
In case you just want to change the params you can change the attribute names.
params.require(:post).permit(:name, :description, :some_you_want, some_more ) etc.
In general, you should add all params you want to save into the list of permitted params. So you make sure that all the attributes you want to save will be stored and no more. You can have permitted_params in every controller. You do not need to call it permitted params. For instance you can call it like this in your posts_controller:
def create
#post = Post.new(post_params)
#.... your code
end
private
def post_params
params.require(:post).permit(:name, :description, :owner)
end
This also works for inherited controllers.
Instead of params.require(:post).permit(...
you can use whatever params you want, like params.require(:reply).permit(...
The required param will throw an error if it is not available. So you need to make sure it exists for example by
#post = Post.new
Other params are optional and will not cause an error by default.

How to do strong parameters with an array of objects

If I have a user
def user_params
params.require(:user).permit(:name, :age)
end
I got that down. I want to batch create users. So a user can fill out a list (theoretically endless) of users, they would come in as:
[{name: "name", age: 12},{name: "name", age: 22},{name: "name", age: 32}]
Question is, how do I use strong parameters for that? I know that I can just loop through the array and create the records, I get that. My understanding is that strong params are a generally good idea, safety wise.
What are strong params protecting me from? What would I be opening myself up to here, if I just looped over the array of users? How can I do it properly, either with strong params, or an alternate method?
The entire point of strong parameters (introduce in rails 4) with the goal of protecting applications from mass assignment vulnerabilities. Like for example, lets say you had a User model and it had a admin attribute. If you were using mass assignment in theory someone could slip in a value for the admin attribute if you did not filter it out some how; see below
class UserController < ApplicationController
def create
#{name: 'Joe', score: 7, title: 'Mr', admin: true} params hash
User.create(params)
end
end
Now if some how a user of your app passed in these values they just made themselves and admin and can do as they please. So thats why you would use strong params to do this.
class UserController < ApplicationController
def create
User.create(user_params)
end
def user_params
params.require(:name).permit(:title, :score) #noticed admin is not allowed
end
end
Now to create multiple records with strong params you could do this
class UserController < ApplicationController
def create
user_params[:users].each do |u|
User.create(u)
end
end
def user_params
params.permit(:users, array: [:name, :age])
end
end

Passing params[:id] to create method in Rails?

I'm trying to write a create method that collects the ID of the profile the user is currently viewing, along with some other information that is irrelevant to this question. However, because the create method POSTs rather than GETs (as I understand it), the value of params[:id] doesn't exist so it's always null. My code is as follows:
class PostsController < ApplicationController
def new
#Post = Post.new
end
def create
#Post = Post.new(post_params)
#Post.user_id = current_user.id
#Post.target_id = params[:id] #this
if #Post.save
redirect_to :back, notice: "You added a post!"
end
end
private
def post_params
params.require(:post).permit(:body)
end
end
Is there a way to get the value of params[:id] from elsewhere, perhaps from my Users controller in the show method where it actually exists?
Keep in mind that I was successfully able to create a hidden field in the Posts form, but I didn't like the fact that users were able to edit the value using Developer Tools, allowing them to change what profile the post would go to.
If there is a direct relation between the Target and the Post model, you should express this in the controller and model structure: link
This expresses your intention and it provides all the rails automations like routing, url helpers, form helpers, a.s.o.
In your concrete example, my guess is the Target would have many Posts:
class Target < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :target
end
Which would lead to the following route structure:
resources :targets do
resources :posts
end
To create a new post for the current target you would post to:
targets/:target_id/posts
And the target id would be accessed via params[:target_id]

Pass data from controller to model

I have to entities, project and words so far every time when I need to check if project 1 had some words, I was passing the project_id to words model.
class Words
def word_exist? project_id
project = Project.find(project_id)
words = project.words.exists?(self.id)
end
end
Because in my controller I already set the project, do I need to do it in my model as well? Or I can pass the instance variable of the project to model.
You can pass project itself as parameter:
def word_exist?(project)
keywords = project.keywords.exists?(id)
end
and in controller:
#project = Project.find(params[:id]) # example
words = Words.new
exists = words.word_exist?(#project)
You'll be best using an instance method to access the required data:
#app/models/project.rb
class Project < ActiveRecord::Base
has_many :keywords
def word_exist?
self.keywords.exists?(self.id)
end
end
This will be called like so:
#project = Project.find params[:id]
#project.word_exist?
Because it's an instance method on your model, you'll already have the object's data, which the instance method can then reference with self
If you wanted to find a specific word, you'll be able to use arguments on your instance method:
#app/models/project.rb
class Project < ActiveRecord::Base
has_many :keywords
def word_exist?(word)
self.keywords.exists?(keyword: word)
end
end
#project = Project.find params[:id]
#project.word_exist?("hello")

Creating Rails ActiveRecord model from params hash

Most Rails tutorials show how to populate a model class via the params hash like so:
class UsersController < ApplicationController
def create
#user = User.create(params[:user])
# more logic for saving user / redirecting / etc.
end
end
This works great if all the attributes in your model are supposed to be strings. However, what happens if some of the attributes are supposed to be ints or dates or some other type?
For instance, let's say the User class looks like this
class User < ActiveRecord::Base
attr_accessible :email, :employment_start_date, :gross_monthly_income
end
The :email attribute should be a string, the :employment_start_date attribute should be a date, and the :gross_monthly_income should be a decimal. In order for these attributes to be of the correct type, do I need to change my controller action to look something like this instead?
class UsersController < ApplicationController
def create
#user = User.new
#user.email = params[:user][:email]
#user.employment_start_date = params[:user][:employment_start_date].convert_to_date
#user.gross_monthly_income = params[:user][:gross_monthly_income].convert_to_decimal
# more logic for saving user / redirecting / etc.
end
end
According to the ActiveRecord documentation, the attributes should automatically be typecasted based on the column types in the database.
I would actually add a before_save callback in your users model to make sure that the values you want are in the correct format i.e.:
class User < ActiveRecord::Base
before_save :convert_values
#...
def convert_values
gross_monthly_income = convert_to_decimal(gross_monthly_income)
#and more conversions
end
end
So you can just call User.new(params[:user]) in your controller, which follows the motto "Keep your controllers skinny"

Resources