Problems using form helpers in ruby on rails

85 views Asked by At

I have a class activity that has the follow atributes:

String type, Date date, String title

By including the associations it also has user_id and place_id.

class Activity < ActiveRecord::Base
   belongs_to :user
   belongs_to :place

In the other side User has many activities and place has many activities

So, the problem is when I want to create a new activity: Scaffold creates the helper _form :

<%= form_for(@activity) do |f| %>
  <% if @activity.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@activity.errors.count, "error") %> prohibited this activity from being saved:</h2>

      <ul>
      <% @activity.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :type %><br>
    <%= f.text_field :type %>
  </div>
  <div class="field">
    <%= f.label :date %><br>
    <%= f.datetime_select :date %>
  </div>
  <div class="field">
    <%= f.label :title %><br>
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label :user_id %><br>
    <%= f.number_field :user_id %>
  </div>
  <div class="field">
    <%= f.label :place_id %><br>
    <%= f.number_field :place_id %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

I want to receive the first 3 fields from the form (type, date and title) but to associate a user and a place I have to do other way. I need the user that is actual logged in and the place is choosen by tiping the name.

My idea to do this is the following:

1) The user issue, I can make a query by using the current_logged_user that I have acess and get his ID.

2) The place issue, I can use the name that I receive from form and query my Places table for the place with the name X and get the ID after.

But, because I don't know too much about rails, how can I do this? How can I use f.text_field and then made the query or whatever and use after in the controller?

Controller has already this stuff :

def create
    @activity = Activity.new(activity_params)
(...)

private
    # Use callbacks to share common setup or constraints between actions.
    def set_activity
      @activity = Activity.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def activity_params
      params.require(:activity).permit(:type, :date, :title, :user_id, :place_id)
    end
1

There are 1 answers

5
AOG On BEST ANSWER

You can structure your rails app to get neither the user_id nor the place_id directly from the form. Especially getting user_id from a submitted form is generally not a good idea. You usually do not want to whitelist user_id at all.

For user_id:

If you are using a gem like devise for user authentication, it gives you access to a method called current_user, which you can use to set the user_id from.

For place_id:

I suggest putting the activity as a sub route of place. e.g. instead of having the form under <host>/activities/new, put it under ``/places/:place_id/activities/new`. In your route file put the route as follows:

resources :places do
    resources :activities
end

Now, in your controller action you can do the following:

def create
    @activity = current_user.activities.new(activity_params)
    @activity.place_id = params[:place_id] (or even safer will be @activity.place = Place.find(params[:place_id], but this will require one more sql query ) 
    (...)
private      

# Never trust parameters from the scary internet, only allow the white list through.
def activity_params
  params.require(:activity).permit(:type, :date, :title)
end

UPDATE:

If you absolutely want to have the form under /activities/new route then you can have a select tag for place_id in your form:

select_tag 'activity[place_id]', options_from_collection_for_select(Place.all, 'id', 'name')

This will create a selection with name 'activity[place_id]' (named this way for params.require(:activity).permit(place_id) ) and options looking like

<option value="1">Barcelona</option>