Ruby on Rails: Submitting to table two different ways - ruby-on-rails

I have an app where the user can submit a project. For each field they have a choice of either putting in new data into the database, or selecting old data from past projects to fill that field.
I am having trouble getting this to work for this piece of code in my New view:
<%= form_for(#technol) do |tech| %>
<%= fields_for(#project_technol) do |ab| %>
<%= text_field_tag :tech, nil, :maxlength => 30 %>
OR<br />
<%= ab.label "All Tech"%> </br>
<%= collection_select( :technols, :id, Technol.all, :id, :tech, {}, {:multiple => true } ) %>
</div>
<% end %>
<% end %>
At the moment the user can select many technologies from the collection_select, and they get saved with the project, but I am trying to give them the option to put there own technologies in through a text box.
My controller actions:
NEW
def new
#project = Project.new
#technol = Technol.new(params[:tech])
#all_technols = Technol.all
tech_ids = params[:technols][:id].reject(&:blank?) unless params[:technols].nil?
#project_technol = #project.projecttechnols.build
respond_to do |format|
format.html # new.html.erb
format.json { render json: #project }
end
end
CREATE
def create
#project = Project.new(params[:project])
params[:technols][:id].each do |tech|
if !tech.empty?
#project.projecttechnols.build(:technol_id => tech)
end
end
respond_to do |format|
if #project.save
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.json { render json: #project, status: :created, location: #project }
else
format.html { render action: "new" }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
So to sum up, I want the user to have the option to enter new tech into the database, AND select existing tech from the dropdown, then all get saved with the project, and the new tech entered get saved in the technol table.
Any ideas? I am a rails noob so please remember this when trying to answer. Any help will be much appreciated. Thanks in advance

How about using token fields instead of a drop down and text field?
Check this revised RailsCast:
http://railscasts.com/episodes/258-token-fields-revised
Here's the original screen cast that's free if you don't have a RailsCast subscription:
http://railscasts.com/episodes/258-token-fields

I will suggest you to use a autocomplete which accepts multiple values. which will improve your view plus the coding will be easy

Related

Form hidden fields and security

I m using hidden field in my app to add user_id to my database "Camping". I have associations "User" has many campings and "Camping" belongs_to "user".
When I run firebug or something like this, I can modify user_id value of this field. If any user puts his ID, I can modify object to other user... I want to avoid this !
My code
<%= f.hidden_field :user_id, :value => current_user.id %>
This code, is necessary because I allow only user to edit / updated / create object if they have user_id == current_user.id.
How to fix this security problem ?
By the way, I m using devise.
Edit with full code
My _form.html.erb
<%= form_for(camping) do |f| %>
<% if camping.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(camping.errors.count, "error") %> prohibited this camping from being saved:</h2>
<ul>
<% camping.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<div class="form-group">
<label for="name">Nom du camping</label>
<%= f.text_field :name, autofocus: true, class:"form-control", id:"name", :required => true%>
</div>
<div class="actions">
<%= f.submit "Enregistrer", class:"btn btn-success" %>
</div>
<% end %>
my controller
def new
#camping = Camping.new
#campings = Camping.all
end
def edit
end
def create
#camping = Camping.new(camping_params)
respond_to do |format|
if #camping.save
format.html { redirect_to #camping, notice: 'Camping was successfully created.' }
format.json { render :show, status: :created, location: #camping }
else
format.html { render :new }
format.json { render json: #camping.errors, status: :unprocessable_entity }
end
end
end
def update
#camping = Camping.find(params[:id])
respond_to do |format|
if #camping.update(camping_params)
format.html { redirect_to #camping, notice: 'Camping was successfully updated.' }
format.json { render :show, status: :ok, location: #camping }
else
format.html { render :edit }
format.json { render json: #camping.errors, status: :unprocessable_entity }
end
end
end
my edit.html.erb
<div class="containershow">
<h1>Editing Camping</h1>
<%= render 'form', camping: #camping %>
<%= link_to 'Show', #camping %> |
<%= link_to 'Back', campings_path %>
</div>
my new.html.erb
<h1>New Camping</h1>
<%= render 'form', camping: #camping %>
<%= link_to 'Back', campings_path %>
Edit solution ?
User can create and update his camping. I delete hidden_field
def create
# #camping = Camping.new(camping_params)
#camping = Camping.new((camping_params).merge(:user_id => current_user.id))
respond_to do |format|
if #camping.save
format.html { redirect_to #camping, notice: 'Camping was successfully created.' }
format.json { render :show, status: :created, location: #camping }
else
format.html { render :new }
format.json { render json: #camping.errors, status: :unprocessable_entity }
end
end
end
In Devise, the current user object is in current_user, available to your controllers. When saving the model, make sure to fill the user id field from that object, and not user input in the update action of your controller. Note that the edit action does not matter, that just renders the edit page, the actual update happens in update (if you follow the default conventions). Of course if you don't want users to even see other users' objects, you also need access control in other controller actions like edit as well, but that (implementing access control in a multi-tenant Rails application) is a different and much broader question.
More generally, be aware that anything that comes from a request can very easily be forged by a user. Always implement security server-side and do not trust user input!
Edit (seeing your code)
To prevent users updating others' Campings, you need to check in update after getting the #camping object (the second line) whether that's a camping object that your logged on user (current_user.id) is supposed to be able to edit.
The same way, if you want to prevent users from creating Campings for other users, you need to make sure in create that user_id will be set to the current user, something like #camping.user_id=current_user.id.
Similarly, if you want to prevent having a look at each other's Campings, you need to add checks to edit, show and pretty much all actions that return such objects.
There are gems like cancan and cancancan that may help with access control in Rails, they are worth a look!
Your Question is quite interesting but simple In the any HTML View Any one can change anything this will cause a security wise vulnerability as well.
To avoid these issues we need to authenticate it by two way You have to check the code by like It should be use by Controller not by view.
Suppose If you are creating any article of particular user
So To avoid it what you can do You can set the User ID in Session and make a Helper Method to find Current User always
So that you can find current user directly from controller and create article according to user
def Create
#article = current_user.articles.create(article_params)
end
This kind of Two way checking you can put up so that It will be safe.
To avoid the spend time on these work you can use gem directly like Devise

Dropdown Menu Rails

I currently have categories seeded to my database within a 'category' model, what I'm trying to do is have a dropdown on the new post page which allows me to select what category it belongs to.
The problem is I'm currently getting below:
This is the field I'm currently using within the form.
<div class="field">
<%= f.select :category, Category.all, :prompt => "Select One" %>
</div>
Any help would be fantastic.
Thanks
UPDATE
Offers Controller Create
def create
#offer = Offer.new(offer_params)
respond_to do |format|
if #offer.save
format.html { redirect_to #offer, notice: 'Offer was successfully created.' }
format.json { render :show, status: :created, location: #offer }
else
format.html { render :new }
format.json { render json: #offer.errors, status: :unprocessable_entity }
end
end
end
Try using this:
<%= f.select :category_id, Category.all.collect{|c| [c.name, c.id]}, :prompt => "Select One" %>
More info can be found here.
Don't forget to add :category_id to permitted parameters list for Post in your controller.
Instead of doing Category.all - which gives you back an ActiveRecord Object you have to specify the attribute on the Object you want.
There is a built in form helper for that:
<%= f.collection_select(:category_id, Category.all, :id, :name) %>
Rails guides for form helpers might help: http://guides.rubyonrails.org/form_helpers.html

creating recurring events with ice_cube gem

I am trying to create recurring events using ice_cube and recurring_select gems.
Here is my _form.html.erb code:
<%= simple_form_for(#event) do |f| %>
<div class="form-inputs">
<%= f.select_recurring :day, [IceCube::Rule.daily] %>
<%= f.input :start_time %>
<%= f.input :end_time %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
In my controller I have (among other things):
def new
#event = Event.new
end
def create
#event = Event.new(event_params)
respond_to do |format|
if #event.save
format.html { redirect_to #event, notice: 'Event was successfully created.' }
format.json { render :show, status: :created, location: #event }
else
format.html { render :new }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
end
def event_params
params.require(:event).permit(:day, :start_time, :end_time, :reserved)
end
As you can see I want to create the same event for each day in a week, but actually my :day column remains empty if I submit this form.
Can you give some feedback? I don't know what can be wrong
Your escape_params seems to be wrong, it should be event_params as you have used in the update action:
private
def event_params
params.require(:event).permit(:day, :start_time, :end_time, :reserved)
end
Update:
After looking into recurring_select gem, the data that it is sending to the server is something like this:
event[:day]: {"interval":1,"until":null,"count":null,"validations":null,"rule_type":"IceCube::DailyRule"}
So it is not a simple single value parameter that you can store in a single field.
You have two choices here, either serialize this value and store it in a single field or create separate fields for each parameter in the database.
And since your data in day field is a hash, permit function simply won't work on it. You can see more information on Rails issue tracker.

Retrieving form params in Rails 4 controller using fields_for and accepts_nested_attributes_for

I am positive this is the dumbest question but I cannot wrap my head around it.
I have two models in a simple has_one/belongs_to relationship
registration_code.rb
class RegistrationCode < ActiveRecord::Base
has_one :billing_transaction
accepts_nested_attributes_for :billing_transaction
billing_transaction.rb
class BillingTransaction < ActiveRecord::Base
belongs_to :registration_code
In my form I am collecting information for BOTH models using fields_for.
_form.html.erb (truncated example)
<%= form_for #registration_code, :html => {:class => "form", role: "form"} do |f| %>
<%= f.label :registration_code, "Registration Code", :class => "control-label" %>
<%= f.text_field :registration_code %>
<%= f.fields_for #billing_transaction do |bt| %>
<%= bt.label :transaction_amount, "Transaction Amount", :class => "control-label" %>
<%= bt.number_field :transaction_amount %>
<% end %>
<% end %>
In my controller, I have the following.
registration_code_controller.rb
def new
#registration_code = RegistrationCode.new
#billing_transaction = BillingTransaction.new
#billing_transaction.registration_code = #registration_code
end
def create
#registration_code = RegistrationCode.new(registration_code_params)
#billing_transaction = BillingTransaction.new # DOES THIS HAVE TO TAKE PARAMS?
#billing_transaction.registration_code = #registration_code # DO I NEED THIS LINE?
##### THE TROUBLE IS ON THIS NEXT LINE #####
#billing_transaction.transaction_amount = params[:billing_transaction_attributes][:transaction_amount] # THIS NEVER GETS SET! NOT SURE HOW TO ACCESS THE PARAMS
respond_to do |format|
if #registration_code.save && #billing_transaction.save
format.html { redirect_to registration_codes_path, notice: 'Registration code was successfully created.' }
else
format.html { render :new }
format.json { render json: #customer.errors, status: :unprocessable_entity }
end
end
end
private
# Never trust parameters from the scary internet, only allow the white list through.
def registration_code_params
params.require(:registration_code).permit(:registration_code, :expires_at, billing_transaction_attributes: [:transaction_amount])
end
The params are submitted and I can access the params for the primary model (registration_code) just fine. But I cannot for the life of me figure out how to get the parameters of the "sub" model (billing_transaction) and use them in the controller.
{"utf8"=>"✓",
"authenticity_token"=>"ePpqwJkeTAGMzb5WRWeI6aYCx4xpqvq4rl2m405IbwLdbp9xE0RyPgTZ6NmX8SvCFu94GKrfMfV9PrOkKa1BLg==",
"registration_code"=>{"registration_code"=>"HFmkbQEN",
"expires_at"=>"2015-07-16",
"billing_transaction"=>{"transaction_amount"=>"958.40" }},
"commit"=>"Create Registration Code"}
To access the billing_transaction.transaction_amount, for example, I have tried many variations:
params[:billing_transaction_attributes][:transaction_amount]
params[:billing_transaction][:transaction_amount]
params[#billing_transaction][:transaction_amount]
params[:registration_code][:billing_transaction][:transaction_amount]
No matter what I enter I cannot seem to access that nested array of parameters.
Help. Feeling super dumb right now. Thanks.
A few important changes to your new and create methods as below would solve your problem.
def new
#registration_code = RegistrationCode.new
#billing_transaction = #registration_code.build_billing_transaction
end
def create
#registration_code = RegistrationCode.new(registration_code_params)
respond_to do |format|
if #registration_code.save
format.html { redirect_to registration_codes_path, notice: 'Registration code was successfully created.' }
else
format.html { render :new }
format.json { render json: #customer.errors, status: :unprocessable_entity }
end
end
end
This is how the nested_attributes gets saved in the DB.
To retrieve the hash of billing transactions:
registration_code_params[:billing_transaction_attributes]
To retrieve the first (and only) key/value pair in the hash:
key, value = registration_code_params[:billing_transaction_attributes].first
puts key #transaction_amount
puts value #958.40

get value from form_for

I'm using rails 4.0.1
<%= form_for #event, :html => { :multipart => true} do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :content %><br>
<%= f.text_area :content %>
</div>
<div class="field">
<%= f.label :place_id %><br>
<%= f.collection_select(:place_id, #places, :id, :title) %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And I want to check for current_user.id and Place.user_id (it stores creator id). In Events cotroller i'm trying to use:
def create
#places = Place.all
#event = Event.new(event_params)
#event.user_id = current_user.id
#curplace = Place.find_by(id: params[:place_id])
#event.content = #curplace.id
respond_to do |format|
if #event.save
format.html { redirect_to #event, notice: 'Event was successfully created.' }
format.json { render action: 'show', status: :created, location: #event }
else
format.html { render action: 'new' }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
end
But i got an error. I think i'm not getting this Place_id param right or anything else?
Further to the comment from Ankush Kataria, the form_for helper basically creates a form which combines all the params into a hash, as opposed to form_tag, which just makes the params independently
As you've discovered, this means your params will be accessed by:
#form_for
params[:variable][:param]
#form_tag
params[:param]
form_for
The reason why this is important is because if you're using the RESTful routes interface, you'll be able to create / edit / update a variety of records
form_for basically keeps consistency throughout this process, pre-populating your forms with the various values, and keeping your code DRY
To call a form_for helper, you have to define the #varaible the form will populate. This #variable needs to be an ActiveRecord object, and is why you have to build it in the new action before your form shows
form_tag
form_tag is much more independent of the form_for helper, doesn't require any #variable, and creates the params individually
You'd use a form_tag for the likes of a contact us form or similar
Your Code
Your form looks good, but your create action can be dried up:
def create
#places = Place.all
#event = Event.new(event_params)
respond_to do |format|
if #event.save
format.html { redirect_to #event, notice: 'Event was successfully created.' }
format.json { render action: 'show', status: :created, location: #event }
else
format.html { render action: 'new' }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
end
private
def event_params
params.require(:event).permit(:title, :content).merge(user_id: current_user.id, place_id: params[:event][:place_id])
end
You are right that params[:place_id] isn't returning the value you expect. It only returns nil. To get the :place_id that's submitted by the form, you have to do this:
#curplace = Place.find(params[:event][:place_id])
Just replace the old line with the code above. It's because your form submits the data in the fields inside an :event key in the params hash since you're using the form_for helper method provided by Rails. That is its default behavior unless you change the 'name' attribute's value in the input fields.
Hope that helps!

Resources