Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Ruby

Jon Moss
Jon Moss
1,150 Points

URL Parameters in Rails

Hi, I am trying to pass a string into a Rails form through the URL

EX: Entry model has entry_type:string, so --> domain.com/entries/new?entry_type=test

How do I link this up to my controller and my form?

4 Answers

Brandon Barrette
Brandon Barrette
20,485 Points

So two things need to happen here:

For the new action, you have it right (although I've posted the shortcut below):

def new
    @entry = Entry.new(name: params[:name])
end

For the form, I'm not sure what you have, I assume you are trying to save a new entry.

<%= form_for(@entry) do |f| %>
  <%= f.hidden_field :name, params[:name] %>
  #other stuff that's omitted
<% end %>

Then in your create method in your controller, you do need to permit the name attribute if you are making it part of the entry, otherwise merge it to the entry_params (this is definitely not recommended for security reasons):

def create
    @entry = Entry.new(entry_params.merge(name: params[:name]))

    if @entry.save
      go here
    else
      render action: 'new'
    end
end


private

def entry_params
   params.require(:entry).permit(:name)
end

I think what might be going wrong is the name attribute is not being recognized as being part of entry. So it's not making it through strong parameters. Could you post your entire form?

EDIT Now seeing your post above, notice that "name" is not part of entry. So it's not making it through. I've edited my answer above.

Jon Moss
Jon Moss
1,150 Points

Getting error:

Started POST "/entries" for 127.0.0.1 at 2014-10-05 09:57:30 -0400
Processing by EntriesController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"Bde6nEjdGJFTg6Xt39TIzZRlNanvjsKLKWYDCHMsOSc=", "name"=>{"asdf1"=>""}, "entry"=>{"caption"=>"asdf"}, "commit"=>"Create Entry"}
   (0.1ms)  begin transaction
   (0.0ms)  rollback transaction
Completed 500 Internal Server Error in 2ms

