Rails - How does ActiveRecord's save method work? - ruby-on-rails

A newbie question regarding the ActiveRecord's save method.
If i have this code (as in rails guide):
def create
#post = Post.new(params[:post])
if #post.save
redirect_to #post
else
.....
end
end
The save method returns the new created Post object? How, after the code #post.save, rails know how to substitute the redirect_to #post with the proper post_id (1 or 2 or 3 or ....) to build the link?

It's an OOP concept.
So, after create, the #post is updated with the id, and everything just works.
Take a look at this code.
Hope it helps.

I'm going to explain it by presenting another piece of code that should make things easy to understand
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user
Now :user contains the following information based on the User model: id, name, email, telephone.
So that means we have actually assigned to #user the above hashes.
The redirect_to will retrieve the hashes that is already stored in #user and display the new profile page.
I hope it helped.

Related

Rails: Strong parameters, Does it have to be present all the time?

As the title says. I know that strong parameters is to prevent other unauthorized attributes to be included when updating or creating new objects. I've seen codes that doesn't have strong parameters. For example in Hartl's tutorial relationships controller:
class RelationshipsController < ApplicationController
before_action :logged_in_user
def create
#user = User.find(params[:followed_id])
current_user.follow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
end
and others have it included such as creating new post or user etc. so my question is, when is the practice to use strong parameters?
Its ideal to use strong parameters when mass assigning values. Like creating new post or user. It may allows attackers to set any database column’s value.
Check out Rails official guide for it.
Doing like these is fine, as long as you know and mentioning model attributes.
#user = User.find(params[:followed_id])
#user = Relationship.find(params[:id]).followed
I would say - use strong params for any actions where you use mass-assignment. This means that, actions like create or update must employ strong params.
For example, instead of having:
#object.update_attributes(params[:object])
Just have a:
#object.update_attributes(object_params)
Which will whitelist params for you. Also, it allows you to pass-through different params for different actions, with methods like object_update_params and object_create_params which will whitelist params for update and params for create, respectively.
Yes, not using strong parameters will raise ActiveModel::ForbiddenAttributesError so it's not optional unless you manage to override this behavior.
In the above example he is just retrieving a record and then creating a relationship with that id in the model.
def follow!(other_user)
relationships.create!(followed_id: other_user.id)
end

Where does params[:id] come from in rails?

I am a beginner of Rails. I am learning rails with the book 'Beginning Rails 4' now. I want to ask you about 'parameter' passed to params method. The following is one of typical rails controllers.
class CommentsController < ApplicationController
before_action :load_article
def create
#comment = #article.comments.new(comment_params)
if #comment.save
redirect_to #article, notice: 'Thanks for your comment'
else
redirect_to #article, alert: 'Unable to add comment'
end
end
def destroy
#comment = #article.comments.find(params[:id])
#comment.destroy
redirect_to #article, notice: 'Comment Deleted'
end
private
def load_article
#article = Article.find(params[:article_id])
end
def comment_params
params.require(:comment).permit(:name, :email, :body)
end
end
Yes, this is just a typical comment controller used to create a comment attached to an article. The Comment model 'belongs to' the Article model, and the Article model 'has many' comments.
Take look at the destroy method.
def destroy
#comment = #article.comments.find(params[:id])
-- snip --
end
It finds the comment associated with the article by find(params[:id]). My question is, where on earth does params[:id] come from?
Does it come from URL? Or does rails save params hash automatically whenever any comment record is created? So we can find any comment by find(params[:id])?
The load_article method is similar.
def load_article
#article = Article.find(params[:article_id])
end
It finds an article by params[:article_id]. Where does this params[:article_id] come from? How does rails find an article by this?
params[:id] is meant to be the string that uniquely identifies a (RESTful) resource within your Rails application. It is found in the URL after the resource's name.
For example, for a resource called my_model, a GET request should correspond to a URL like myserver.com/my_model/12345, where 12345 is the params[:id] that identifies that specific instance of my_model. Analogies follow for the other HTTP requests (PUT, DELETE etc) and their RESTful counterparts.
You should read about Rails routing and its interpretation of RESTful architecture if you're still confused about these concepts and terminologies.
params[:id] does come from the URL. When you use resources in your routes file, Rails will automatically generate the standard REST routes for you. In your destroy example, that would usually be a be a request to /comments/:id using the DELETE HTTP method, in which that :id is added to the params hash, i.e. params[:id].

Why does passing this object through redirect_to call the `show` action?

def create
#article = Article.new(params[:article])
#article.save
redirect_to #article
end
def show
#article = Article.find(params[:id])
end
I'm following the Getting Started with Rails guide. I have a form that, upon clicking the submit button, creates an #article object (a RESTful object) with the form input.
redirect_to #article sends a GET request to /articles/id, right? If so, why does this happen? Is this simply how redirect_to works?
redirect_to calls url_for if the argument passed is a Record.
url_for calls to_param on record which by default returns id.
Hence redirect_to #article will generate /articles/:id
Objects
To add to Nishu's answer, when you reference an object in any path_helper (link_to, redirect_to etc), Rails will do the hard work and show you the route for the object
Remember, Ruby / Rails is an object-orientated framework, meaning everything you do has to revolve around an object. In the case of Rails, objects are populated with data from your models, which is done using ActiveRecord
--
So if you use redirect_to #article, Rails will basically look for the show action for that particular record. As your routes should be resourceful - meaning Rails can basically look for the relevant route to display that record on its own
Hope this helps?

Ruby on Rails - Forbidden Attributes on params?

Im trying to redirect someone after they click a button and I keep getting the following error:
ActiveModel::ForbiddenAttributesError
Extracted source (around line #20):
It is throwing the error on the #post = Post.new line.
def create
#post = Post.new(params[:post])
if #post.save
redirect_to posts_path, :notice => "Your post was saved"
I am very new to Ruby and at the moment I am very confused of what this means. I am just following a tutorial and mine isnt working. If anyone could help that would be awesome :D
#post = Post.new(params[:post])
... is no longer used in the latest versions of rails. The problem is that it provided weak security. Someone who was updating their user profile (for example) could theoretically insert an attribute like "administrator: true" to change themselves into an administrator (if that's how admin flag is stored)
Strong parameters now require that you explicity specify which attributes you want to allow to be entered.
So nowadays we do...
#post = Post.new(post_params)
And we have a method later in the controller that specifies the permitted attributes, and looks like...
def post_params
params.require(:post).permit(:title, :body)
end
While I don't have quite enough of your code to specifically answer the question, I can probably get pretty close (minus some column/attribute naming). With strong_params now the standard for Rails applications, you'd probably be looking to do something more like:
def create
#post = Post.new(post_params)
if #post.save
redirect_to posts_path, :notice => "Your post was saved"
else
#other stuff here
end
end
private
def post_params
params.require(:post).permit(:content, ....etc) #I took a guess at the attributes you are passing through your params on the create.
end
For a little extra easy-reading on the history/reason: http://blog.8thlight.com/will-warner/2014/04/05/strong-parameters-in-rails.html
Let me know if you'd like any additional clarification.

controller action contains more than one model method call?

I have a simple update action in a Rails 4 controller:
#more stuff here
def update
if #user.update(user_params)
flash[:notice] = "User #{#user.username} updated"
redirect_to users_path
else
render 'edit'
end
end
private
def set_user
#user = User.find(params[:id])
end
However, RubyMine is warning about #user.update and #user.username:
This inspection warns if a controller action contains more than one model method call, after the initial .find or .new. It’s recommended that you implement all business logic inside the model class, and use a single method to access it.
I don't see more than one model method call here. Can some one explain what is going on?
EDIT - I have something similar in the create action without warns, so I believe there is something to do with user_params...
def create
if #user.save
flash[:notice] = "User #{#user.username} created"
redirect_to users_path
else
render 'new'
end
end
Assuming username is a method in model where you merge user first_name and last_name.
I guess #user.update(user_params) and #user.username are your both method calls. One that saves the model, another that sets the user full name in flash notice.
It's just a warning from rubymine that just recommends you some actions to do, not necessary to follow them.

Resources