TypeError (can't cast ActionController::Parameters to string):
  app/controllers/entries_controller.rb:29:in `create'

Controller:

def new
    @entry = Entry.new(name: params[:name])
  end

def create
    @entry = Entry.new(name: params[:name])
      if @entry.save
        redirect_to @entry, notice: 'Entry was successfully created.'
      else
        render :new
      end
  end

Form field for this:

<%= hidden_field_tag :name, params[:name] %>
Ethan Lowry
PLUS
Ethan Lowry
Courses Plus Student 7,323 Points

Hi Jon,

At any time in your Rails controller you can access any params passed in with the last request (whether they're embedded in the URL via GET request, or via a POST request such as from a submitted form) using the params hash. So, for your example above you might use something like this to get the value passed in:

type = params["entry_type"]

Of course, you can then do whatever you wish with this value.

Other than that just make sure you have created a correct controller action such as index, show, etc. and that it's been hooked up in your routes file. (If you generated your controllers etc. via rails generate then this should have been done for you.)

Hope that helps and good luck.

Jon Moss
Jon Moss
1,150 Points

So like this:

def new
@entry = current_user.entries.build
@entry.type = params["entry_type"]
end
Jon Moss
Jon Moss
1,150 Points

To sum up... (Bear with me, I changed the name of the attribute)

(controller)
  def new
    @entry = Entry.new
    @entry.name = params["name"]
  end

(form)
  <%= hidden_field_tag :name, params[:name] %>

However, this doesn't save and the attribute ends up blank, but when I view the HTML source for /entries/new?name=test

<input id="name" name="name" type="hidden" value="test" />

Suggestions

J Scott Erickson
J Scott Erickson
11,883 Points

Ethan's answer is correct about accessing params. However you're missing one step in the chain of accessing params in rails. You will want to create a 'whitelist' of params. previous versions on rails used attr_accessor. However Rails is now using 'Strong params', this allows you to filter the input coming into the controller and keep out unwanted attributes that could be harmful or unnecessary.

class MyClassController < ApplicationController
  def new
    ... code here  
  end

  def create
    ... code here
  end

  def show
    @my_instance = MyClass.find(my_class_params[:id]) 
  end
  .
  .

  private
    def my_class_params #this can really be whatever you want to call it
      params.require(:my_class).permit(:attribute, :attribute, :attribute)
    end
end
Jon Moss
Jon Moss
1,150 Points

Would I still need to do that if I'm only using the URL params for the new action?

Ethan Lowry
Ethan Lowry
Courses Plus Student 7,323 Points

Good point, but I believe you only need to do this when the params are being passed in to either update or create an instance of the model - if you're just using the params for your own original use here (using it to help in setting up the new form, rather than in the create action itself) then you probably don't need to.

J Scott Erickson
J Scott Erickson
11,883 Points

If you are just using it in the new action you actually don't need the params at all. (depending.... if you're presetting some of your params in your new record then maybe you will need to give it some attributes prior to displaying the new view.

def new
  @my_instance = MyClass.new
end

Will work just fine, and doesn't require you to grab any params. if however you are redirect during the create method and want to be able to show errors before a save then:

def create
  @my_instance = MyClass.new(my_class_params)
  if @my_instance.save
    render @my_instance # the show path of this model if that how you're directing
  else
    render action: 'new' # renders the new.html.erb view using the @my_instance variable that has the params
    # this allows you to show the errors specific to the object based on the params supplied in the view
  end
end
Jon Moss
Jon Moss
1,150 Points

Yep, the idea was that the type would be preset through the URL

J Scott Erickson
J Scott Erickson
11,883 Points

Is it a sub-class? or just a class that has different types based on user input? Basically, when the model is called in the new view will you know what 'type' it will be, or is that info furnished by the user?

Jon Moss
Jon Moss
1,150 Points

You would know what type it would be via the URL, no form input.

Ex: domain.com/entries/new?entry_type=test

Ethan Lowry
Ethan Lowry
Courses Plus Student 7,323 Points

I think we've gotten off course a bit here - do you actually have a failure or error with what you're trying to do right now? It sounds like you understand what's up.

Jon Moss
Jon Moss
1,150 Points

To sum up... (Bear with me, I changed the name of the attribute)

(controller)
  def new
    @entry = Entry.new
    @entry.name = params["name"]
  end

(form)
  <%= hidden_field_tag :name, params[:name] %>

However, this doesn't save and the attribute ends up blank, but when I view the HTML source for /entries/new?name=test

<input id="name" name="name" type="hidden" value="test" />

Suggestions?

Ethan Lowry
Ethan Lowry
Courses Plus Student 7,323 Points

Jon Moss All right, interesting. Could you check the development server log in your terminal, immediately after submitting that form?

Just scroll up it for a bit and keep an eye out for anything interesting or out of place looking, particularly anything that might mention parameters, if there is anything like that.

Jon Moss
Jon Moss
1,150 Points

Terminal: (keep in mind I also have a caption:string attribute which is not typed in via params)

Started GET "/entries/new?name=asdf1" for 127.0.0.1 at 2014-10-04 20:32:40 -0400
Processing by EntriesController#new as HTML
  Parameters: {"name"=>"asdf1"}
  Rendered entries/_form.html.erb (2.3ms)
  Rendered entries/new.html.erb within layouts/application (10.4ms)
Completed 200 OK in 133ms (Views: 131.4ms | ActiveRecord: 0.0ms)
Started POST "/entries" for 127.0.0.1 at 2014-10-04 20:32:44 -0400
Processing by EntriesController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"Bde6nEjdGJFTg6Xt39TIzZRlNanvjsKLKWYDCHMsOSc=", "name"=>"asdf1", "entry"=>{"caption"=>"asdf"}, "commit"=>"Create Entry"}
   (0.1ms)  begin transaction
  SQL (0.3ms)  INSERT INTO "entries" ("caption", "created_at", "updated_at") VALUES (?, ?, ?)  [["caption", "asdf"], ["created_at", "2014-10-05 00:32:44.072674"], ["updated_at", "2014-10-05 00:32:44.072674"]]
   (89.8ms)  commit transaction
Redirected to http://localhost:3000/entries/4
Completed 302 Found in 94ms (ActiveRecord: 90.2ms)

Both Ethan and J have good answers. I believe you only need to use strong parameters when you are going to be making an HTTP request for POST, PUT/PATCH, or DELETE. This is because you are actually trying to manipulate or change information in a database instead of just GETting data back from it. So if you were building a search field or just getting a list of Entry.where(entry_type: "something").all then you're fine without using strong params. I see above you are creating a new action in your controller. If you'll be using Rails' create or update method, then yeah, just simply pass it through your strong params.